§What’s new in Play 2.6
This page highlights the new features of Play 2.6. If you want to learn about the changes you need to make when you migrate to Play 2.6, check out the Play 2.6 Migration Guide.
§Akka HTTP Server Backend
Play now uses the Akka-HTTP server engine as the default backend. More detail about Play’s integration with Akka-HTTP can be found on the Akka HTTP Server page. There is an additional page on configuring Akka HTTP.
The Netty backend is still available, and has been upgraded to use Netty 4.1. You can explicitly configure your project to use Netty on the NettyServer page.
§Request attributes
Requests in Play 2.6 now contain attributes. Attributes allow you to store extra information inside request objects. For example, you can write a filter that sets an attribute in the request and then access the attribute value later from within your actions.
Attributes are stored in a TypedMap
that is attached to each request. TypedMap
s are immutable maps that store type-safe keys and values. Attributes are indexed by a key and the key’s type indicates the type of the attribute.
Java:
// Create a TypedKey to store a User object
class Attrs {
public static final TypedKey<User> USER = TypedKey.<User>create("user");
}
// Get the User object from the request
User user = req.attrs().get(Attrs.USER);
// Put a User object into the request
Request newReq = req.addAttr(Attrs.USER, newUser);
Scala:
// Create a TypedKey to store a User object
object Attrs {
val User: TypedKey[User] = TypedKey[User].apply("user")
}
// Get the User object from the request
val user: User = req.attrs(Attrs.User)
// Put a User object into the request
val newReq = req.addAttr(Attrs.User, newUser)
Attributes are stored in a TypedMap
. You can read more about attributes in the TypedMap
documentation: Javadoc, Scaladoc.
Request tags have now been deprecated and you should migrate to use attributes instead. See the tags section in the migration docs for more information.
§Injectable Twirl Templates
Twirl templates can now be created with a constructor annotation using @this
. The constructor annotation means that Twirl templates can be injected into templates directly and can manage their own dependencies, rather than the controller having to manage dependencies not only for itself, but also for the templates it has to render.
As an example, suppose a template has a dependency on a component TemplateRenderingComponent
, which is not used by the controller.
First, add the @Inject
annotation to Twirl in build.sbt
:
TwirlKeys.constructorAnnotations += "@javax.inject.Inject()"
Then create a file IndexTemplate.scala.html
using the @this
syntax for the constructor. Note that the constructor must be placed before the @()
syntax used for the template’s parameters for the apply
method:
@this(trc: TemplateRenderingComponent)
@()
@{trc.render(item)}
And finally define the controller by injecting the template in the constructor:
public MyController @Inject()(indexTemplate: views.html.IndexTemplate,
cc: ControllerComponents)
extends AbstractController(cc) {
def index = Action { implicit request =>
Ok(indexTemplate())
}
}
or
public class MyController extends Controller {
private final views.html.indexTemplate template;
@Inject
public MyController(views.html.indexTemplate template) {
this.template = template;
}
public Result index() {
return ok(template.render());
}
}
Once the template is defined with its dependencies, then the controller can have the template injected into the controller, but the controller does not see TemplateRenderingComponent
.
§Filters Enhancements
Play now comes with a default set of enabled filters, defined through configuration.
play.filters.csrf.CSRFFilter
play.filters.headers.SecurityHeadersFilter
play.filters.hosts.AllowedHostsFilter
This means that on new projects, CSRF protection (ScalaCsrf / JavaCsrf), SecurityHeaders and AllowedHostsFilter are all defined automatically. This provides a “secure by default” experience for new Play applications, and tightens security on existing Play applications.
In addition, filters can now be configured through application.conf
. To append to the defaults list, use the +=
:
play.filters.enabled+=MyFilter
If you want to specifically disable a filter for testing, you can also do that from configuration:
play.filters.disabled+=MyFilter
Please see the Filters page for more details.
NOTE: If you are migrating from an existing project that does not use CSRF form helpers such as
CSRF.formField
, then you may see “403 Forbidden” on PUT and POST requests, from the CSRF filter. To check this behavior, please add<logger name="play.filters.csrf" value="TRACE"/>
to yourlogback.xml
. Likewise, if you are running a Play application on something other than localhost, you must configure the AllowedHostsFilter to specifically allow the hostname/ip you are connecting from.
§JWT Cookies
Play now uses JSON Web Token (JWT) format for session and flash cookies. This allows for a standardized signed cookie data format, cookie expiration (making replay attacks harder) and more flexibility in signing cookies.
Please see Scala or Java pages for more details.
§Logging Marker API
SLF4J Marker support has been added to play.Logger
and play.api.Logger
.
In the Java API, it is a straight port of the SLF4J Logger API. This is useful, but you may find an SLF4J wrapper like Godaddy Logger for a richer logging experience.
In the Scala API, markers are added through a MarkerContext trait, which is added as an implicit parameter to the logger methods, i.e.
import play.api._
logger.info("some info message")(MarkerContext(someMarker))
This opens the door for implicit markers to be passed for logging in several statements, which makes adding context to logging much easier without resorting to MDC. In particular, see what you can do with the Logstash Logback Encoder:
implicit def requestToMarkerContext[A](request: Request[A]): MarkerContext = {
import net.logstash.logback.marker.LogstashMarker
import net.logstash.logback.marker.Markers._
val requestMarkers: LogstashMarker = append("host", request.host)
.and(append("path", request.path))
MarkerContext(requestMarkers)
}
def index = Action { request =>
logger.debug("index: ")(request)
Ok("testing")
}
Note that markers are also very useful for “tracer bullet” style logging, where you want to log on a specific request without explicitly changing log levels:
package controllers
import javax.inject._
import play.api.mvc._
@Singleton
class TracerBulletController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {
private val logger = org.slf4j.LoggerFactory.getLogger("application")
// in logback.xml
/*
<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
<Name>TRACER_FILTER</Name>
<Marker>TRACER</Marker>
<OnMatch>ACCEPT</OnMatch>
</turboFilter>
*/
private val tracerMarker = org.slf4j.MarkerFactory.getMarker("TRACER")
private def generateMarker(implicit request: RequestHeader): org.slf4j.Marker = {
val marker = org.slf4j.MarkerFactory.getDetachedMarker("dynamic") // base do-nothing marker...
if (request.getQueryString("trace").nonEmpty) {
marker.add(tracerMarker)
}
marker
}
def index = Action { implicit request =>
val marker = generateMarker
if (logger.isTraceEnabled(marker)) {
logger.trace(marker, "Hello world!")
}
Ok("hello world")
}
}
For more information, please see ScalaLogging or JavaLogging.
For more information about using Markers in logging, see TurboFilters and marker based triggering sections in the Logback manual.
§Improved Form Handling I18N support
The MessagesApi
and Lang
classes are used for internationalization in Play, and are required to display error messages in forms.
In the past, putting together a form in Play has required multiple steps, and the creation of a Messages
instance from a request was not discussed in the context of form handling.
In addition, it was inconvenient to have a Messages
instance passed through all template fragments when form handling was required, and Messages
implicit support was provided directly through the controller trait. The I18N API has been refined with the addition of a MessagesProvider
trait, implicits that are tied directly to requests, and the forms documentation has been improved.
For more information, please see ScalaI18N or JavaI18N.
Next: Migration Guides