How is play framework




















Play quickly grew to become one of the most loved and used web frameworks for full-stack applications in Java and Scala, with a large and passionate developer community. It has played a key role in bringing the Reactive Principles to web development and has pushed the envelope for web frameworks when it comes to the overall developer experience.

However, as a majority of our customers and the industry at large have moved to the Cloud, we have steered our focus more and more towards our core competency, our DNA, the reason why we started this company: building the best developer platform and experience for distributed systems, the Cloud, and the emerging Edge ecosystem. That means primarily focusing on Akka Open Source, and our new PaaS-platform Akka Serverless , a product that enables the values of Akka—low-latency and high-throughput real-time data, paired with incredibly high levels of scalability, reliability, and availability—to any developer, packaged up a serverless, simple, and to a large extent declarative developer experience, regardless of the language they want to program in.

All this means that we as a company are still as passionate about OSS as ever, but will not be able to be the stewards and main developers of the Play project anymore. This does not mean that Play is dead and that you should migrate off it.

Play is rock-solid and used by hundreds of thousands of Java and Scala developers every month. We at Lightbend have simply finished writing our chapter in the story and are now trusting the following chapters to the community and power of OSS. If you love Play and care about its future then step in and help out. It is, as I said, both extremely rewarding and fun. Let's start by returning a JSON value in the details action:.

This code tries to retrieve the item in the shop and if found, returns it as a JSON object. The Json. If there is no item with the ID passed as a parameter, the action returns a NotFound response. The Jackson object mapper that performs this task can handle some simple data structures like the Item class, but for more complex data structures for example, involving cycles or bidirectional associations , you might have to supply your own serialization process by annotating your types with Jackson annotations.

The Scala API does not follow this approach because the Scala language gives convenient mechanisms that allow you to tell how to serialize data without relying on reflection and with minimal boilerplate. If you call this action from your HTTP client, you will get a response like the following assuming your shop has an item with the ID 42 :.

Similar to the implementation of the details action, here is how you can implement the list action and return the list of the items in the shop as a JSON array. The Java version of the code is as follows:.

Again, the Json. We use the Json. You might have noticed that the code defining these JSON objects from the items duplicates the code already written in the details action. Instead, you should isolate the logic corresponding to the serialization of an item into a JSON object as a function and reuse it.

Writes[A] typeclass that captures the serialization logic for the type A , so you can just write an implicit value of type Writes[Item] and Play will use it when needed. The implicit value, writesItem , defines the serialization logic for Items. Then, in the list and details actions, we use Json. This toJson method has the following signature:. This means that it can serialize any value of type A if there is an implicit value of type Writes[A] in the implicit scope. Fortunately, Play defines such JSON serializers for common types and can combine them by chaining implicits; this is why Play is able to serialize an Item as well as a List[Item].

Though the code of your writesItem is quite concise, it follows a repetitive pattern. Hopefully, Play provides a macro that generates JSON serializers following this pattern, so the previous serializer can be synthesized by just writing the following:. You might be wondering why automatic generation of JSON serializers is not the default behavior. There are two reasons for this.

First, the automatic generation mechanism cannot handle all data types for example, cyclic data types. Secondly, sometimes you don't want to use the same names for your JSON object fields and your Scala object fields. Now your web service is able to send JSON data representing the application data. However, clients cannot yet create new data by sending JSON requests; the create action is still not implemented.

This action should read the JSON data of requests to extract the information required to create a new item, effectively create the item, and return a response telling the client whether its operation succeeded. The CreateItem data type just glues together the information needed to create a new item: a name and price.

The Java version uses public fields so that it can automatically be handled by the Jackson object mapper. That is, a JSON object with a member "name" that contains a string value and a member "price" that contains a number value.

The next step consists of defining how to convert a JSON object consistent with this structure into a CreateItem value. This typeclass has one abstract method:. The JsResult[A] type represents either a successful conversion, JsSuccess a , or a unsuccessful conversion, JsError errors , which contains a list of errors such as missing fields in the JSON source object. You can then combine them to define Reads[A] values for more complex types. This code combines the Reads[String] and Reads[Double] values using the and combinator.

Finally, these values are passed to the apply method of the CreateItem data type to make a CreateItem instance. As our readsCreateItem type is built by combining two subreaders using and , it tries to apply all of them.

If all succeed, the obtained values are passed to the CreateItem. If one of the subreaders fails, the reader returns a JsError value. The and combinator is not a method of Reads[A]. It is available thanks to an implicit conversion imported by play. This import brings several other combinators, such as or , which succeeds if one of the two subreaders succeeds. Consider, for instance, the following expression that defines a path locating a member "latitude" nested in a "position" member of a JSON object:.

Just like the Writes definition, the readsCreateItem definition is quite mechanical. Just like the Write s definition, there is a macro automating the work for Scala case classes so that the preceding Reads definition is completely equivalent to the following:.

Finally, the last step consists of making the create action interpret request content as JSON data and making a CreateItem value from this data:. There are three important points to note in the preceding code. First, we tell the create action to interpret the request body as JSON data by supplying the parse.

Of BodyParser. In Play, the component responsible for interpreting the body of an HTTP request is named body parser. By default, actions use a tolerant body parser that will be able to parse the request content as JSON, XML, or URL-encoded form or multipart form data, but you can force the use of a specific body parser by supplying it as the first parameter of your action builder or by using the BodyParser.

Of annotation in Java. The advantage is that within the body of your request, you are guaranteed that the request body available as the body field on the request value has the right type. If the request body cannot be parsed by the body parser, Play returns an error response with the status Bad Request.

The previous way to use the Action builder by just passing it a block of type Result was just a convenient shorthand for writing an action ignoring its request parameter. The type parameter, A , in Request[A] represents the type of the request body.

In our case, because we use the parse. In Java, Play sets up a context before calling your action code just after the routing process so that within a controller, you can always refer to the current HTTP request by using the request method.

Thirdly, we make the CreateItem value from this request body by calling request. In Java, the result is simply null in the case of an error. In Scala, there is a body parser that not only parses the request body a JSON blob but also validates it according to a reader definition and returns a Bad Request response in the case of a failure so that the previous Scala code is equivalent to the following shorter version:.

At this point, your clients can consult the items of the shop and create new items. What happens if one tries to create an item with an empty name or a negative price? Your application should not accept such requests. More precisely, it should reject them with the Bad Request error. To achieve this, you have to perform validation on data submitted by clients.

You should implement this validation process in the business layer, but implementing it in the controller layer gives you the advantages of detecting errors earlier and error messages can directly refer to the structure of the submitted JSON data so that they can be more precise for clients. The recommended way is to validate data after it has been transformed into a POJO. In Scala, adding a validation step in our CreateItem reader requires a few modifications.

Indeed, the Reads[A] data type already gives us the opportunity to report errors when the type of coercion process fails. We can also leverage this opportunity to report business validation errors. Incidentally, the Play JSON API provides combinators for common errors such as minimum and maximum values and length verification so that we can forbid negative prices and empty item names, as follows:.

The preceding code rejects JSON objects that have an empty name or negative price. You can try it in the REPL:. The returned object describes two errors: the first error is related to the price field; it has the "error. The second error is related to the name field; it has the "error. The Reads. Consider the following data type representing an item with an optional description:. In Scala, optional values are represented with the Option[A] type.

In JSON, though you can perfectly represent them in a similar way, optional fields are often modeled using null to represent the absence of a value:. Alternatively, the absence of a value can also be represented by simply omitting the field itself:. Aug 26, Jun 16, Original string and character position in message. Sep 7, Sep 30, Fix path in. Oct 1, Jan 11, Remove status:merge-when-green label after merge and mark Steward's…. Oct 28, Feb 12, Fix, refactor and add stuff to scala-steward conf.

Aug 31, Update scalafmt-core to 2. Dec 9, Travis: remove explicit notifications. Sep 27,



0コメント

  • 1000 / 1000