SlideShare a Scribd company logo
1 of 263
Download to read offline
1
Working with Play - Java
Play framework Documentation 2.6.x
Translation from English to Vietnamese
ORG: https://www.playframework.com/documentation/2.6.x/JavaHome
Translater: Dao Van Thien, Cao Sy Trung, Do Van Chung, Le Thi Lanh | Asahina
Infotech Company Limited
Date: 02-Aug-2016
Scope: Working with Play - Java
I. Main concepts for Java .................................................................................2
II. HTTP programming ...................................................................................3
III. Asynchronous HTTP programming .........................................................52
IV. The Twirl template engine ......................................................................56
V. Form submission and validation .............................................................68
VI. Working with Json ...................................................................................91
VII. Working with XML ...................................................................................97
2
VIII. Handling file upload ...............................................................................100
IX. Accessing an SQL database ..................................................................102
X. Using the Cache ....................................................................................130
XI. Calling WebServices ..............................................................................136
XII. Integrating with Akka ...........................................................................160
XIII. Internationalization.................................................................................172
XIV. Dependency Injection ...........................................................................179
XV. Application Settings ...............................................................................204
XVI. Testing your application ........................................................................218
XVII. Logging ..................................................................................................253
I. Main concepts for Java
This section introduces you to the most common aspects of writing a Play application in
Java. You’ll learn about handling HTTP requests, sending HTTP responses, working
with different types of data, using databases and much more.
Đoạn này sẽ giới thiệu cho bạn những khía cạnh chung nhất về việc viết một app Play
trong Java. Bạn sẽ học được về cách xử lí yêu cầu HTTP, gửi phản hồi HTTP, làm việc
với các kiểu dữ liệu khác nhau, sử dụng DB và một số cái khác.
Note: The Play APIs for Java and Scala are separated into different packages. All the
Java APIs are under the play package; all the Scala APIs are under play.api. For
example, the Java MVC API is under play.mvc and the Scala MVC API is under
play.api.mvc.
3
Note: Các play API cho Java và Scala được chia thành các package khác nhau. Tắt cả
các Java API nằm trong play package, tất cả các Scala API nằm tròng play.api. Ví dụ,
Java MVC API nằm trong play.mvc, còn Scala MVC API nằm trong play.api.mvc.
4
II. HTTP PROGRAMMING
Actions, Controllers and Results
-Hoạt động, điều khiển và kết quả
§What is an Action?
-Một hành động là gì?
Most of the requests received by a Play application are handled by an action.
An action is basically a Java method that processes the request parameters, and
produces a result to be sent to the client.
-Hầu hết các yêu cầu nhận được bởi một ứng dụng Play được xử lý bởi một hành
động. Một hành động về cơ bản là một phương pháp Java xử lý các thông số yêu
cầu, và tạo ra một kết quả sẽ được gửi cho khách hàng.
public Result index() {
return ok("Got request " + request() + "!");
}
An action returns a play.mvc.Result value, representing the HTTP response to
send to the web client. In this example ok constructs a 200 OK response containing
a text/plain response body.
- Một hành động trả về một giá trị play.mvc.Result, đại diện cho các phản ứng HTTP
để gửi cho khách hàng web. Trong ví dụ này ok xây dựng một phản ứng OK 200
chứa một text /plain.
§Controllers
5
-Điều khiển
A controller is nothing more than a class extending play.mvc.Controller that
groups several action methods.
- Một bộ điều khiển là không có gì nhiều hơn một lớp mở rộng play.mvc.Controller
nhóm một số phương thức hành động.
package controllers;
import play.*;
import play.mvc.*;
public class Application extends Controller {
public Result index() {
return ok("It works!");
}
}
The simplest syntax for defining an action is a method with no parameters that
returns a Result value, as shown above.
An action method can also have parameters:
-Cú pháp đơn giản để xác định một hành động là một phương pháp không có tham
số trả về một giá trị kết quả, như trình bày ở trên.
Một phương pháp hành động cũng có thể có các thông số:
public Result index(String name) {
return ok("Hello " + name);
}
These parameters will be resolved by the Router and will be filled with values from
the request URL. The parameter values can be extracted from either the URL path
6
or the URL query string.
- Các tham số này sẽ được giải quyết bởi các Router và sẽ được lấp đầy với các giá
trị từ các URL yêu cầu. Các giá trị tham số có thể được chiết xuất từ một trong hai
đường dẫn URL hoặc chuỗi truy vấn URL.
§Results
-Kết quả
Let’s start with simple results: an HTTP result with a status code, a set of HTTP
headers and a body to be sent to the web client.
These results are defined by play.mvc.Result, and the play.mvc.Results class
provides several helpers to produce standard HTTP results, such as the ok method
we used in the previous section:
- Hãy bắt đầu với kết quả đơn giản: một HTTP dẫn với mã trạng thái, một bộ các tiêu
đề HTTP và một phần thân phải được gửi đến các web client.
Các kết quả này được xác định bởi play.mvc.Result, và lớp play.mvc.Results cung
cấp nhiều sợ giúp đỡ để tạo ra kết quả HTTP tiêu chuẩn, chẳng hạn như phương
pháp ok chúng tôi được sử dụng trong phần trước:
public Result index() {
return ok("Hello world!");
}
Here are several examples that create various results:
- Dưới đây là một số ví dụ để tạo ra kết quả khác nhau:
Result ok = ok("Hello world!");
Result notFound = notFound();
Result pageNotFound = notFound("<h1>Page not
found</h1>").as("text/html");
Result badRequest = badRequest(views.html.form.render(formWithErrors));
7
Result oops = internalServerError("Oops");
Result anyStatus = status(488, "Strange response type");
All of these helpers can be found in the play.mvc.Results class.
- Tất cả những phương thức hỗ trợ có thể được tìm thấy trong lớp play.mvc.Results.
§Redirects are simple results too
-Chuyển hướng là kết quả đơn giản
Redirecting the browser to a new URL is just another kind of simple result. However,
these result types don’t have a response body.
There are several helpers available to create redirect results:
- Chuyển hướng trình duyệt tới một URL mới chỉ là một loại kết quả đơn giản. Tuy
nhiên, những kết quả không có nội dung phản hồi.
Có một số phương thứ có sẵn để tạo ra kết quả chuyển hướng:
public Result index() {
return redirect("/user/home");
}
The default is to use a 303 SEE_OTHER response type, but you can also specify a
more specific status code:
- Giá trị mặc định là sử dụng một loại phản hồi 303 SEE_OTHER, nhưng bạn cũng
có thể chỉ định một mã trạng thái cụ thể hơn:
public Result index() {
return temporaryRedirect("/user/home");
}
8
HTTP routing
-Đinhj tuyến HTTP
§The built-in HTTP router
The router is the component that translates each incoming HTTP request to an
action call (a public method in a controller class).
An HTTP request is seen as an event by the MVC framework. This event contains
two major pieces of information:
 the request path (such as /clients/1542, /photos/list), including the
query string.
 the HTTP method (GET, POST, …).
Routes are defined in the conf/routes file, which is compiled. This means that
you’ll see route errors directly in your browser:
-Việc xây dựng trong bộ định tuyến HTTP
Các router là thành phần dịch mỗi yêu cầu HTTP đến với một cuộc gọi hành động
(một phương pháp nào trong một lớp điều khiển).
Một yêu cầu HTTP được xem như là một sự kiện do khuôn khổ MVC. Sự kiện này
bao gồm hai phần chính của thông tin:
• con đường yêu cầu (ví dụ như / clients/ 1542, / photos/ list), bao gồm các chuỗi
truy vấn.
• phương pháp HTTP (GET, POST, ...).
Các tuyến đường được quy định tại conf / routes tập tin, được biên dịch. Điều này
có nghĩa rằng bạn sẽ nhìn thấy các lỗi đường trực tiếp trong trình duyệt của bạn:
9
§Dependency Injection
Play supports generating two types of routers, one is a dependency injected router,
the other is a static router. The default is the dependency injected router, and that
is also the case in the Play seed Activator templates, since we recommend you use
dependency-injected controllers. If you need to use static controllers you can switch
to the static routes generator by adding the following configuration to your
build.sbt:
-Dependency Injection
10
Phát hỗ trợ tạo ra hai loại router, một là một phụ thuộc router, là một bộ định
tuyến tĩnh. Mặc định là phụ thuộc router, và đó cũng là trường hợp trong các mẫu
Activator play giống, kể từ khi chúng tôi khuyên bạn sử dụng bộ điều khiển phụ
thuộc . Nếu bạn cần phải sử dụng các bộ điều khiển tĩnh bạn có thể chuyển sang
bộ tạo các tuyến tĩnh bằng cách thêm các cấu hình sau đây để build.sbt của
bạn:bằng cách thêm các cấu hình sau đây để build.sbt của bạn:
routesGenerator := StaticRoutesGenerator
The code samples in Play’s documentation assume that you are using the injected
routes generator. If you are not using this, you can trivially adapt the code samples
for the static routes generator, either by prefixing the controller invocation part of
the route with an @ symbol, or by declaring each of your action methods as static.
- Các mẫu mã trong tài liệu hướng dẫn Play cho rằng bạn đang sử dụng các bô tạo
tuyến đường injected. Nếu bạn không sử dụng này, bạn có thể thích ứng trivially
mẫu mã cho các bộ tạo các tuyến đường tĩnh, hoặc bằng cách đặt trước phần điều
khiển gọi với một biểu tượng @, hoặc bằng cách tuyên bố mỗi phương pháp
hành động của bạn là tĩnh.
§The routes file syntax
conf/routes is the configuration file used by the router. This file lists all of the
routes needed by the application. Each route consists of an HTTP method and URI
pattern associated with a call to an action method.
Let’s see what a route definition looks like:
-Các cú pháp tuyến đường tập tin
conf / routes là file cấu hình được sử dụng bởi các bộ định tuyến. File này liệt kê
tất cả các tuyến đường cần thiết cho các ứng dụng. Mỗi tuyến đường bao gồm một
phương thức HTTP và mẫu URI liên kết với một cuộc gọi đến một phương pháp
hành động.
Hãy xem những gì một định nghĩa đường trông giống như:
11
GET /clients/:id controllers.Clients.show(id: Long)
Note: in the action call, the parameter type comes after the parameter name, like in
Scala.
Each route starts with the HTTP method, followed by the URI pattern. The last
element of a route is the call definition.
You can also add comments to the route file, with the # character:
- Lưu ý: trong các cuộc gọi hành động, các loại tham số đi kèm sau tên tham số,
như trong Scala.
Mỗi tuyến đường bắt đầu với các phương thức HTTP, tiếp theo là mẫu URI. Yếu tố
cuối cùng của một tuyến đường là định nghĩa cuộc gọi.
Bạn cũng có thể thêm ý kiến để các tập tin tuyến đường, với các ký tự #:
# Display a client.
GET /clients/:id controllers.Clients.show(id: Long)
§The HTTP method
The HTTP method can be any of the valid methods supported by HTTP (GET,
PATCH, POST, PUT, DELETE, HEAD, OPTIONS).
-Các phương thức HTTP
phương thức HTTP có thể là bất kỳ một phương thức hỗ trợ hợp lệ bởi HTTP
(GET, Patch, POST, PUT, DELETE, HEAD, OPTIONS).
§The URI pattern
The URI pattern defines the route’s request path. Some parts of the request path
can be dynamic.
12
§Static path
For example, to exactly match GET /clients/all incoming requests, you can
define this route:
-Các mẫu URI
mẫu URI xác định con đường yêu cầu của tuyến đường. . Một số bộ phận của con
đường yêu cầu có thể động
-Phần tĩnh
Ví dụ, so sánh chính xác GET / clients/all các yêu cầu gửi đến, bạn có thể xác
định tuyến đường này:
GET /clients/all controllers.Clients.list()
§Dynamic parts
If you want to define a route that, say, retrieves a client by id, you need to add a
dynamic part:
-Phần động
Nếu bạn muốn xác định 1 tuyến đường, để lấy một id của clinet bạn cần add một
đường dẫ động
GET /clients/:id controllers.Clients.show(id: Long)
Note: A URI pattern may have more than one dynamic part.
The default matching strategy for a dynamic part is defined by the regular
expression [^/]+, meaning that any dynamic part defined as :id will match exactly
one URI path segment.
- Lưu ý:. Một mẫu URI có thể có nhiều hơn một phần động
mặc định cho một phần năng động được xác định bởi các biểu thức chính quy [^ /]
+, có nghĩa là bất kỳ phần động định nghĩa là: id sẽ phù hợp chính xác một URI
đoạn đường.
13
§Dynamic parts spanning several /
If you want a dynamic part to capture more than one URI path segment, separated
by forward slashes, you can define a dynamic part using the *id syntax, which
uses the .* regular expression:
-Phần động mổ rộng/
Nếu bạn muốn có một phần động để nắm bắt nhiều hơn một đoạn đường URI,
cách nhau bằng dấu gạch chéo, bạn có thể xác định một phần động bằng cách
sử dụng cú pháp * id, trong đó sử dụng * biểu hiện thường xuyên.:
GET /files/*name controllers.Application.download(name)
Here, for a request like GET /files/images/logo.png, the name dynamic part will
capture the images/logo.png value.
-Ở đây, đối với một yêu cầu GET /files/images/logo.png, name phần động
sẽ lấy giá trị ảnh trong thư múc images/ logo.png
§Dynamic parts with custom regular expressions
You can also define your own regular expression for a dynamic part, using the
$id<regex> syntax:
-Phần động với tùy chỉnh biểu thức quy tắc.
Bạn cũng có thể xác định biểu thức quy tắc của riêng bạn cho một phần động, sử
dụng $ id cú pháp:
GET /items/$id<[0-9]+> controllers.Items.show(id: Long)
§Call to action generator method
The last part of a route definition is the call. This part must define a valid call to an
action method.
If the method does not define any parameters, just give the fully-qualified method
name:
14
-Gọi các hành động tạo phương thức
Phần cuối cùng của route là cuộc gọi. Phần này phải xác định một cuộc gọi hợp lệ
để một phương thức hành động.
Nếu phương thức này không xác định bất kỳ các thông số, chỉ cần cung cấp tên
phương thức đầy đủ điều kiện:
GET / controllers.Application.homePage()
If the action method defines parameters, the corresponding parameter values will
be searched for in the request URI, either extracted from the URI path itself, or from
the query string.
-Nếu các phương thức hành động xác định các thông số, các giá trị tham số tương
ứng sẽ được tìm kiếm trong yêu cầu URI, hoặc chiết xuất từ các đường dẫn URI
chính nó, hoặc từ chuỗi truy vấn.
# Extract the page parameter from the path.
# i.e. http://myserver.com/index
GET /:page controllers.Application.show(page)
Or:
# Extract the page parameter from the query string.
# i.e. http://myserver.com/?page=index
GET / controllers.Application.show(page)
Here is the corresponding show method definition in the controllers.Application
controller:
- Đây là định nghĩa phương thức show tương ứng trong bộ điều khiển
controllers.Application:
public Result show(String page) {
String content = Page.getContentOf(page);
response().setContentType("text/html");
15
return ok(content);
}
§Parameter types
For parameters of type String, the parameter type is optional. If you want Play to
transform the incoming parameter into a specific Scala type, you can add an
explicit type:
-Các loại tham số
cho các tham số kiểu String, các loại tham số là tùy chọn. Nếu bạn muốn Play biến
đổi các tham số đầu vào thành một loại Scala cụ thể, bạn có thể thêm một loại rõ
ràng:
GET /clients/:id controllers.Clients.show(id: Long)
Then use the same type for the corresponding action method parameter in the
controller:
- Sau đó, sử dụng các loại tương tự cho các tham số phương thức hành động
tương ứng trong bộ điều khiển:
public Result show(Long id) {
Client client = clientService.findById(id);
return ok(views.html.Client.show(client));
}
Note: The parameter types are specified using a suffix syntax. Also, the generic
types are specified using the [] symbols instead of <>, as in Java. For example,
List[String] is the same type as the Java List<String>.
- Lưu ý: Các loại tham số được chỉ định bằng một cú pháp hậu tố. Ngoài ra, các
loại chung được quy định bằng cách sử dụng [] biểu tượng thay vì <>, như trong
Java. Ví dụ, Danh sách [Chuỗi] là loại giống như Danh sách Java.
§Parameters with fixed values
Sometimes you’ll want to use a fixed value for a parameter:
16
-Các thông số với các giá trị cố định
Đôi khi bạn sẽ muốn sử dụng một giá trị cố định cho một tham số:
# Extract the page parameter from the path, or fix the value for /
GET / controllers.Application.show(page =
"home")
GET /:page controllers.Application.show(page)
§Parameters with default values
You can also provide a default value that will be used if no value is found in the
incoming request:
-Các thông số có giá trị mặc định
Bạn cũng có thể cung cấp một giá trị mặc định sẽ được sử dụng nếu không có giá
trị được tìm thấy trong yêu cầu đầu vào:
# Pagination links, like /clients?page=3
GET /clients controllers.Clients.list(page: Int ?= 1)
§Optional parameters
You can also specify an optional parameter that does not need to be present in all
requests:
-Thông số tùy chọn
Bạn cũng có thể chỉ định một tham số tùy chọn mà không cần phải có mặt trong tất
cả các yêu cầu:
# The version parameter is optional. E.g. /api/list-all?version=3.0
GET /api/list-all controllers.Api.list(version ?= null)
§Routing priority
Many routes can match the same request. If there is a conflict, the first route (in
declaration order) is used.
17
§Reverse routing
The router can be used to generate a URL from within a Java call. This makes it
possible to centralize all your URI patterns in a single configuration file, so you can
be more confident when refactoring your application.
For each controller used in the routes file, the router will generate a ‘reverse
controller’ in the routes package, having the same action methods, with the same
signature, but returning a play.mvc.Call instead of a play.mvc.Result.
The play.mvc.Call defines an HTTP call, and provides both the HTTP method and
the URI.
For example, if you create a controller like:
-Ưu tiên định tuyến
Nhiều tuyến đường có thể phù hợp với các yêu cầu tương tự. Nếu có mâu thuẫn,
các tuyến đường đầu tiên (theo thứ tự khai) được sử dụng.
Xếp định tuyến
Router có thể được sử dụng để tạo ra một URL trong một cuộc gọi Java. Điều này
làm cho nó có thể tập trung tất cả các mẫu URI của bạn trong một tập tin cấu hình
duy nhất, vì vậy bạn có thể tự tin hơn khi sắp xếp ứng dụng của bạn.
Đối với mỗi bộ điều khiển được sử dụng trong các tập tin các tuyến đường, các
router sẽ tạo ra một 'điều khiển đảo ngược' trong gói đường, có phương pháp
hành động tương tự, với cùng một chữ ký, nhưng trở về một play.mvc.Call thay vì
một play.mvc.Result.
các play.mvc.Call định nghĩa một cuộc gọi HTTP, và cung cấp cho cả hai phương
thức HTTP và URI.
Ví Ví dụ, nếu bạn tạo một điều khiển như:
package controllers;
import play.*;
18
import play.mvc.*;
public class Application extends Controller {
public Result hello(String name) {
return ok("Hello " + name + "!");
}
}
And if you map it in the conf/routes file:
# Hello action
GET /hello/:name controllers.Application.hello(name)
You can then reverse the URL to the hello action method, by using the
controllers.routes.Application reverse controller:
// Redirect to /hello/Bob
public Result index() {
return redirect(controllers.routes.Application.hello("Bob"));
}
Note: There is a routes subpackage for each controller package. So the action
controllers.admin.Application.hello can be reversed via
controllers.admin.routes.Application.hello.
Manipulating the response
-Thao tác với các phane hồi
§Changing the default Content-Type
-Thay đổi giá trị mặ định các kiểu Content
The result content type is automatically inferred from the Java value you specify as
body.
19
For example:
- Các kiểu nội dung kết quả được tự động suy ra từ giá trị Java bạn chỉ định khi
trong phần thân
Ví dụ:
Result textResult = ok("Hello World!");
Will automatically set the Content-Type header to text/plain, while:
- Sẽ tự động thiết lập các tiêu đề Content-Type text / plain, trong khi
JsonNode json = Json.toJson(object);
Result jsonResult = ok(json);
will set the Content-Type header to application/json.
This is pretty useful, but sometimes you want to change it. Just use the
as(newContentType) method on a result to create a new similar result with a
different Content-Type header:
- Sẽ thiết lập các tiêu đề Content-Type application / json.
Điều này là khá hữu ích, nhưng đôi khi bạn muốn thay đổi nó. Chỉ cần sử dụng các
phương pháp như (newContentType) vào một kết quả để tạo ra một kết quả mới
tương tự với một Content-Type tiêu đề khác nhau:
Result htmlResult = ok("<h1>Hello World!</h1>").as("text/html");
§Setting HTTP response headers
-Thiết lập tiêu đề phản hồi HTTP
public Result index() {
response().setContentType("text/html");
response().setHeader(CACHE_CONTROL, "max-age=3600");
response().setHeader(ETAG, "xxx");
return ok("<h1>Hello World!</h1>");
20
}
Note that setting an HTTP header will automatically discard any previous value.
- Lưu ý rằng thiết lập một tiêu đề HTTP sẽ tự động loại bỏ bất kỳ giá trị trước đó.
§Setting and discarding cookies
Cookies are just a special form of HTTP headers, but Play provides a set of helpers
to make it easier.
You can easily add a Cookie to the HTTP response:
-Thiết lập và loại bỏ các tập tin cookie
Cookie chỉ là một hình thức đặc biệt của các tiêu đề HTTP, nhưng Play cung cấp
một tập hợp của những cơ chế để làm cho nó dễ dàng hơn.
Bạn có thể dễ dàng thêm một Cookie để đáp ứng HTTP:
response().setCookie("theme", "blue");
If you need to set more details, including the path, domain, expiry, whether it’s
secure, and whether the HTTP only flag should be set, you can do this with the
overloaded methods:
- Nếu bạn cần phải thiết lập thêm chi tiết, bao gồm cả đường dẫn, tên miền, hạn
sử dụng, cho dù đó là an toàn, và liệu HTTP chỉ nên được thiết lập, bạn có thể
làm điều này với các phương thức :
response().setCookie(
"theme", // name
"blue", // value
3600, // maximum age
"/some/path", // path
".example.com", // domain
false, // secure
true // http only
21
);
To discard a Cookie previously stored on the web browser:
- Để loại bỏ một Cookie được lưu trước đó trên các trình duyệt web:
response().discardCookie("theme");
If you set a path or domain when setting the cookie, make sure that you set the
same path or domain when discarding the cookie, as the browser will only discard it
if the name, path and domain match.
§Specifying the character encoding for text results
For a text-based HTTP response it is very important to handle the character
encoding correctly. Play handles that for you and uses utf-8 by default.
The encoding is used to both convert the text response to the corresponding bytes
to send over the network socket, and to add the proper ;charset=xxx extension to
the Content-Type header.
The encoding can be specified when you are generating the Result value:
-Nếu bạn đặt một đường dẫn hoặc miền khi thiết lập các cookie, hãy chắc chắn
rằng bạn thiết lập đường dẫn hoặc tên miền khi loại bỏ các tập tin cookie, như trình
duyệt sẽ chỉ loại bỏ nó nếu tên, đường dẫn và tên miền không đúng.
Xác định các ký tự mã hóa cho kết quả văn bản
Đối với một hành động HTTP dựa trên văn bản là rất quan trọng để xử lý các ký tự
mã hóa một cách chính xác. Play xử lý đó cho bạn và sử dụng utf-8 theo mặc định.
Việc mã hóa được sử dụng cho cả hai chuyển đổi các hnahf động văn bản để các
byte tương ứng để gửi qua các ổ cắm mạng, và để thêm thích hợp; charset = xxx
mở rộng của header Content-Type .
các mã hóa có thể được chỉ định khi bạn đang tạo ra các giá trị kết quả:
public Result index() {
22
return ok("<h1>Hello World!</h1>", "iso-8859-1").as("text/html;
charset=iso-8859-1");
}
Session and Flash scopes
-Phiên làm việc và phạm vi của Flash
§How it is different in Play
-Phan biệt sự khác nhau trong Play
If you have to keep data across multiple HTTP requests, you can save them in the
Session or the Flash scope. Data stored in the Session are available during the
whole user session, and data stored in the flash scope are only available to the
next request.
It’s important to understand that Session and Flash data are not stored in the
server but are added to each subsequent HTTP Request, using Cookies. This
means that the data size is very limited (up to 4 KB) and that you can only store
string values.
Cookies are signed with a secret key so the client can’t modify the cookie data (or it
will be invalidated). The Play session is not intended to be used as a cache. If you
need to cache some data related to a specific session, you can use the Play built-in
cache mechanism and use the session to store a unique ID to associate the
cached data with a specific user.
Note: There is no technical timeout for the session, which expires when the user
closes the web browser. If you need a functional timeout for a specific application,
just store a timestamp into the user Session and use it however your application
needs (e.g. for a maximum session duration, maximum inactivity duration, etc.).
You can also set the maximum age of the session cookie by configuring the key
23
play.http.session.maxAge (in milliseconds) in application.conf, but note that this
does not prevent an attacker from keeping and reusing the cookie past the
expiration date.
-Nếu bạn có để giữ cho dữ liệu trên nhiều yêu cầu HTTP, bạn có thể lưu chúng
trong phiên làm việc hoặc phạm vi Flash. Dữ liệu được lưu trữ trong các phiên có
sẵn trong các phiên sử dụng toàn bộ, và các dữ liệu được lưu trữ trong phạm vi
đèn flash chỉ có sẵn cho các yêu cầu tiếp theo.
Điều quan trọng là phải hiểu rằng phiên và Flash dữ liệu không được lưu trữ trong
các máy chủ nhưng được thêm vào mỗi HTTP tiếp theo Yêu cầu, sử dụng
Cookies. Điều này có nghĩa rằng kích thước dữ liệu rất hạn chế (lên đến 4 KB) và
rằng bạn chỉ có thể lưu trữ các giá trị chuỗi.
Cookie được kết hợp với một khóa bí mật vì vậy khách hàng không có thể sửa
đổi các dữ liệu cookie (hoặc nó sẽ không còn giá trị). Các phiên Play không được
dự định sẽ được sử dụng như một bộ nhớ cache. Nếu bạn cần bộ nhớ cache một
số dữ liệu liên quan đến một phiên cụ thể, bạn có thể sử dụng Play được xây dựng
trong cơ chế bộ nhớ cache và sử dụng phiên để lưu trữ một ID duy nhất để kết
hợp các dữ liệu được lưu trữ với một người dùng cụ thể.
Lưu ý: Không có thời gian chờ kỹ thuật cho phiên, hết hiệu lực khi người dùng
đóng trình duyệt web. Nếu bạn cần một thời gian chờ chức năng cho một ứng
dụng cụ thể, chỉ cần lưu trữ một dấu thời gian vào các phiên lam việc dùng và sử
dụng nó tuy nhiên nhu cầu ứng dụng của bạn (ví dụ như trong thời hạn tối đa
phiên, thời gian không hoạt động tối đa, vv). Bạn cũng có thể thiết lập độ tuổi tối đa
của các cookie phiên bằng cách cấu hình play.http.session.maxAge chính (trong
mili giây) trong application.conf, nhưng lưu ý rằng điều này không ngăn chặn kẻ tấn
công từ việc giữ và tái sử dụng cookie quá hạn
§Storing data into the Session
-lưu trư lữ liệu vào phiên làm việc
As the Session is just a Cookie, it is also just an HTTP header, but Play provides a
helper method to store a session value:
-1 phiên làm việc chỉ có 1 Cookie, nó cũng chỉ là một HTTP header, nhưng Play
cung cấp một phương pháp giúp đỡ để lưu trữ một giá trị session:
24
public Result login() {
session("connected", "user@gmail.com");
return ok("Welcome!");
}
Theo cùng một cách, bạn có thể loại bỏ bất kỳ giá trị so với phiên đầu vào.
public Result logout() {
session().remove("connected");
return ok("Bye");
}
§Reading a Session value
-Đọc 1 giá trị trong phiên làm việc
You can retrieve the incoming Session from the HTTP request:
- Bạn có thể lấy các giá trị từ phiên đến từ yêu cầu HTTP:
public Result index() {
String user = session("connected");
if(user != null) {
return ok("Hello " + user);
} else {
return unauthorized("Oops, you are not connected");
}
}
§Discarding the whole session
If you want to discard the whole session, there is special operation:
-Loại bỏ toàn bộ phiên
Nếu bạn muốn loại bỏ toàn bộ phiên, có hoạt động đặc biệt:
public Result logout() {
session().clear();
return ok("Bye");
25
}
§Flash scope
The Flash scope works exactly like the Session, but with two differences:
 data are kept for only one request
 the Flash cookie is not signed, making it possible for the user to modify it.
Important: The flash scope should only be used to transport success/error
messages on simple non-Ajax applications. As the data are just kept for the next
request and because there are no guarantees to ensure the request order in a
complex Web application, the Flash scope is subject to race conditions.
So for example, after saving an item, you might want to redirect the user back to
the index page, and you might want to display an error on the index page saying
that the save was successful. In the save action, you would add the success
message to the flash scope:
-Phạm vi flash
Phạm vi Flash làm việc chính xác như các phiên, nhưng với hai sự khác biệt:
• Dữ liệu được lưu giữ cho chỉ có một yêu cầu
• cookie Flash không được kết hợp, làm cho nó có thể cho người sử dụng để sửa
đổi nó.
Quan trọng: Phạm vi flash chỉ nên sử dụng để vận chuyển các thông điệp thành
công / lỗi trên các ứng dụng không-Ajax đơn giản. Khi dữ liệu được chỉ giữ cho các
yêu cầu tiếp theo và vì không có bảo đảm để đảm bảo trật tự yêu cầu trong một
ứng dụng web phức tạp, phạm vi Flash là tùy thuộc vào điều kiện chủng tộc.
Vì vậy, ví dụ, sau khi tiết kiệm một mục, bạn có thể muốn chuyển hướng người sử
dụng trở lại các trang chỉ mục, và bạn có thể muốn hiển thị một lỗi trên trang chỉ
mục nói rằng tiết kiệm là thành công. Trong lưu hành động, bạn sẽ thêm các tin
nhắn thành công với phạm vi đèn flash:
public Result save() {
26
flash("success", "The item has been created");
return redirect("/home");
}
Then in the index action, you could check if the success message exists in the flash
scope, and if so, render it:
-Sau đó, trong hành động chỉ số, bạn có thể kiểm tra xem các tin nhắn thành công
tồn tại trong phạm vi flash, và nếu như vậy, làm cho nó
public Result index() {
String message = flash("success");
if(message == null) {
message = "Welcome!";
}
return ok(message);
}
A flash value is also automatically available in Twirl templates. For example:
-Một giá trị flash cũng là tự động có sẵn trong Twirl mẫu. Ví dụ:
@if(flash.containsKey("success")) {
@flash.get("success")
} else {
Welcome!
}
Body parsers
§What is a body parser?
An HTTP request is a header followed by a body. The header is typically small - it
can be safely buffered in memory, hence in Play it is modelled using the
RequestHeader class. The body however can be potentially very long, and so is not
27
buffered in memory, but rather is modelled as a stream. However, many request
body payloads are small and can be modelled in memory, and so to map the body
stream to an object in memory, Play provides a BodyParser abstraction.
Since Play is an asynchronous framework, the traditional InputStream can’t be
used to read the request body - input streams are blocking, when you invoke read,
the thread invoking it must wait for data to be available. Instead, Play uses an
asynchronous streaming library called Akka Streams. Akka Streams is an
implementation of Reactive Streams, a SPI that allows many asynchronous
streaming APIs to seamlessly work together, so though traditional InputStream
based technologies are not suitable for use with Play, Akka Streams and the entire
ecosystem of asynchronous libraries around Reactive Streams will provide you with
everything you need.
-Body phân tích cú pháp
một bộ phân tích Body là gì?
Một yêu cầu HTTP là một tiêu đề tiếp theo là một Body. Các tiêu đề là thường nhỏ
- nó có thể được đệm một cách an toàn trong bộ nhớ, do đó trong Chơi nó được
mô hình hóa bằng cách sử dụng lớp RequestHeader. Tuy nhiên Body có thể có
tiềm năng rất dài, do đó cũng không đệm trong bộ nhớ, nhưng thay vì được mô
phỏng như một dòng suối. Tuy nhiên, nhiều trọng tải theo yêu cầu cơ thể là nhỏ và
có thể được mô hình trong bộ nhớ, và do đó, để lập bản đồ các dòng cơ thể đến
một đối tượng trong bộ nhớ, Play cung cấp một trừu tượng BodyParser.
Kể từ Play là một khuôn khổ không đồng bộ, InputStream truyền thống không thể
được sử dụng để đọc nội dung yêu cầu - dòng đầu vào được ngăn chặn, khi bạn
gọi đọc, thread gọi nó phải đợi cho dữ liệu có sẵn. Thay vào đó, Play sử dụng một
thư viện trực tuyến không đồng bộ được gọi là Akka Streams. Akka Streams là một
thực hiện của Reactive Streams, một SPI cho phép nhiều API đồng bộ trực tuyến
để liên tục làm việc với nhau, vì vậy mặc dù công nghệ InputStream truyền thống
không phù hợp để sử dụng với Play, Akka Streams và toàn bộ hệ sinh thái của các
thư viện không đồng bộ xung quanh phản ứng Streams sẽ cung cấp bạn với tất cả
mọi thứ bạn cần.
28
§Using the built in body parsers
Most typical web apps will not need to use custom body parsers, they can simply
work with Play’s built in body parsers. These include parsers for JSON, XML,
forms, as well as handling plain text bodies as Strings and byte bodies as
ByteString.
§The default body parser
The default body parser that’s used if you do not explicitly select a body parser will
look at the incoming Content-Type header, and parses the body accordingly. So
for example, a Content-Type of type application/json will be parsed as a
JsonNode, while a Content-Type of application/x-form-www-urlencoded will be
parsed as a Map<String, String[]>.
The request body can be accessed through the body() method on Request, and is
wrapped in a RequestBody object, which provides convenient accessors to the
various types that the body could be. For example, to access a JSON body:
-Sử dụng được xây dựng trong bộ phân tích Body
ứng dụng web điển hình, hầu hết sẽ không cần phải sử dụng bộ phân tích Body
tùy chỉnh, họ chỉ đơn giản có thể làm việc với Play được xây dựng trong bộ phân
tích Body. Chúng bao gồm phân tích cú pháp JSON, XML, hình thức, cũng như xử
lý các cơ quan văn bản đơn giản như Strings và các cơ quan byte như ByteString.
Phân tích cú pháp Body thể mặc định
Bộ phân tích cơ thể mặc định mà được sử dụng nếu bạn không chọn một cách rõ
ràng một bộ phân tích cơ thể sẽ xem xét đến Content-Type header, và phân tích
cơ thể phù hợp. Vì vậy, ví dụ, một Content-Type của ứng dụng loại / json sẽ được
phân tích như một JsonNode, trong khi một Content-Type của application / x-form-
www-urlencoded sẽ được phân tích như một bản đồ.
Cơ thể yêu cầu có thể được truy cập thông qua các phương pháp cơ thể () theo
yêu cầu, và được gói trong một đối tượng RequestBody, cung cấp truy cập thông
thuận tiện đến các loại mà cơ thể có thể được. Ví dụ, để truy cập vào một cơ thể
JSON:
29
public Result index() {
JsonNode json = request().body().asJson();
return ok("Got name: " + json.get("name").asText());
}
The following is a mapping of types supported by the default body parser:
 text/plain: String, accessible via asText().
 application/json: com.fasterxml.jackson.databind.JsonNode,
accessible via asJson().
 application/xml, text/xml or application/XXX+xml: org.w3c.Document,
accessible via asXml().
 application/form-url-encoded: Map<String, String[]>, accessible via
asFormUrlEncoded().
 multipart/form-data: MultipartFormData, accessible via
asMultipartFormData().
 Any other content type: RawBuffer, accessible via asRaw().
The default body parser tries to determine if the request has a body before it tries to
parse. According to the HTTP spec, the presence of either the Content-Length or
Transfer-Encoding header signals the presence of a body, so the parser will only
parse if one of those headers is present, or on FakeRequest when a non-empty
body has explicitly been set.
If you would like to try to parse a body in all cases, you can use the AnyContent
body parser, described below.
- Sau đây là một bản đồ của các loại hỗ trợ bởi Body mặc định phân tích cú pháp:
• text / plain: String, truy cập qua asText ().
• application / json:. Com.fasterxml.jackson.databind.JsonNode, truy cập qua
asJson ()
• Ứng dụng / xml text / xml hoặc ứng dụng / XXX + xml: org.w3c.Document, truy
cập qua asXml ().
• ứng dụng / form-url-mã hóa: bản đồ, Truy cập qua asFormUrlEncoded ().
30
• multipart / form-data: MultipartFormData, truy cập qua asMultipartFormData ().
• Bất kỳ loại nội dung khác:. RawBuffer, truy cập qua asRaw ()
Bộ phân tích Body mặc định sẽ cố gắng để xác định xem các yêu cầu có một Body
trước khi nó sẽ cố gắng để phân tích. Theo spec HTTP, sự hiện diện của một trong
hai Content-Length hoặc tiêu đề Transfer-Encoding báo hiệu sự hiện diện của một
Body, do đó, các phân tích cú pháp sẽ chỉ phân tích nếu một trong những tiêu đề là
hiện tại, hoặc trên FakeRequest khi cơ thể không sản phẩm nào có một cách rõ
ràng được thiết lập.
Nếu bạn muốn thử để phân tích một Body trong tất cả các trường hợp, bạn có thể
sử dụng bộ phân tích Body AnyContent, mô tả dưới đây.
§Choosing an explicit body parser
If you want to explicitly select a body parser, this can be done using the
@BodyParser.Of annotation, for example:
-Lựa chọn một trình phân tích Body rõ ràng
Nếu bạn muốn chọn một cách rõ ràng một bộ phân tích Body, điều này có thể
được thực hiện bằng cách sử dụng chú thích @ BodyParser.Of, ví dụ:
@BodyParser.Of(BodyParser.Text.class)
public Result index() {
RequestBody body = request().body();
return ok("Got text: " + body.asText());
}
The body parsers that Play provides out of the box are all inner classes of the
BodyParser class. Briefly, they are:
 Default: The default body parser.
 AnyContent: Like the default body parser, but will parse bodies of GET, HEAD
and DELETE requests.
 Json: Parses the body as JSON.
31
 TolerantJson: Like Json, but does not validate that the Content-Type
header is JSON.
 Xml: Parses the body as XML.
 TolerantXml: Like Xml, but does not validate that the Content-Type header
is XML.
 Text: Parses the body as a String.
 TolerantText: Like Text, but does not validate that the Content-Type is
text/plain.
 Bytes: Parses the body as a ByteString.
 Raw: Parses the body as a RawBuffer. This will attempt to store the body in
memory, up to Play’s configured memory buffer size, but fallback to writing it
out to a File if that’s exceeded.
 FormUrlEncoded: Parses the body as a form.
 MultipartFormData: Parses the body as a multipart form, storing file parts
to files.
 Empty: Does not parse the body, rather it ignores it.
- Các bộ phân tích Body Play cung cấp ra khỏi hộp là tất cả các lớp bên trong
của lớp BodyParser. Tóm lại, đó là:
• Mặc định: Bộ phân tích Body mặc định.
• AnyContent: Giống như bộ phân tích Body mặc định, nhưng sẽ phân tích
phương thức của GET, HEAD và DELETE yêu cầu.
• Json:. Tách Body như JSON
• TolerantJson: Giống như JSON, nhưng không xác nhận rằng Content-Type
tiêu đề là JSON.
• Xml: Tách cơ thể như XML.
• TolerantXml:. Giống như Xml, nhưng không xác nhận rằng Content-Type
tiêu đề là XML
• Tiêu đề: Tách cơ thể như một string.
• TolerantText: Giống như văn bản, nhưng không xác nhận rằng Content-Type
32
là text / plain.
• Bytes: Tách Body như một ByteString.
• Nguyên: Tách Body như một RawBuffer. Điều này sẽ cố gắng để lưu trữ
Body trong bộ nhớ, lên đến Play cấu hình kích thước bộ nhớ đệm, nhưng dự
phòng để viết nó ra một tập tin nếu đó là vượt quá.
• FormUrlEncoded: Tách Body như một hình thức.
• MultipartFormData: Tách Body như một multipart hình thức, lưu trữ phần
tập tin vào tập tin.
• rỗng: không phân tích Body, chứ không phải nó bỏ qua nó.
§Content length limits
Most of the built in body parsers buffer the body in memory, and some buffer it on
disk. If the buffering was unbounded, this would open up a potential vulnerability to
malicious or careless use of the application. For this reason, Play has two
configured buffer limits, one for in memory buffering, and one for disk buffering.
The memory buffer limit is configured using play.http.parser.maxMemoryBuffer,
and defaults to 100KB, while the disk buffer limit is configured using
play.http.parser.maxDiskBuffer, and defaults to 10MB. These can both be
configured in application.conf, for example, to increase the memory buffer limit
to 256KB:
- Giới hạn chiều dài nội dung
Hầu hết được xây dựng trong bộ phân tích Body trong bộ nhớ, và một số bộ
đệm trên đĩa. Nếu đệm là vô hạn, điều này sẽ mở ra một lỗ hổng tiềm năng để sử
dụng độc hại hoặc bất cẩn của ứng dụng. Vì lý do này, Play có hai giới hạn bộ đệm
cấu hình, một cho trong bộ nhớ đệm, và một cho đệm đĩa.
Các giới hạn bộ nhớ đệm được cấu hình sử dụng
play.http.parser.maxMemoryBuffer, và mặc định là 100KB, trong khi giới hạn đĩa
đệm được cấu hình sử dụng play.http.parser.maxDiskBuffer, và mặc định là 10MB.
33
Những cả hai có thể được cấu hình trong application.conf, ví dụ, để tăng giới hạn
bộ nhớ đệm 256KB:
play.http.parser.maxMemoryBuffer = 256kb
You can also limit the amount of memory used on a per action basis by writing a
custom body parser, see below for details.
- Bạn cũng có thể giới hạn số lượng bộ nhớ được sử dụng trên một cơ sở cho mỗi
hành động bằng cách viết một bộ phân tích cơ thể tùy chỉnh, xem dưới đây để biết
chi tiết.
§Writing a custom body parser
A custom body parser can be made by implementing the BodyParser class. This
class has one abstract method:
-Viết một phân tích cú pháp body tùy chỉnh
Một phân tích body tùy chỉnh có thể được thực hiện bằng cách thực hiện các lớp
BodyParser. Lớp này có một phương pháp trừu tượng:
public abstract Accumulator<ByteString, F.Either<Result, A>>
apply(RequestHeader request);
The signature of this method may be a bit daunting at first, so let’s break it down.
The method takes a RequestHeader. This can be used to check information about
the request - most commonly, it is used to get the Content-Type, so that the body
can be correctly parsed.
The return type of the method is an Accumulator. An accumulator is a thin layer
around an Akka Streams Sink. An accumulator asynchronously accumulates
streams of elements into a result, it can be run by passing in an Akka Streams
Source, this will return a CompletionStage that will be redeemed when the
accumulator is complete. It is essentially the same thing as a Sink<E,
CompletionStage<A>>, in fact it is nothing more than a wrapper around this type,
34
but the big difference is that Accumulator provides convenient methods such as
map, mapFuture, recover etc. for working with the result as if it were a promise,
where Sink requires all such operations to be wrapped in a mapMaterializedValue
call.
The accumulator that the apply method returns consumes elements of type
ByteString - these are essentially arrays of bytes, but differ from byte[] in that
ByteString is immutable, and many operations such as slicing and appending
happen in constant time.
The return type of the accumulator is F.Either<Result, A>. This says it will either
return a Result, or it will return a body of type A. A result is generally returned in
the case of an error, for example, if the body failed to be parsed, if the Content-
Type didn’t match the type that the body parser accepts, or if an in memory buffer
was exceeded. When the body parser returns a result, this will short circuit the
processing of the action - the body parsers result will be returned immediately, and
the action will never be invoked.
- Chữ ký của phương pháp này có thể là một chút khó khăn lúc đầu, vì vậy hãy
phá vỡ nó xuống.
Phương pháp này có một RequestHeader. Điều này có thể được sử dụng để kiểm
tra thông tin về yêu cầu - thông thường nhất, nó được sử dụng để có được những
Content-Type, vì vậy mà cơ thể có thể được phân tích một cách chính xác.
Các kiểu trả về của phương pháp này là một Accumulator. Một ắc quy là một lớp
mỏng xung quanh một Akka Streams Sink. Một ắc quy không đồng bộ tích lũy suối
của các yếu tố vào một kết quả, nó có thể chạy bằng cách đi qua trong một Akka
Streams Nguồn, điều này sẽ trả về một CompletionStage đó sẽ được hoàn trả lại
khi ắc hoàn tất. Nó chủ yếu là điều tương tự như một Chìm>, Trên thực tế nó là gì
khác hơn một bọc xung quanh loại này, nhưng sự khác biệt lớn là Accumulator
cung cấp các phương tiện như bản đồ, mapFuture, phục hồi, vv để làm việc với kết
quả như thể nó là một lời hứa, nơi Chìm yêu cầu tất cả các hoạt động đó được bao
bọc trong một cuộc gọi mapMaterializedValue.
các ắc rằng áp dụng trở về phương pháp tiêu thụ các yếu tố của loại ByteString -
đây là những yếu mảng byte, nhưng khác nhau từ byte [] trong ByteString đó là
35
không thay đổi, và nhiều hoạt động như cắt và phụ thêm xảy ra trong thời gian liên
tục.
các kiểu trả về của ắc quy là F.Either. Điều này nói hoặc là nó sẽ trả về một kết
quả, hoặc nó sẽ trở lại một cơ thể của loại A. Một kết quả thường được trả lại trong
trường hợp có lỗi, ví dụ, nếu cơ thể không được phân tích, nếu Content-Type
không phù hợp với các loại mà các phân tích cú pháp cơ thể chấp nhận, hoặc nếu
một trong bộ nhớ đệm được vượt quá. Khi phân tích cơ thể trả về một kết quả,
điều này sẽ làm chập mạch các xử lý của hành động - các bộ phân tích cơ thể cho
kết quả sẽ được trả lại ngay lập tức, và hành động sẽ không bao giờ được gọi.
§Composing an existing body parser
As a first example, we’ll show how to compose an existing body parser. Let’s say
you want to parse some incoming JSON into a class that you have defined, called
Item.
First we’ll define a new body parser that depends on the JSON body parser:
-Soạn một trình phân tích body hiện tại
Ví dụ đầu tiên, chúng ta sẽ thấy làm thế nào để soạn một trình phân tích body hiện
tại. Hãy nói rằng bạn muốn phân tích một số JSON đến vào một lớp học mà bạn
đã xác định, gọi là Item.
Đầu tiên chúng ta sẽ xác định một bộ phân tích body mới mà phụ thuộc
public static class UserBodyParser implements BodyParser<User> {
private BodyParser.Json jsonParser;
private Executor executor;
@Inject
public UserBodyParser(BodyParser.Json jsonParser, Executor
executor) {
this.jsonParser = jsonParser;
this.executor = executor;
}
36
Now, in our implementation of the apply method, we’ll invoke the JSON body
parser, which will give us back the Accumulator<ByteString, F.Either<Result,
JsonNode>> to consume the body. We can then map that like a promise, to convert
the parsed JsonNode body to a User body. If the conversion fails, we return a Left
of a Result saying what the error was:
- Bây giờ, trong việc thực hiện của chúng ta về áp dụng phương pháp, chúng tôi sẽ
gọi bộ phân tích cơ thể JSON, mà sẽ cung cấp cho chúng ta trở về Accumulator>
Để tiêu thụ trong cơ thể. Sau đó chúng tôi có thể bản đồ đó như một lời hứa, để
chuyển đổi body JsonNode phân tích cú pháp cho một cơ quan tài. Nếu chuyển đổi
thất bại, chúng tôi trở lại một trái của một quả nói những gì các lỗi là:
public Accumulator<ByteString, F.Either<Result, User>>
apply(RequestHeader request) {
Accumulator<ByteString, F.Either<Result, JsonNode>>
jsonAccumulator = jsonParser.apply(request);
return jsonAccumulator.map(resultOrJson -> {
if (resultOrJson.left.isPresent()) {
return F.Either.Left(resultOrJson.left.get());
} else {
JsonNode json = resultOrJson.right.get();
try {
User user = play.libs.Json.fromJson(json, User.class);
return F.Either.Right(user);
} catch (Exception e) {
return F.Either.Left(Results.badRequest(
"Unable to read User from json: " +
e.getMessage()));
}
}
}, executor);
}
The returned body will be wrapped in a RequestBody, and can be accessed using
the as method:
37
- Body trả lại sẽ được bọc trong một RequestBody, và có thể được truy cập bằng
cách sử dụng như là phương pháp
@BodyParser.Of(UserBodyParser.class)
public Result save() {
RequestBody body = request().body();
User user = body.as(User.class);
return ok("Got: " + user.name);
}
§Writing a custom max length body parser
Another use case may be to define a body parser that uses a custom maximum
length for buffering. Many of the built in Play body parsers are designed to be
extended to allow overriding the buffer length in this way, for example, this is how
the text body parser can be extended:
- Viết một tùy chỉnh chiều dài body phân tích cú pháp
Một trường hợp sử dụng có thể xác định một phân tích cú pháp body mà sử dụng
một chiều dài tối đa tùy chỉnh cho đệm. Nhiều người trong số được xây dựng trong
bộ phân tích body Play được thiết kế để được mở rộng để cho phép ghi đè chiều
dài đệm theo cách này, ví dụ, đây là cách phân tích cú pháp cơ bản có thể được
mở rộng
// Accept only 10KB of data.
public static class Text10Kb extends BodyParser.Text {
@Inject
public Text10Kb(HttpErrorHandler errorHandler) {
super(10 * 1024, errorHandler);
}
}
@BodyParser.Of(Text10Kb.class)
public Result index() {
return ok("Got body: " + request().body().asText());
}
38
§Directing the body elsewhere
So far we’ve shown extending and composing the existing body parsers.
Sometimes you may not actually want to parse the body, you simply want to
forward it elsewhere. For example, if you want to upload the request body to
another service, you could do this by defining a custom body parser:
- Điều khiển các body ở nơi khác
đến nay chúng tôi đã chứng minh được mở rộng và sáng tác các bộ phân tích
body hiện tại. Đôi khi bạn có thể không thực sự muốn phân tích body, bạn chỉ đơn
giản là muốn chuyển tiếp nó ở nơi khác. Ví dụ, nếu bạn muốn tải lên body yêu cầu
dịch vụ khác, bạn có thể làm điều này bằng cách định nghĩa một bộ phân tích cơ
thể tùy chỉnh:
public static class ForwardingBodyParser implements
BodyParser<WSResponse> {
private WSClient ws;
private Executor executor;
@Inject
public ForwardingBodyParser(WSClient ws, Executor executor) {
this.ws = ws;
this.executor = executor;
}
String url = "http://example.com";
public Accumulator<ByteString, F.Either<Result, WSResponse>>
apply(RequestHeader request) {
Accumulator<ByteString, Source<ByteString, ?>> forwarder =
Accumulator.source();
return forwarder.mapFuture(source -> {
// TODO: when streaming upload has been implemented, pass
the source as the body
return ws.url(url)
39
.setMethod("POST")
// .setBody(source)
.execute().thenApply(F.Either::Right);
}, executor);
}
}
§Custom parsing using Akka Streams
In rare circumstances, it may be necessary to write a custom parser using Akka
Streams. In most cases it will suffice to buffer the body in a ByteString first, by
composing the Bytes parser as described above, this will typically offer a far
simpler way of parsing since you can use imperative methods and random access
on the body.
However, when that’s not feasible, for example when the body you need to parse is
too long to fit in memory, then you may need to write a custom body parser.
A full description of how to use Akka Streams is beyond the scope of this
documentation - the best place to start is to read the Akka Streams documentation.
However, the following shows a CSV parser, which builds on the Parsing lines from
a stream of ByteStrings documentation from the Akka Streams cookbook:
-Tuỳ chỉnh phân tích sử dụng Akka Streams
Trong trường hợp hiếm hoi, nó body là cần thiết để viết một phân tích cú pháp tùy
chỉnh sử dụng Akka Streams. Trong hầu hết các trường hợp, nó sẽ đủ để đệm cơ
thể trong một ByteString đầu tiên, bằng cách soạn các Bytes phân tích cú pháp
như đã mô tả ở trên, điều này thường sẽ cung cấp một cách đơn giản hơn của
phân tích kể từ khi bạn có thể sử dụng phương pháp bắt buộc và truy cập ngẫu
nhiên trên body.
Tuy nhiên, khi . đó là không khả thi, ví dụ như khi body bạn cần phải phân tích là
quá dài để phù hợp trong bộ nhớ, sau đó bạn có thể cần phải viết một bộ phân tích
body tùy chỉnh
mô tả đầy đủ về cách sử dụng Akka Streams là vượt ra ngoài phạm vi của tài liệu
này - nơi tốt nhất để bắt đầu là để đọc tài liệu Akka Streams. Tuy nhiên, sau đây
40
cho thấy một phân tích cú pháp CSV, xây dựng trên các dòng phân tích cú pháp từ
một dòng của tài liệu ByteStrings từ sách dạy nấu ăn Akka Streams:
public static class CsvBodyParser implements
BodyParser<List<List<String>>> {
private Executor executor;
@Inject
public CsvBodyParser(Executor executor) {
this.executor = executor;
}
@Override
public Accumulator<ByteString, F.Either<Result,
List<List<String>>>> apply(RequestHeader request) {
// A flow that splits the stream into CSV lines
Sink<ByteString, CompletionStage<List<List<String>>>> sink =
Flow.<ByteString>create()
// We split by the new line character, allowing a maximum
of 1000 characters per line
.via(Framing.delimiter(ByteString.fromString("n"), 1000,
FramingTruncation.ALLOW))
// Turn each line to a String and split it by commas
.map(bytes -> {
String[] values =
bytes.utf8String().trim().split(",");
return Arrays.asList(values);
})
// Now we fold it into a list
.toMat(Sink.<List<List<String>>, List<String>>fold(
new ArrayList<>(), (list, values) -> {
list.add(values);
return list;
}), Keep.right());
// Convert the body to a Right either
41
return Accumulator.fromSink(sink).map(F.Either::Right,
executor);
}
}
Action composition
This chapter introduces several ways to define generic action functionality.
-Thành phần hành động
Chương này giới thiệu một số cách để xác định chức năng hành động chung.
§Reminder about actions
Previously, we said that an action is a Java method that returns a play.mvc.Result
value. Actually, Play manages internally actions as functions. Because Java
doesn’t yet support first class functions, an action provided by the Java API is an
instance of play.mvc.Action:
- Reminder hành động
Trước đây, chúng tôi đã nói rằng một hành động là một phương thức Java mà
trả về một giá trị play.mvc.Result. Trên thực tế, Play quản lý trong nội bộ các
hành động như chức năng. Bởi vì Java chưa hỗ trợ chức năng hạng nhất, một
hành động được cung cấp bởi các API Java là một thể hiện của play.mvc.Action:
public abstract class Action {
public abstract CompletionStage<Result> call(Context ctx) throws
Throwable;
}
Play builds a root action for you that just calls the proper action method. This allows
for more complicated action composition.
- Play xây dựng một hành động gốc cho bạn mà chỉ cần gọi phương thức hành
động thích hợp. Điều này cho phép phần hành động phức tạp hơn.
42
§Composing actions
Here is the definition of the VerboseAction:
-Dưới đây là định nghĩa của VerboseAction:
public class VerboseAction extends play.mvc.Action.Simple {
public CompletionStage<Result> call(Http.Context ctx) {
Logger.info("Calling action for {}", ctx);
return delegate.call(ctx);
}
}
You can compose the code provided by the action method with another
play.mvc.Action, using the @With annotation:
- Bạn có thể soạn mã được cung cấp bởi các phương pháp hành động với
play.mvc.Action khác, sử dụng các chú thích @With:
@With(VerboseAction.class)
public Result verboseIndex() {
return ok("It works!");
}
At one point you need to delegate to the wrapped action using
delegate.call(...).
You also mix several actions by using custom action annotations:
- Tại một thời điểm bạn cần phải uỷ thác cho các hành động được bao bọc bằng
delegate.call (...).
Bạn cũng có trộn một vài hành động bằng cách sử dụng các chú thích hành
động tùy chỉnh:
@Security.Authenticated
@Cached(key = "index.result")
public Result authenticatedCachedIndex() {
return ok("It works!");
43
}
Note: play.mvc.Security.Authenticated and play.cache.Cached annotations
and the corresponding predefined Actions are shipped with Play. See the relevant
API documentation for more information.
Note: Every request must be served by a distinct instance of your
play.mvc.Action. If a singleton pattern is used, requests will be routed incorrectly
during multiple request scenarios. For example, if you are using Spring as a DI
container for Play, you need to make sure that your action beans are prototype
scoped.
- Lưu ý: play.mvc.Security.Authenticated và chú thích play.cache.Cached và
nhanh được xác định trước tương ứng được phát triển với Play. Xem tài liệu API
có liên quan để biết thêm thông tin.
Lưu ý: Mỗi yêu cầu phải được phục vụ bởi một trường hợp riêng biệt của
play.mvc.Action của bạn.Nếu một mẫu singleton được sử dụng, yêu cầu sẽ được
định tuyến không đúng trong nhiều kịch bản yêu cầu. Ví dụ, nếu bạn đang sử
dụng Spring như là một container DI cho Play, bạn cần phải chắc chắn rằng đậu
hành động của bạn là nguyên mẫu scoped.
§Defining custom action annotations
You can also mark action composition with your own annotation, which must itself
be annotated using @With:
-Xác định hành động tùy chỉnh chú thích
Bạn cũng có thể đánh dấu thành phần hành động với chú thích của riêng bạn,
mà chính nó phải được chú thích bằng @With:
@With(VerboseAnnotationAction.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface VerboseAnnotation {
boolean value() default true;
}
44
Your Action definition retrieves the annotation as configuration:
- Định nghĩa hành động của bạn lấy các chú thích như cấu hình:
public class VerboseAnnotationAction extends Action<VerboseAnnotation>
{
public CompletionStage<Result> call(Http.Context ctx) {
if (configuration.value()) {
Logger.info("Calling action for {}", ctx);
}
return delegate.call(ctx);
}
}
You can then use your new annotation with an action method:
- Sau đó bạn có thể sử dụng chú thích mới của bạn với một phương pháp hành
động:
@VerboseAnnotation(false)
public Result verboseAnnotationIndex() {
return ok("It works!");
}
§Annotating controllers
You can also put any action composition annotation directly on the Controller
class. In this case it will be applied to all action methods defined by this controller.
-Việc chú thích các bộ điều khiển
Bạn cũng có thể đặt bất kỳ chú thích phần hành động trực tiếp trên lớp điều
khiển. Trong trường hợp này, nó sẽ được áp dụng cho tất cả các phương pháp
hành động được xác định bởi bộ điều khiển này.
@Security.Authenticated
public class Admin extends Controller {
...
45
}
Note: If you want the action composition annotation(s) put on a Controller class
to be executed before the one(s) put on action methods set
play.http.actionComposition.controllerAnnotationsFirst = true in
application.conf. However, be aware that if you use a third party module in your
project it may rely on a certain execution order of its annotations.
- Lưu ý: Nếu bạn muốn chú thích phần hành động (s) đặt trên một lớp điều
khiển được thực hiện trước khi một (s) đặt trên phương pháp hành động thiết
play.http.actionComposition.controllerAnnotationsFirst = true trong
application.conf. Tuy nhiên, lưu ý rằng nếu bạn sử dụng một mô-đun của bên
thứ ba trong dự án của bạn có thể dựa vào một trật tự thực hiện một số chú
thích của nó.
§Passing objects from action to controller
You can pass an object from an action to a controller by utilizing the context args
map.
-Đi qua các đối tượng từ hành động để điều khiển
Bạn có thể vượt qua một đối tượng từ một hành động để một bộ điều khiển
bằng cách sử dụng bản đồ args ngữ cảnh.
public class PassArgAction extends play.mvc.Action.Simple {
public CompletionStage<Result> call(Http.Context ctx) {
ctx.args.put("user", User.findById(1234));
return delegate.call(ctx);
}
}
Then in an action you can get the arg like this:
@With(PassArgAction.class)
public static Result passArgIndex() {
Object user = ctx().args.get("user");
return ok(Json.toJson(user));
46
Intercepting HTTP requests
Play’s Java APIs provide two ways of intercepting action calls. The first is called
ActionCreator, which provides a createAction method that is used to create the
initial action used in action composition. It handles calling the actual method for
your action, which allows you to intercept requests.
The second way is to implement your own HttpRequestHandler, which is the
primary entry point for all HTTP requests in Play. This includes requests from both
Java and Scala actions.
-Chặn các yêu cầu HTTP
API Java Play cung cấp hai cách để chặn các cuộc gọi hành động. Việc đầu tiên
được gọi ActionCreator, cung cấp một phương pháp createAction được sử dụng
để tạo ra các hành động ban đầu được sử dụng trong phần hành động. Nó xử lý
gọi là phương pháp thực tế cho hành động của bạn, cho phép bạn chặn yêu
cầu.
Cách thứ hai là thực hiện HttpRequestHandler riêng của bạn, đó là điểm vào
chính cho tất cả các yêu cầu HTTP trong Play. Điều này bao gồm các yêu cầu từ
cả hai hành động Java và Scala.
§Action creators
The ActionCreator interface has two methods that can be implemented:
 createAction: Takes the request and the controller’s action method
associated with the passed request. The action can either be the first or the
last action depending on the configuration setting
play.http.actionComposition.executeActionCreatorActionFirst.
 wrapAction: Takes the action to be run and allows for a final global
interceptor to be added to the action. This method is deprecated since the
same can be achieved using createAction and the above setting.
47
There is also a DefaultActionCreator interface you can extend with default
implementations.
Note: If you are implementing a custom ActionCreator because you need to apply
a cross cutting concern to an action before it is executed, creating a filter is a more
idiomatic way of achieving the same.
A custom action creator can be supplied by creating a class in the root package
called ActionCreator that implements play.http.ActionCreator, for example:
-Sáng tạo hành động
Giao diện ActionCreator có hai phương pháp có thể được thực hiện:
• createAction: Đưa các yêu cầu và phương pháp hành động của bộ điều khiển
kết hợp với các yêu cầu thông qua. Các hành động hoặc có thể là người đầu
tiên hoặc hành động cuối cùng phụ thuộc vào các thiết lập cấu hình
play.http.actionComposition.executeActionCreatorActionFirst.
• wrapAction: Đưa các hành động được chạy và cho phép một máy bay đánh
chặn toàn cầu cuối cùng sẽ được thêm vào hành động. Phương pháp này không
được chấp kể từ khi cùng có thể đạt được bằng cách sử createAction và các
thiết lập ở trên.
Ngoài ra còn có một giao diện DefaultActionCreator bạn có thể mở rộng với các
cài đặt mặc định.
Lưu ý: Nếu bạn đang thực hiện một ActionCreator tùy chỉnh bởi vì bạn cần phải
áp dụng một mối quan tâm xuyên suốt một hành động trước khi nó được thực
thi, tạo ra một bộ lọc là một cách nhiều thành ngữ để đạt được như vậy.
một người tạo hành động tùy chỉnh có thể được cung cấp bằng cách tạo ra một
lớp trong gói gốc gọi là ActionCreator mà thực hiện play.http.ActionCreator, ví
dụ:
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Result;
import java.util.concurrent.CompletionStage;
import java.lang.reflect.Method;
public class ActionCreator implements play.http.ActionCreator {
48
@Override
public Action createAction(Http.Request request, Method
actionMethod) {
return new Action.Simple() {
@Override
public CompletionStage<Result> call(Http.Context ctx) {
return delegate.call(ctx);
}
};
}
}
If you don’t want to place this class in the root package, or if you want to be able to
configure different action handlers for different environments, you can do this by
configuring the play.http.actionCreator configuration property in
application.conf:
- Nếu bạn không muốn đặt lớp này trong gói gốc, hoặc nếu bạn muốn để có thể
cấu hình xử lý hành động khác nhau cho các môi trường khác nhau, bạn có thể
làm điều này bằng cách cấu hình các thuộc tính cấu hình
play.http.actionCreator trong application.conf:
play.http.actionCreator = "com.example.MyActionCreator"
Note: If you are also using action composition then the action returned by the
createAction method is executed after the action composition ones by default. If
you want to change this order set
play.http.actionComposition.executeActionCreatorActionFirst = true in
application.conf.
- Lưu ý: Nếu bạn cũng đang sử dụng phần hành động thì một hành động trả về
bởi phương pháp createAction được thực hiện sau khi những thành phần hoạt
động theo mặc định. Nếu bạn muốn thay đổi thứ tự này thiết
play.http.actionComposition.executeActionCreatorActionFirst = true trong
application.conf.
49
§HTTP request handlers
Sometimes an application will have more advanced needs that aren’t met by Play’s
abstractions. When this is the case, applications can provide custom
implementations of Play’s lowest level HTTP pipeline API, the
HttpRequestHandler.
Providing a custom HttpRequestHandler should be a last course of action. Most
custom needs can be met through implementing a custom router or a filter.
§Implementing a custom request handler
The HttpRequestHandler interface has one method to be implemented,
handlerForRequest. This takes the request to get a handler for, and returns a
HandlerForRequest instance containing a RequestHeader and a Handler.
The reason why a request header is returned is so that information, such as routing
information, can be added to the request. In this way, the router is able to tag
requests with routing information, such as which route matched the request, which
can be useful for monitoring or even for injecting cross cutting functionality.
A very simple request handler that simply delegates to a router might look like this:
-Yêu cầu HTTP xử lý
Đôi khi một ứng dụng sẽ có nhu cầu cao cấp hơn mà không được đáp ứng bằng
cách trừu tượng Play.Khi điều này là trường hợp, các ứng dụng có thể cung cấp
triển khai thực hiện tùy chỉnh của API đường ống cấp độ HTTP thấp nhất Play,
các HttpRequestHandler.
Cung cấp một tùy chỉnh HttpRequestHandler nên là một khóa học cuối cùng của
hành động. Hầu hết các nhu cầu tùy chỉnh có thể được đáp ứng thông qua việc
thực hiện một bộ định tuyến tùy chỉnh hoặc một bộ lọc.
§Implementing một xử lý yêu cầu tùy chỉnh
giao diện HttpRequestHandler đã có một phương pháp được thực hiện,
handlerForRequest. Này có các yêu cầu để có được một xử lý cho, và trả về một
thể HandlerForRequest chứa một RequestHeader và một Handler.
Lý do tại sao một tiêu đề yêu cầu được trả về là để thông tin, chẳng hạn như
thông tin định tuyến, có thể được thêm vào theo yêu cầu. Bằng cách này, các
router có thể gắn thẻ yêu cầu với thông tin định tuyến, chẳng hạn như những
50
tuyến đường phù hợp theo yêu cầu, có thể hữu ích để theo dõi hay thậm chí
chích năng cắt chéo.
Một xử lý yêu cầu rất đơn giản mà chỉ đơn giản là đại biểu cho một bộ định
tuyến có thể trông Như thế này:
import javax.inject.Inject;
import play.routing.Router;
import play.api.mvc.Handler;
import play.http.*;
import play.mvc.*;
import play.libs.streams.Accumulator;
public class SimpleHttpRequestHandler implements HttpRequestHandler {
private final Router router;
@Inject
public SimpleHttpRequestHandler(Router router) {
this.router = router;
}
public HandlerForRequest handlerForRequest(Http.RequestHeader
request) {
Handler handler = router.route(request).orElseGet(() ->
EssentialAction.of(req ->
Accumulator.done(Results.notFound()))
);
return new HandlerForRequest(request, handler);
}
}
Note that HttpRequestHandler currently has two legacy methods with default
implementations that have since been moved to ActionCreator.
§Configuring the http request handler
A custom http handler can be supplied by creating a class in the root package
called RequestHandler that implements HttpRequestHandler.
51
If you don’t want to place your request handler in the root package, or if you want to
be able to configure different request handlers for different environments, you can
do this by configuring the play.http.requestHandler configuration property in
application.conf:
play.http.requestHandler = "com.example.RequestHandler"
Content negotiation
Content negotiation is a mechanism that makes it possible to serve different
representation of a same resource (URI). It is useful e.g. for writing Web Services
supporting several output formats (XML, JSON, etc.). Server-driven negotiation is
essentially performed using the Accept* requests headers. You can find more
information on content negotiation in the HTTP specification.
§Language
You can get the list of acceptable languages for a request using the
play.mvc.Http.RequestHeader#acceptLanguages method that retrieves them from
the Accept-Language header and sorts them according to their quality value. Play
uses it to set the lang value of request’s HTTP context, so they automatically use
the best possible language (if supported by your application, otherwise your
application’s default language is used).
§Content
Similarly, the play.mvc.Http.RequestHeader#acceptedTypes method gives the list
of acceptable result’s MIME types for a request. It retrieves them from the Accept
request header and sorts them according to their quality factor.
You can test if a given MIME type is acceptable for the current request using the
play.mvc.Http.RequestHeader#accepts method:
-Nội dung thảo luận
nội dung thảo luận là một cơ chế mà làm cho nó có thể để phục vụ đại diện
52
khác nhau của một tài nguyên tương tự (URI). Đây là ví dụ hữu ích cho việc
viết Dịch vụ web hỗ trợ nhiều định dạng đầu ra (XML, JSON, vv). Thương lượng
máy chủ theo định hướng chủ yếu được thực hiện bằng cách sử dụng Chấp
nhận * yêu cầu tiêu đề. Bạn có thể tìm thêm thông tin về đàm phán nội dung
trong đặc tả HTTP.
§Language
Bạn có thể nhận được danh sách các ngôn ngữ được chấp nhận cho một yêu
cầu bằng cách sử dụng phương pháp play.mvc.Http.RequestHeader #
acceptLanguages để lấy chúng từ tiêu đề Accept-Language và sắp xếp chúng
theo giá trị chất lượng của họ. Chơi sử dụng nó để thiết lập giá trị lang của bối
cảnh HTTP yêu cầu, vì vậy họ sẽ tự động sử dụng ngôn ngữ tốt nhất có thể
(nếu được hỗ trợ bởi ứng dụng của bạn, nếu ngôn ngữ mặc định của ứng dụng
của bạn đang sử dụng).
§Content
Tương tự như vậy, các play.mvc.Http.RequestHeader # phương pháp
acceptedTypes cho danh sách các kiểu MIME kết quả chấp nhận được đối với
một yêu cầu. Nó lấy chúng từ Chấp nhận yêu cầu tiêu đề và sắp xếp chúng
theo yếu tố chất lượng của họ.
Bạn có thể kiểm tra nếu một loại MIME cho là chấp nhận được đối với các yêu
cầu hiện tại bằng cách sử dụng play.mvc.Http.RequestHeader # chấp nhận
phương pháp:
public Result list() {
List<Item> items = Item.find.all();
if (request().accepts("text/html")) {
return ok(views.html.Application.list.render(items));
} else {
return ok(Json.toJson(items));
}
}
53
III. Asynchronous HTTP
programming
Handling asynchronous results
(Xử lí kết quả không đồng bồ )
Make controllers asynchronous
(Điếu khiến không đồng bồ)
Internally, Play Framework is asynchronous from the bottom up. Play handles every request
in an asynchronous, non-blocking way.
(Nồi tải của Play Framework không đồng bồ tử dửới lên , Play xử lí mồi yêu cảu không đồng
bồ ....)
The default configuration is tuned for asynchronous controllers. In other words, the
application code should avoid blocking in controllers,
i.e., having the controller code wait for an operation. Common examples of such blocking
operations are JDBC calls,
streaming API, HTTP requests and long computations.
(Cảu hình mảc định là điếu hửớng cho điếu khiến không đồng bồ .Nói cách khác , mã của
bản nên tránh bị chản trong controllers,
streaming AOI , HTTP và sử tính toàn dài hản )
Although it’s possible to increase the number of threads in the default execution context to
allow more concurrent requests to be processed by blocking controllers,
following the recommended approach of keeping the controllers asynchronous makes it easier
to scale and to keep the system responsive under load.
(Mảc dù nó có thế tăng thêm thread trong ngử cảnh mảc định đế đế cho phép xử lí nhiếu
54
yêu cảu đồng thới hớn bảng blocking controllers,
Theo cách tiếp cản muồn đế nghị )
Creating non-blocking actions
(Tảo actions không đồng bồ)
Because of the way Play works, action code must be as fast as possible, i.e., non-blocking.
So what should we return from our action if we are not yet able to compute the result? We
should return the promise of a result!
(Bới vì làm viếc Play , các action cản xử lí càng nhanh càng tồt - non-blocking / , chúng ta
nên sử dủng 1 promise cho kết quả
Vì vảy chsung ta nên trả vế cái gì tử action của chúng ta khi không tính toán đửớc kết quả
)
Java 8 provides a generic promise API called CompletionStage. A CompletionStage<Result>
will eventually be redeemed with a value of type Result.
By using a CompletionStage<Result> instead of a normal Result, we are able to return from
our action quickly without blocking anything.
Play will then serve the result as soon as the promise is redeemed.
(Java 8 cung cảp CompletionStage ,CompletionStage<Result> cuồi sẽ đửớc lảy lải với 1 giá
trị của Result ,
bảng cách sử dủng CompletionStage<Result> thay thế cho Reult bình thửớng ., chúng ta có
thế action nhanh hớn vs block )
The web client will be blocked while waiting for the response, but nothing will be blocked on
the server, and server resources can be used to serve other clients.
(Các khách hàng sẽ bị chản khi trong thới gian chớ đới oharn hồi , nhửng không có sử chản
ớ server , và tài nguyên ớ server có thế đửớc sử dủng
cho các khách hàng khac )
How to create a CompletionStage<Result>
( làm thế nào đế cài đảt CompletionStage<Result>)
To create a CompletionStage<Result> we need another promise first: the promise that will
give us the actual value we need to compute the result:
(Đế cài tảo ra CompletionStage<Result> chúng ta cản 1 promise đảu tiên : promise này sẽ
đửa chúng ta các giá trị thảt đế tính toán kết quả )
55
CompletionStage<Double> promiseOfPIValue = computePIAsynchronously();
CompletionStage<Result> promiseOfResult = promiseOfPIValue.thenApply(pi ->
ok("PI value computed: " + pi)
);
Play asynchronous API methods give you a CompletionStage. This is the case when you are
calling an external web service using the play.libs.WS API,
or if you are using Akka to schedule asynchronous tasks or to communicate with Actors using
play.libs.Akka.
(thuồc tính API không đồng bồ của Play cung caaso 1 CompletionStage , đây là trửớng hớp
khi bản gồi đến dịch vủ web ngoài bảng cách sử dủng play .libs.WS API,
hoảc nếu bản sử dủng Akka đế tới các task không đồng bồ hoảc đế giao tiếp vs Actor sử dủng
play.libs.Akka)
A simple way to execute a block of code asynchronously and to get a CompletionStage is to
use the CompletableFuture.supplyAsync() helper:
(Cách đớn giản đế thửc hiến 1 khồi không đồng bồ và lảy CompletionStage là sử dủng
CompletableFuture.supplyAsync( )) :
CompletionStage<Integer> promiseOfInt = CompletableFuture.supplyAsync(() ->
intensiveComputation());
Note: It’s important to understand which thread code runs on which promises. Here, the
intensive computation will just be run on another thread.
(Lửu ý : nó rât quan trồng đế hiếu vế luồng , ớ đây viếc tính toán sẽ chỉ chỉ xảy ra trên 1
luồng)
You can’t magically turn synchronous IO into asynchronous by wrapping it in a
CompletionStage.
If you can’t change the application’s architecture to avoid blocking operations, at some point
that operation will have to be executed,
and that thread is going to block. So in addition to enclosing the operation in a
CompletionStage,
it’s necessary to configure it to run in a separate execution context that has been configured
with enough threads to deal with the expected concurrency.
See Understanding Play thread pools for more information.
56
(Bản không thế đửa 1 đồng bồ IO(vào ra) vào trong không đồng bồ bảng cách gói nó trong
CompletionState .
Nếu bản không thế thay dồi kiến trúc đế tránh khóa viếc tính toán , 1 vài điếm tính toán đó
sẽ thửc hiến và luồng sẽ bị chản ,
It can also be helpful to use Actors for blocking operations. Actors provide a clean model for
handling timeouts and failures,
setting up blocking execution contexts, and managing any state that may be associated with
the service.
Also Actors provide patterns like ScatterGatherFirstCompletedRouter to address
simultaneous cache and database requests and allow remote execution on a
cluster of backend servers. But an Actor may be overkill depending on what you need.
Async results
We have been returning Result up until now. To send an asynchronous result our action needs
to return a CompletionStage<Result>:
public CompletionStage<Result> index() {
return CompletableFuture.supplyAsync(() -> intensiveComputation())
.thenApply(i -> ok("Got result: " + i));
}
Actions are asynchronous by default
Play actions are asynchronous by default. For instance, in the controller code below, the
returned Result is internally enclosed in a promise:
public Result index() {
return ok("Got request " + request() + "!");
}
Note: Whether the action code returns a Result or a CompletionStage<Result>, both kinds of
returned object are handled internally in the same way.
There is a single kind of Action, which is asynchronous, and not two kinds (a synchronous
one and an asynchronous one).
Returning a CompletionStage is a technique for writing non-blocking code.
57
IV. The Twirl template engine
The template engine
Overview
A Play Scala template is a simple text file that contains small blocks of Scala code.
Templates can generate any text-based format, such as HTML, XML or CSV.
Template play scala là một file text mà chứa code Scala. Template có thể tự sinh ra
bất kỳ định dạng như html, xml hoặc csc.
The template system has been designed to feel comfortable to those used to working
with HTML, allowing front-end developers to easily work with the templates.
Hệ thống template này là được thiết kế sao cho dễ dàng làm việc với html, cho phép
người phát triển front-end dễ dàng làm việc với template.
Templates are compiled as standard Scala functions, following a simple naming
convention. If you create a views/Application/index.scala.html template
file, it will generate a views.html.Application.index class that has
a render() method.
Template là được biên dịch theo chuẩn của Scala, cùng với qui ước tên. Nếu như
bạn tạo file views/Application/index.scala.html, nó sẽ sinh ra class
views.html.Application.index cái mà có phương thức render().
For example, here is a simple template:
Ví dụ:
@(customer: Customer, orders: List[Order])
<h1>Welcome @customer.name!</h1>
<ul>
58
@for(order <- orders) {
<li>@order.getTitle()</li>
}
</ul>
You can then call this from any Java code as you would normally call a method on a
class:
Bạn có thể sau đó gọi nó như java gọi phương thức trên một class
Content html = views.html.Application.index.render(customer, orders);
Syntax: the magic ‘@’
character
The Scala template uses @ as the single special character. Every time this character
is encountered, it indicates the beginning of a dynamic statement. You are not
required to explicitly close the code block - the end of the dynamic statement will be
inferred from your code:
Scala template sử dụng @ như là một ký tự đơn đặc biệt. Ký tự này rất hay gặp và
nó chỉ cho ta biết bắt đầu một dynamic statement. Bạn không cần phải viết ký tự kết
thúc.
Hello @customer.getName()!
^^^^^^^^^^^^^^^^^^
Dynamic code
Because the template engine automatically detects the end of your code block by
analysing your code, this syntax only supports simple statements. If you want to
insert a multi-token statement, explicitly mark it using brackets:
Bởi vì template tự động phát hiện kết thúc của khối lệnh bằng cách phân tích code
của bạn, cú pháp này chỉ hỗ trợ cho một statement, nếu muốn chèn nhiều statement,
sử dụng với () hoặc {} để bao khối lệnh
Hello @(customer.getFirstName() + customer.getLastName())!
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Dynamic Code
59
Note: Make sure not to include whitespaces between keywords of dynamic statements and
parentheses.
For example, the following code doesn’t work:
Chú ý là không có ký tự trắng giữa từ khóa của dynamic content và ngoặc.
Ví dụ bên dưới là không làm việc.
@for (menu <- menuList) { ... } // Compilation error: '(' expected but ')' found.
^
You can also use curly brackets, to write a multi-statement block:
Hello @{val name = customer.getFirstName() + customer.getLastName(); name}!
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Dynamic Code
Because @ is a special character, you’ll sometimes need to escape it. Do this by
using@@:
My email is bob@@example.com
Template parameters
A template is like a function, so it needs parameters, which must be declared at the
top of the template file:
Template giống như một hàm, do đó nó cần có biến, cái mà phải được định nghĩa ở
trên cùng của template file.
@(customer: models.Customer, orders: List[models.Order])
You can also use default values for parameters:
Cũng có thể sử dụng giá trị mặc định cho biến.
@(title: String = "Home")
Or even several parameter groups:
Hoặc thậm chí là nhóm các biến.
60
@(title:String)(body: Html)
Template constructor
By default, a template is generated as a static function that can be invoked in any
context. If your template has dependencies on components, such as the messages
API, you may find it easier to inject it with the components (and other templates) that
it needs, and then you can inject that template into the controllers that use it.
Mặc định, template là được sinh ra như là một hàm static cái mà có thể gọi ở bất kỳ
context. Nếu teamplate có sự phụ thuộc trên các thành phần, như là messages API,
bạn có thể tìm thấy dễ dàng để inject các thành phần hoặc template khác, sau đó có
thể inject template vào controllers để sử dụng.
Twirl supports declaring a constructor for templates, using @this() syntax at the
start of the file, before the template parameters. The arguments to the constructor
can be defined in the same way as the template parameters:
Twirl hỗ trợ định nghĩa hàm khởi tạo cho template, sử dụng @this() để bắt đầu file,
trước các khai báo biến.
@this(messagesApi: MessagesApi)
@(customer: models.Customer, orders: List[models.Order])
Iterating
You can use the for keyword, in a pretty standard way:
Sử dụng từ khóa for theo như bên dưới để duyệt phần tử.
<ul>
@for(p <- products) {
<li>@p.getName() ($@p.getPrice())</li>
}
</ul>
Note: Make sure that { is on the same line with for to indicate that the expression continues
to next line.
Chú ý rằng { là cùng dòng với for.
61
If-blocks
If-blocks are nothing special. Simply use Scala’s standard if statement:
Khối lệnh if thì không có gì đặc biệt, chỉ đơn giản là sử dụng if của scala như bên
dưới:
@if(items.isEmpty()) {
<h1>Nothing to display</h1>
} else {
<h1>@items.size() items!</h1>
}
Declaring reusable blocks
You can create reusable code blocks:
Bạn có thể tạo tái sử dụng khối lệnh.
@display(product: models.Product) = {
@product.getName() ($@product.getPrice())
}
<ul>
@for(product <- products) {
@display(product)
}
</ul>
Note that you can also declare reusable pure code blocks:
@title(text: String) = @{
text.split(' ').map(_.capitalize).mkString(" ")
}
<h1>@title("hello world")</h1>
Note: Declaring code block this way in a template can be sometime useful but keep in mind
that a template is not the best place to write complex logic. It is often better to externalize
62
these kind of code in a Java class (that you can store under the views/package as well if
you want).
Chú ý là việc định nghĩa khối lệnh trong template có thể thỉnh thoảng có ích, nhưng nhớ rằng
template không phải là nơi tốt để viết xử lý logic phức tạp. Thông thường chúng ta tạo nó bên
ngoài như là các java class.
By convention a reusable block defined with a name starting with implicit will be
marked as implicit:
Tên cho khối lệnh tái sử dụng bắt đầu với implicit sẽ đánh dấu như là implicit:
@implicitFieldConstructor = @{ MyFieldConstructor() }
Declaring reusable values
You can define scoped values using the defining helper:
Bạn có thể định nghĩa phạm vi values sử dung defining helper:
@defining(user.getFirstName() + " " + user.getLastName()) { fullName =>
<div>Hello @fullName</div>
}
Import statements
You can import whatever you want at the beginning of your template (or sub-
template):
Bạn có thể import bất cứ nơi nào bạn muốn để bắt đầu template hoặc sub-template.
@(customer: models.Customer, orders: List[models.Order])
@import utils._
...
To make an absolute resolution, use root prefix in the import statement.
Sử dụng import tuyệt đối, sử dụng root ở đầu
@import _root_.company.product.core._
If you have common imports, which you need in all templates, you can declare
inbuild.sbt
TwirlKeys.templateImports += "org.abc.backend._"
63
Comments
You can write server side block comments in templates using @* *@:
Viết comment sử dụng @**@:
@*********************
* This is a comment *
*********************@
You can put a comment on the first line to document your template into the Scala API
doc:
@*************************************
* Home page. *
* *
* @param msg The message to display *
*************************************@
@(msg: String)
<h1>@msg</h1>
Escaping
By default, dynamic content parts are escaped according to the template type’s (e.g.
HTML or XML) rules. If you want to output a raw content fragment, wrap it in the
template content type.
For example to output raw HTML:
Bằng mặc định dynamic content là được bao bởi qui tắc của các loại template. Nếu
muốn output một content fragment bao nó bởi template content type.
<p>
@Html(article.content)
</p>
64
Common template use cases
Templates, being simple functions, can be composed in any way you want. Below
are a few examples of some common scenarios.
Một vài trường hợp sử dụng template
Layout
Let’s declare a views/main.scala.html template that will act as a main layout
template:
Định nghĩa views/main.scala.html tạo main layout.
@(title: String)(content: Html)
<!DOCTYPE html>
<html>
<head>
<title>@title</title>
</head>
<body>
<section class="content">@content</section>
</body>
</html>
As you can see, this template takes two parameters: a title and an HTML content
block. Now we can use it from
another views/Application/index.scala.html template:
Template định nghĩa 2 biến: tiêu đề và html content. Bây giờ có thể sử dụng chúng
từ:
@main(title = "Home") {
<h1>Home page</h1>
}
Note: You can use both named parameters (like @main(title = "Home") and
positional parameters, like @main("Home"). Choose whichever is clearer in a specific
65
context.
Sometimes you need a second page-specific content block for a sidebar or
breadcrumb trail, for example. You can do this with an additional parameter:
Thỉnh thoảng cần một trang thứ 2 cái mà định danh nội dung cho sidebar hoặc
breadcumb train. Ví dụ:
@(title: String)(sidebar: Html)(content: Html)
<!DOCTYPE html>
<html>
<head>
<title>@title</title>
</head>
<body>
<section class="content">@content</section>
<section class="sidebar">@sidebar</section>
</body>
</html>
Using this from our ‘index’ template, we have:
@main("Home") {
<h1>Sidebar</h1>
} {
<h1>Home page</h1>
}
Alternatively, we can declare the sidebar block separately:
@sidebar = {
<h1>Sidebar</h1>
}
@main("Home")(sidebar) {
<h1>Home page</h1>
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn
Technical note playframework_documentation_working with play - java_vn

More Related Content

Similar to Technical note playframework_documentation_working with play - java_vn

Thiết lập i pv6 trên pc
Thiết lập i pv6 trên pcThiết lập i pv6 trên pc
Thiết lập i pv6 trên pcVNG
 
Lập trình web asp.net MVC
Lập trình web asp.net MVCLập trình web asp.net MVC
Lập trình web asp.net MVCMasterCode.vn
 
Devexpress cho asp.net
Devexpress cho asp.netDevexpress cho asp.net
Devexpress cho asp.netthichxoidau
 
Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008 (...
Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008  (...Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008  (...
Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008 (...Ngô Hùng
 
Cấu hình và bảo mật cho Ubuntu Server
Cấu hình và bảo mật cho Ubuntu Server Cấu hình và bảo mật cho Ubuntu Server
Cấu hình và bảo mật cho Ubuntu Server Ho Quang Thanh
 
Lab 8 dhcp server
Lab 8 dhcp server Lab 8 dhcp server
Lab 8 dhcp server tinhban269
 
Asp.net mvc framework qua cac vi du
Asp.net mvc framework  qua cac vi duAsp.net mvc framework  qua cac vi du
Asp.net mvc framework qua cac vi duKim Hyun Hai
 
Bai2 tong quan_mvc_0567
Bai2 tong quan_mvc_0567Bai2 tong quan_mvc_0567
Bai2 tong quan_mvc_0567Ham Chơi
 
Bai1 gioi thieu_servlet_va_jsp_8952
Bai1 gioi thieu_servlet_va_jsp_8952Bai1 gioi thieu_servlet_va_jsp_8952
Bai1 gioi thieu_servlet_va_jsp_8952Ham Chơi
 
Khac nhau bridge & route cho cấu hình cisco 878 megawan
Khac nhau bridge & route cho cấu hình cisco 878 megawanKhac nhau bridge & route cho cấu hình cisco 878 megawan
Khac nhau bridge & route cho cấu hình cisco 878 megawanthanhthat1
 
Go micro framework to build microservices
Go micro framework to build microservicesGo micro framework to build microservices
Go micro framework to build microservicesTechMaster Vietnam
 

Similar to Technical note playframework_documentation_working with play - java_vn (20)

Aspnet 3.5 _04
Aspnet 3.5 _04Aspnet 3.5 _04
Aspnet 3.5 _04
 
Yii
YiiYii
Yii
 
Phalcon căn bản
Phalcon căn bảnPhalcon căn bản
Phalcon căn bản
 
Báo cáo tuần đồ án
Báo cáo tuần đồ ánBáo cáo tuần đồ án
Báo cáo tuần đồ án
 
Aspnet 3.5_03
Aspnet 3.5_03Aspnet 3.5_03
Aspnet 3.5_03
 
Thiết lập i pv6 trên pc
Thiết lập i pv6 trên pcThiết lập i pv6 trên pc
Thiết lập i pv6 trên pc
 
Lập trình web asp.net MVC
Lập trình web asp.net MVCLập trình web asp.net MVC
Lập trình web asp.net MVC
 
Laravel 5 framework
Laravel 5 frameworkLaravel 5 framework
Laravel 5 framework
 
Devexpress cho asp.net
Devexpress cho asp.netDevexpress cho asp.net
Devexpress cho asp.net
 
Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008 (...
Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008  (...Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008  (...
Athena - Nghiên cứu và triển khai các dịch vụ mạng trên windows sever 2008 (...
 
Cấu hình và bảo mật cho Ubuntu Server
Cấu hình và bảo mật cho Ubuntu Server Cấu hình và bảo mật cho Ubuntu Server
Cấu hình và bảo mật cho Ubuntu Server
 
Lab 8 dhcp server
Lab 8 dhcp server Lab 8 dhcp server
Lab 8 dhcp server
 
Lab 8 dhcp server
Lab 8 dhcp server Lab 8 dhcp server
Lab 8 dhcp server
 
Asp.net mvc framework qua cac vi du
Asp.net mvc framework  qua cac vi duAsp.net mvc framework  qua cac vi du
Asp.net mvc framework qua cac vi du
 
Dsd05 02a-xml-rpca
Dsd05 02a-xml-rpcaDsd05 02a-xml-rpca
Dsd05 02a-xml-rpca
 
Bai2 tong quan_mvc_0567
Bai2 tong quan_mvc_0567Bai2 tong quan_mvc_0567
Bai2 tong quan_mvc_0567
 
Asp control
Asp controlAsp control
Asp control
 
Bai1 gioi thieu_servlet_va_jsp_8952
Bai1 gioi thieu_servlet_va_jsp_8952Bai1 gioi thieu_servlet_va_jsp_8952
Bai1 gioi thieu_servlet_va_jsp_8952
 
Khac nhau bridge & route cho cấu hình cisco 878 megawan
Khac nhau bridge & route cho cấu hình cisco 878 megawanKhac nhau bridge & route cho cấu hình cisco 878 megawan
Khac nhau bridge & route cho cấu hình cisco 878 megawan
 
Go micro framework to build microservices
Go micro framework to build microservicesGo micro framework to build microservices
Go micro framework to build microservices
 

Technical note playframework_documentation_working with play - java_vn

  • 1. 1 Working with Play - Java Play framework Documentation 2.6.x Translation from English to Vietnamese ORG: https://www.playframework.com/documentation/2.6.x/JavaHome Translater: Dao Van Thien, Cao Sy Trung, Do Van Chung, Le Thi Lanh | Asahina Infotech Company Limited Date: 02-Aug-2016 Scope: Working with Play - Java I. Main concepts for Java .................................................................................2 II. HTTP programming ...................................................................................3 III. Asynchronous HTTP programming .........................................................52 IV. The Twirl template engine ......................................................................56 V. Form submission and validation .............................................................68 VI. Working with Json ...................................................................................91 VII. Working with XML ...................................................................................97
  • 2. 2 VIII. Handling file upload ...............................................................................100 IX. Accessing an SQL database ..................................................................102 X. Using the Cache ....................................................................................130 XI. Calling WebServices ..............................................................................136 XII. Integrating with Akka ...........................................................................160 XIII. Internationalization.................................................................................172 XIV. Dependency Injection ...........................................................................179 XV. Application Settings ...............................................................................204 XVI. Testing your application ........................................................................218 XVII. Logging ..................................................................................................253 I. Main concepts for Java This section introduces you to the most common aspects of writing a Play application in Java. You’ll learn about handling HTTP requests, sending HTTP responses, working with different types of data, using databases and much more. Đoạn này sẽ giới thiệu cho bạn những khía cạnh chung nhất về việc viết một app Play trong Java. Bạn sẽ học được về cách xử lí yêu cầu HTTP, gửi phản hồi HTTP, làm việc với các kiểu dữ liệu khác nhau, sử dụng DB và một số cái khác. Note: The Play APIs for Java and Scala are separated into different packages. All the Java APIs are under the play package; all the Scala APIs are under play.api. For example, the Java MVC API is under play.mvc and the Scala MVC API is under play.api.mvc.
  • 3. 3 Note: Các play API cho Java và Scala được chia thành các package khác nhau. Tắt cả các Java API nằm trong play package, tất cả các Scala API nằm tròng play.api. Ví dụ, Java MVC API nằm trong play.mvc, còn Scala MVC API nằm trong play.api.mvc.
  • 4. 4 II. HTTP PROGRAMMING Actions, Controllers and Results -Hoạt động, điều khiển và kết quả §What is an Action? -Một hành động là gì? Most of the requests received by a Play application are handled by an action. An action is basically a Java method that processes the request parameters, and produces a result to be sent to the client. -Hầu hết các yêu cầu nhận được bởi một ứng dụng Play được xử lý bởi một hành động. Một hành động về cơ bản là một phương pháp Java xử lý các thông số yêu cầu, và tạo ra một kết quả sẽ được gửi cho khách hàng. public Result index() { return ok("Got request " + request() + "!"); } An action returns a play.mvc.Result value, representing the HTTP response to send to the web client. In this example ok constructs a 200 OK response containing a text/plain response body. - Một hành động trả về một giá trị play.mvc.Result, đại diện cho các phản ứng HTTP để gửi cho khách hàng web. Trong ví dụ này ok xây dựng một phản ứng OK 200 chứa một text /plain. §Controllers
  • 5. 5 -Điều khiển A controller is nothing more than a class extending play.mvc.Controller that groups several action methods. - Một bộ điều khiển là không có gì nhiều hơn một lớp mở rộng play.mvc.Controller nhóm một số phương thức hành động. package controllers; import play.*; import play.mvc.*; public class Application extends Controller { public Result index() { return ok("It works!"); } } The simplest syntax for defining an action is a method with no parameters that returns a Result value, as shown above. An action method can also have parameters: -Cú pháp đơn giản để xác định một hành động là một phương pháp không có tham số trả về một giá trị kết quả, như trình bày ở trên. Một phương pháp hành động cũng có thể có các thông số: public Result index(String name) { return ok("Hello " + name); } These parameters will be resolved by the Router and will be filled with values from the request URL. The parameter values can be extracted from either the URL path
  • 6. 6 or the URL query string. - Các tham số này sẽ được giải quyết bởi các Router và sẽ được lấp đầy với các giá trị từ các URL yêu cầu. Các giá trị tham số có thể được chiết xuất từ một trong hai đường dẫn URL hoặc chuỗi truy vấn URL. §Results -Kết quả Let’s start with simple results: an HTTP result with a status code, a set of HTTP headers and a body to be sent to the web client. These results are defined by play.mvc.Result, and the play.mvc.Results class provides several helpers to produce standard HTTP results, such as the ok method we used in the previous section: - Hãy bắt đầu với kết quả đơn giản: một HTTP dẫn với mã trạng thái, một bộ các tiêu đề HTTP và một phần thân phải được gửi đến các web client. Các kết quả này được xác định bởi play.mvc.Result, và lớp play.mvc.Results cung cấp nhiều sợ giúp đỡ để tạo ra kết quả HTTP tiêu chuẩn, chẳng hạn như phương pháp ok chúng tôi được sử dụng trong phần trước: public Result index() { return ok("Hello world!"); } Here are several examples that create various results: - Dưới đây là một số ví dụ để tạo ra kết quả khác nhau: Result ok = ok("Hello world!"); Result notFound = notFound(); Result pageNotFound = notFound("<h1>Page not found</h1>").as("text/html"); Result badRequest = badRequest(views.html.form.render(formWithErrors));
  • 7. 7 Result oops = internalServerError("Oops"); Result anyStatus = status(488, "Strange response type"); All of these helpers can be found in the play.mvc.Results class. - Tất cả những phương thức hỗ trợ có thể được tìm thấy trong lớp play.mvc.Results. §Redirects are simple results too -Chuyển hướng là kết quả đơn giản Redirecting the browser to a new URL is just another kind of simple result. However, these result types don’t have a response body. There are several helpers available to create redirect results: - Chuyển hướng trình duyệt tới một URL mới chỉ là một loại kết quả đơn giản. Tuy nhiên, những kết quả không có nội dung phản hồi. Có một số phương thứ có sẵn để tạo ra kết quả chuyển hướng: public Result index() { return redirect("/user/home"); } The default is to use a 303 SEE_OTHER response type, but you can also specify a more specific status code: - Giá trị mặc định là sử dụng một loại phản hồi 303 SEE_OTHER, nhưng bạn cũng có thể chỉ định một mã trạng thái cụ thể hơn: public Result index() { return temporaryRedirect("/user/home"); }
  • 8. 8 HTTP routing -Đinhj tuyến HTTP §The built-in HTTP router The router is the component that translates each incoming HTTP request to an action call (a public method in a controller class). An HTTP request is seen as an event by the MVC framework. This event contains two major pieces of information:  the request path (such as /clients/1542, /photos/list), including the query string.  the HTTP method (GET, POST, …). Routes are defined in the conf/routes file, which is compiled. This means that you’ll see route errors directly in your browser: -Việc xây dựng trong bộ định tuyến HTTP Các router là thành phần dịch mỗi yêu cầu HTTP đến với một cuộc gọi hành động (một phương pháp nào trong một lớp điều khiển). Một yêu cầu HTTP được xem như là một sự kiện do khuôn khổ MVC. Sự kiện này bao gồm hai phần chính của thông tin: • con đường yêu cầu (ví dụ như / clients/ 1542, / photos/ list), bao gồm các chuỗi truy vấn. • phương pháp HTTP (GET, POST, ...). Các tuyến đường được quy định tại conf / routes tập tin, được biên dịch. Điều này có nghĩa rằng bạn sẽ nhìn thấy các lỗi đường trực tiếp trong trình duyệt của bạn:
  • 9. 9 §Dependency Injection Play supports generating two types of routers, one is a dependency injected router, the other is a static router. The default is the dependency injected router, and that is also the case in the Play seed Activator templates, since we recommend you use dependency-injected controllers. If you need to use static controllers you can switch to the static routes generator by adding the following configuration to your build.sbt: -Dependency Injection
  • 10. 10 Phát hỗ trợ tạo ra hai loại router, một là một phụ thuộc router, là một bộ định tuyến tĩnh. Mặc định là phụ thuộc router, và đó cũng là trường hợp trong các mẫu Activator play giống, kể từ khi chúng tôi khuyên bạn sử dụng bộ điều khiển phụ thuộc . Nếu bạn cần phải sử dụng các bộ điều khiển tĩnh bạn có thể chuyển sang bộ tạo các tuyến tĩnh bằng cách thêm các cấu hình sau đây để build.sbt của bạn:bằng cách thêm các cấu hình sau đây để build.sbt của bạn: routesGenerator := StaticRoutesGenerator The code samples in Play’s documentation assume that you are using the injected routes generator. If you are not using this, you can trivially adapt the code samples for the static routes generator, either by prefixing the controller invocation part of the route with an @ symbol, or by declaring each of your action methods as static. - Các mẫu mã trong tài liệu hướng dẫn Play cho rằng bạn đang sử dụng các bô tạo tuyến đường injected. Nếu bạn không sử dụng này, bạn có thể thích ứng trivially mẫu mã cho các bộ tạo các tuyến đường tĩnh, hoặc bằng cách đặt trước phần điều khiển gọi với một biểu tượng @, hoặc bằng cách tuyên bố mỗi phương pháp hành động của bạn là tĩnh. §The routes file syntax conf/routes is the configuration file used by the router. This file lists all of the routes needed by the application. Each route consists of an HTTP method and URI pattern associated with a call to an action method. Let’s see what a route definition looks like: -Các cú pháp tuyến đường tập tin conf / routes là file cấu hình được sử dụng bởi các bộ định tuyến. File này liệt kê tất cả các tuyến đường cần thiết cho các ứng dụng. Mỗi tuyến đường bao gồm một phương thức HTTP và mẫu URI liên kết với một cuộc gọi đến một phương pháp hành động. Hãy xem những gì một định nghĩa đường trông giống như:
  • 11. 11 GET /clients/:id controllers.Clients.show(id: Long) Note: in the action call, the parameter type comes after the parameter name, like in Scala. Each route starts with the HTTP method, followed by the URI pattern. The last element of a route is the call definition. You can also add comments to the route file, with the # character: - Lưu ý: trong các cuộc gọi hành động, các loại tham số đi kèm sau tên tham số, như trong Scala. Mỗi tuyến đường bắt đầu với các phương thức HTTP, tiếp theo là mẫu URI. Yếu tố cuối cùng của một tuyến đường là định nghĩa cuộc gọi. Bạn cũng có thể thêm ý kiến để các tập tin tuyến đường, với các ký tự #: # Display a client. GET /clients/:id controllers.Clients.show(id: Long) §The HTTP method The HTTP method can be any of the valid methods supported by HTTP (GET, PATCH, POST, PUT, DELETE, HEAD, OPTIONS). -Các phương thức HTTP phương thức HTTP có thể là bất kỳ một phương thức hỗ trợ hợp lệ bởi HTTP (GET, Patch, POST, PUT, DELETE, HEAD, OPTIONS). §The URI pattern The URI pattern defines the route’s request path. Some parts of the request path can be dynamic.
  • 12. 12 §Static path For example, to exactly match GET /clients/all incoming requests, you can define this route: -Các mẫu URI mẫu URI xác định con đường yêu cầu của tuyến đường. . Một số bộ phận của con đường yêu cầu có thể động -Phần tĩnh Ví dụ, so sánh chính xác GET / clients/all các yêu cầu gửi đến, bạn có thể xác định tuyến đường này: GET /clients/all controllers.Clients.list() §Dynamic parts If you want to define a route that, say, retrieves a client by id, you need to add a dynamic part: -Phần động Nếu bạn muốn xác định 1 tuyến đường, để lấy một id của clinet bạn cần add một đường dẫ động GET /clients/:id controllers.Clients.show(id: Long) Note: A URI pattern may have more than one dynamic part. The default matching strategy for a dynamic part is defined by the regular expression [^/]+, meaning that any dynamic part defined as :id will match exactly one URI path segment. - Lưu ý:. Một mẫu URI có thể có nhiều hơn một phần động mặc định cho một phần năng động được xác định bởi các biểu thức chính quy [^ /] +, có nghĩa là bất kỳ phần động định nghĩa là: id sẽ phù hợp chính xác một URI đoạn đường.
  • 13. 13 §Dynamic parts spanning several / If you want a dynamic part to capture more than one URI path segment, separated by forward slashes, you can define a dynamic part using the *id syntax, which uses the .* regular expression: -Phần động mổ rộng/ Nếu bạn muốn có một phần động để nắm bắt nhiều hơn một đoạn đường URI, cách nhau bằng dấu gạch chéo, bạn có thể xác định một phần động bằng cách sử dụng cú pháp * id, trong đó sử dụng * biểu hiện thường xuyên.: GET /files/*name controllers.Application.download(name) Here, for a request like GET /files/images/logo.png, the name dynamic part will capture the images/logo.png value. -Ở đây, đối với một yêu cầu GET /files/images/logo.png, name phần động sẽ lấy giá trị ảnh trong thư múc images/ logo.png §Dynamic parts with custom regular expressions You can also define your own regular expression for a dynamic part, using the $id<regex> syntax: -Phần động với tùy chỉnh biểu thức quy tắc. Bạn cũng có thể xác định biểu thức quy tắc của riêng bạn cho một phần động, sử dụng $ id cú pháp: GET /items/$id<[0-9]+> controllers.Items.show(id: Long) §Call to action generator method The last part of a route definition is the call. This part must define a valid call to an action method. If the method does not define any parameters, just give the fully-qualified method name:
  • 14. 14 -Gọi các hành động tạo phương thức Phần cuối cùng của route là cuộc gọi. Phần này phải xác định một cuộc gọi hợp lệ để một phương thức hành động. Nếu phương thức này không xác định bất kỳ các thông số, chỉ cần cung cấp tên phương thức đầy đủ điều kiện: GET / controllers.Application.homePage() If the action method defines parameters, the corresponding parameter values will be searched for in the request URI, either extracted from the URI path itself, or from the query string. -Nếu các phương thức hành động xác định các thông số, các giá trị tham số tương ứng sẽ được tìm kiếm trong yêu cầu URI, hoặc chiết xuất từ các đường dẫn URI chính nó, hoặc từ chuỗi truy vấn. # Extract the page parameter from the path. # i.e. http://myserver.com/index GET /:page controllers.Application.show(page) Or: # Extract the page parameter from the query string. # i.e. http://myserver.com/?page=index GET / controllers.Application.show(page) Here is the corresponding show method definition in the controllers.Application controller: - Đây là định nghĩa phương thức show tương ứng trong bộ điều khiển controllers.Application: public Result show(String page) { String content = Page.getContentOf(page); response().setContentType("text/html");
  • 15. 15 return ok(content); } §Parameter types For parameters of type String, the parameter type is optional. If you want Play to transform the incoming parameter into a specific Scala type, you can add an explicit type: -Các loại tham số cho các tham số kiểu String, các loại tham số là tùy chọn. Nếu bạn muốn Play biến đổi các tham số đầu vào thành một loại Scala cụ thể, bạn có thể thêm một loại rõ ràng: GET /clients/:id controllers.Clients.show(id: Long) Then use the same type for the corresponding action method parameter in the controller: - Sau đó, sử dụng các loại tương tự cho các tham số phương thức hành động tương ứng trong bộ điều khiển: public Result show(Long id) { Client client = clientService.findById(id); return ok(views.html.Client.show(client)); } Note: The parameter types are specified using a suffix syntax. Also, the generic types are specified using the [] symbols instead of <>, as in Java. For example, List[String] is the same type as the Java List<String>. - Lưu ý: Các loại tham số được chỉ định bằng một cú pháp hậu tố. Ngoài ra, các loại chung được quy định bằng cách sử dụng [] biểu tượng thay vì <>, như trong Java. Ví dụ, Danh sách [Chuỗi] là loại giống như Danh sách Java. §Parameters with fixed values Sometimes you’ll want to use a fixed value for a parameter:
  • 16. 16 -Các thông số với các giá trị cố định Đôi khi bạn sẽ muốn sử dụng một giá trị cố định cho một tham số: # Extract the page parameter from the path, or fix the value for / GET / controllers.Application.show(page = "home") GET /:page controllers.Application.show(page) §Parameters with default values You can also provide a default value that will be used if no value is found in the incoming request: -Các thông số có giá trị mặc định Bạn cũng có thể cung cấp một giá trị mặc định sẽ được sử dụng nếu không có giá trị được tìm thấy trong yêu cầu đầu vào: # Pagination links, like /clients?page=3 GET /clients controllers.Clients.list(page: Int ?= 1) §Optional parameters You can also specify an optional parameter that does not need to be present in all requests: -Thông số tùy chọn Bạn cũng có thể chỉ định một tham số tùy chọn mà không cần phải có mặt trong tất cả các yêu cầu: # The version parameter is optional. E.g. /api/list-all?version=3.0 GET /api/list-all controllers.Api.list(version ?= null) §Routing priority Many routes can match the same request. If there is a conflict, the first route (in declaration order) is used.
  • 17. 17 §Reverse routing The router can be used to generate a URL from within a Java call. This makes it possible to centralize all your URI patterns in a single configuration file, so you can be more confident when refactoring your application. For each controller used in the routes file, the router will generate a ‘reverse controller’ in the routes package, having the same action methods, with the same signature, but returning a play.mvc.Call instead of a play.mvc.Result. The play.mvc.Call defines an HTTP call, and provides both the HTTP method and the URI. For example, if you create a controller like: -Ưu tiên định tuyến Nhiều tuyến đường có thể phù hợp với các yêu cầu tương tự. Nếu có mâu thuẫn, các tuyến đường đầu tiên (theo thứ tự khai) được sử dụng. Xếp định tuyến Router có thể được sử dụng để tạo ra một URL trong một cuộc gọi Java. Điều này làm cho nó có thể tập trung tất cả các mẫu URI của bạn trong một tập tin cấu hình duy nhất, vì vậy bạn có thể tự tin hơn khi sắp xếp ứng dụng của bạn. Đối với mỗi bộ điều khiển được sử dụng trong các tập tin các tuyến đường, các router sẽ tạo ra một 'điều khiển đảo ngược' trong gói đường, có phương pháp hành động tương tự, với cùng một chữ ký, nhưng trở về một play.mvc.Call thay vì một play.mvc.Result. các play.mvc.Call định nghĩa một cuộc gọi HTTP, và cung cấp cho cả hai phương thức HTTP và URI. Ví Ví dụ, nếu bạn tạo một điều khiển như: package controllers; import play.*;
  • 18. 18 import play.mvc.*; public class Application extends Controller { public Result hello(String name) { return ok("Hello " + name + "!"); } } And if you map it in the conf/routes file: # Hello action GET /hello/:name controllers.Application.hello(name) You can then reverse the URL to the hello action method, by using the controllers.routes.Application reverse controller: // Redirect to /hello/Bob public Result index() { return redirect(controllers.routes.Application.hello("Bob")); } Note: There is a routes subpackage for each controller package. So the action controllers.admin.Application.hello can be reversed via controllers.admin.routes.Application.hello. Manipulating the response -Thao tác với các phane hồi §Changing the default Content-Type -Thay đổi giá trị mặ định các kiểu Content The result content type is automatically inferred from the Java value you specify as body.
  • 19. 19 For example: - Các kiểu nội dung kết quả được tự động suy ra từ giá trị Java bạn chỉ định khi trong phần thân Ví dụ: Result textResult = ok("Hello World!"); Will automatically set the Content-Type header to text/plain, while: - Sẽ tự động thiết lập các tiêu đề Content-Type text / plain, trong khi JsonNode json = Json.toJson(object); Result jsonResult = ok(json); will set the Content-Type header to application/json. This is pretty useful, but sometimes you want to change it. Just use the as(newContentType) method on a result to create a new similar result with a different Content-Type header: - Sẽ thiết lập các tiêu đề Content-Type application / json. Điều này là khá hữu ích, nhưng đôi khi bạn muốn thay đổi nó. Chỉ cần sử dụng các phương pháp như (newContentType) vào một kết quả để tạo ra một kết quả mới tương tự với một Content-Type tiêu đề khác nhau: Result htmlResult = ok("<h1>Hello World!</h1>").as("text/html"); §Setting HTTP response headers -Thiết lập tiêu đề phản hồi HTTP public Result index() { response().setContentType("text/html"); response().setHeader(CACHE_CONTROL, "max-age=3600"); response().setHeader(ETAG, "xxx"); return ok("<h1>Hello World!</h1>");
  • 20. 20 } Note that setting an HTTP header will automatically discard any previous value. - Lưu ý rằng thiết lập một tiêu đề HTTP sẽ tự động loại bỏ bất kỳ giá trị trước đó. §Setting and discarding cookies Cookies are just a special form of HTTP headers, but Play provides a set of helpers to make it easier. You can easily add a Cookie to the HTTP response: -Thiết lập và loại bỏ các tập tin cookie Cookie chỉ là một hình thức đặc biệt của các tiêu đề HTTP, nhưng Play cung cấp một tập hợp của những cơ chế để làm cho nó dễ dàng hơn. Bạn có thể dễ dàng thêm một Cookie để đáp ứng HTTP: response().setCookie("theme", "blue"); If you need to set more details, including the path, domain, expiry, whether it’s secure, and whether the HTTP only flag should be set, you can do this with the overloaded methods: - Nếu bạn cần phải thiết lập thêm chi tiết, bao gồm cả đường dẫn, tên miền, hạn sử dụng, cho dù đó là an toàn, và liệu HTTP chỉ nên được thiết lập, bạn có thể làm điều này với các phương thức : response().setCookie( "theme", // name "blue", // value 3600, // maximum age "/some/path", // path ".example.com", // domain false, // secure true // http only
  • 21. 21 ); To discard a Cookie previously stored on the web browser: - Để loại bỏ một Cookie được lưu trước đó trên các trình duyệt web: response().discardCookie("theme"); If you set a path or domain when setting the cookie, make sure that you set the same path or domain when discarding the cookie, as the browser will only discard it if the name, path and domain match. §Specifying the character encoding for text results For a text-based HTTP response it is very important to handle the character encoding correctly. Play handles that for you and uses utf-8 by default. The encoding is used to both convert the text response to the corresponding bytes to send over the network socket, and to add the proper ;charset=xxx extension to the Content-Type header. The encoding can be specified when you are generating the Result value: -Nếu bạn đặt một đường dẫn hoặc miền khi thiết lập các cookie, hãy chắc chắn rằng bạn thiết lập đường dẫn hoặc tên miền khi loại bỏ các tập tin cookie, như trình duyệt sẽ chỉ loại bỏ nó nếu tên, đường dẫn và tên miền không đúng. Xác định các ký tự mã hóa cho kết quả văn bản Đối với một hành động HTTP dựa trên văn bản là rất quan trọng để xử lý các ký tự mã hóa một cách chính xác. Play xử lý đó cho bạn và sử dụng utf-8 theo mặc định. Việc mã hóa được sử dụng cho cả hai chuyển đổi các hnahf động văn bản để các byte tương ứng để gửi qua các ổ cắm mạng, và để thêm thích hợp; charset = xxx mở rộng của header Content-Type . các mã hóa có thể được chỉ định khi bạn đang tạo ra các giá trị kết quả: public Result index() {
  • 22. 22 return ok("<h1>Hello World!</h1>", "iso-8859-1").as("text/html; charset=iso-8859-1"); } Session and Flash scopes -Phiên làm việc và phạm vi của Flash §How it is different in Play -Phan biệt sự khác nhau trong Play If you have to keep data across multiple HTTP requests, you can save them in the Session or the Flash scope. Data stored in the Session are available during the whole user session, and data stored in the flash scope are only available to the next request. It’s important to understand that Session and Flash data are not stored in the server but are added to each subsequent HTTP Request, using Cookies. This means that the data size is very limited (up to 4 KB) and that you can only store string values. Cookies are signed with a secret key so the client can’t modify the cookie data (or it will be invalidated). The Play session is not intended to be used as a cache. If you need to cache some data related to a specific session, you can use the Play built-in cache mechanism and use the session to store a unique ID to associate the cached data with a specific user. Note: There is no technical timeout for the session, which expires when the user closes the web browser. If you need a functional timeout for a specific application, just store a timestamp into the user Session and use it however your application needs (e.g. for a maximum session duration, maximum inactivity duration, etc.). You can also set the maximum age of the session cookie by configuring the key
  • 23. 23 play.http.session.maxAge (in milliseconds) in application.conf, but note that this does not prevent an attacker from keeping and reusing the cookie past the expiration date. -Nếu bạn có để giữ cho dữ liệu trên nhiều yêu cầu HTTP, bạn có thể lưu chúng trong phiên làm việc hoặc phạm vi Flash. Dữ liệu được lưu trữ trong các phiên có sẵn trong các phiên sử dụng toàn bộ, và các dữ liệu được lưu trữ trong phạm vi đèn flash chỉ có sẵn cho các yêu cầu tiếp theo. Điều quan trọng là phải hiểu rằng phiên và Flash dữ liệu không được lưu trữ trong các máy chủ nhưng được thêm vào mỗi HTTP tiếp theo Yêu cầu, sử dụng Cookies. Điều này có nghĩa rằng kích thước dữ liệu rất hạn chế (lên đến 4 KB) và rằng bạn chỉ có thể lưu trữ các giá trị chuỗi. Cookie được kết hợp với một khóa bí mật vì vậy khách hàng không có thể sửa đổi các dữ liệu cookie (hoặc nó sẽ không còn giá trị). Các phiên Play không được dự định sẽ được sử dụng như một bộ nhớ cache. Nếu bạn cần bộ nhớ cache một số dữ liệu liên quan đến một phiên cụ thể, bạn có thể sử dụng Play được xây dựng trong cơ chế bộ nhớ cache và sử dụng phiên để lưu trữ một ID duy nhất để kết hợp các dữ liệu được lưu trữ với một người dùng cụ thể. Lưu ý: Không có thời gian chờ kỹ thuật cho phiên, hết hiệu lực khi người dùng đóng trình duyệt web. Nếu bạn cần một thời gian chờ chức năng cho một ứng dụng cụ thể, chỉ cần lưu trữ một dấu thời gian vào các phiên lam việc dùng và sử dụng nó tuy nhiên nhu cầu ứng dụng của bạn (ví dụ như trong thời hạn tối đa phiên, thời gian không hoạt động tối đa, vv). Bạn cũng có thể thiết lập độ tuổi tối đa của các cookie phiên bằng cách cấu hình play.http.session.maxAge chính (trong mili giây) trong application.conf, nhưng lưu ý rằng điều này không ngăn chặn kẻ tấn công từ việc giữ và tái sử dụng cookie quá hạn §Storing data into the Session -lưu trư lữ liệu vào phiên làm việc As the Session is just a Cookie, it is also just an HTTP header, but Play provides a helper method to store a session value: -1 phiên làm việc chỉ có 1 Cookie, nó cũng chỉ là một HTTP header, nhưng Play cung cấp một phương pháp giúp đỡ để lưu trữ một giá trị session:
  • 24. 24 public Result login() { session("connected", "user@gmail.com"); return ok("Welcome!"); } Theo cùng một cách, bạn có thể loại bỏ bất kỳ giá trị so với phiên đầu vào. public Result logout() { session().remove("connected"); return ok("Bye"); } §Reading a Session value -Đọc 1 giá trị trong phiên làm việc You can retrieve the incoming Session from the HTTP request: - Bạn có thể lấy các giá trị từ phiên đến từ yêu cầu HTTP: public Result index() { String user = session("connected"); if(user != null) { return ok("Hello " + user); } else { return unauthorized("Oops, you are not connected"); } } §Discarding the whole session If you want to discard the whole session, there is special operation: -Loại bỏ toàn bộ phiên Nếu bạn muốn loại bỏ toàn bộ phiên, có hoạt động đặc biệt: public Result logout() { session().clear(); return ok("Bye");
  • 25. 25 } §Flash scope The Flash scope works exactly like the Session, but with two differences:  data are kept for only one request  the Flash cookie is not signed, making it possible for the user to modify it. Important: The flash scope should only be used to transport success/error messages on simple non-Ajax applications. As the data are just kept for the next request and because there are no guarantees to ensure the request order in a complex Web application, the Flash scope is subject to race conditions. So for example, after saving an item, you might want to redirect the user back to the index page, and you might want to display an error on the index page saying that the save was successful. In the save action, you would add the success message to the flash scope: -Phạm vi flash Phạm vi Flash làm việc chính xác như các phiên, nhưng với hai sự khác biệt: • Dữ liệu được lưu giữ cho chỉ có một yêu cầu • cookie Flash không được kết hợp, làm cho nó có thể cho người sử dụng để sửa đổi nó. Quan trọng: Phạm vi flash chỉ nên sử dụng để vận chuyển các thông điệp thành công / lỗi trên các ứng dụng không-Ajax đơn giản. Khi dữ liệu được chỉ giữ cho các yêu cầu tiếp theo và vì không có bảo đảm để đảm bảo trật tự yêu cầu trong một ứng dụng web phức tạp, phạm vi Flash là tùy thuộc vào điều kiện chủng tộc. Vì vậy, ví dụ, sau khi tiết kiệm một mục, bạn có thể muốn chuyển hướng người sử dụng trở lại các trang chỉ mục, và bạn có thể muốn hiển thị một lỗi trên trang chỉ mục nói rằng tiết kiệm là thành công. Trong lưu hành động, bạn sẽ thêm các tin nhắn thành công với phạm vi đèn flash: public Result save() {
  • 26. 26 flash("success", "The item has been created"); return redirect("/home"); } Then in the index action, you could check if the success message exists in the flash scope, and if so, render it: -Sau đó, trong hành động chỉ số, bạn có thể kiểm tra xem các tin nhắn thành công tồn tại trong phạm vi flash, và nếu như vậy, làm cho nó public Result index() { String message = flash("success"); if(message == null) { message = "Welcome!"; } return ok(message); } A flash value is also automatically available in Twirl templates. For example: -Một giá trị flash cũng là tự động có sẵn trong Twirl mẫu. Ví dụ: @if(flash.containsKey("success")) { @flash.get("success") } else { Welcome! } Body parsers §What is a body parser? An HTTP request is a header followed by a body. The header is typically small - it can be safely buffered in memory, hence in Play it is modelled using the RequestHeader class. The body however can be potentially very long, and so is not
  • 27. 27 buffered in memory, but rather is modelled as a stream. However, many request body payloads are small and can be modelled in memory, and so to map the body stream to an object in memory, Play provides a BodyParser abstraction. Since Play is an asynchronous framework, the traditional InputStream can’t be used to read the request body - input streams are blocking, when you invoke read, the thread invoking it must wait for data to be available. Instead, Play uses an asynchronous streaming library called Akka Streams. Akka Streams is an implementation of Reactive Streams, a SPI that allows many asynchronous streaming APIs to seamlessly work together, so though traditional InputStream based technologies are not suitable for use with Play, Akka Streams and the entire ecosystem of asynchronous libraries around Reactive Streams will provide you with everything you need. -Body phân tích cú pháp một bộ phân tích Body là gì? Một yêu cầu HTTP là một tiêu đề tiếp theo là một Body. Các tiêu đề là thường nhỏ - nó có thể được đệm một cách an toàn trong bộ nhớ, do đó trong Chơi nó được mô hình hóa bằng cách sử dụng lớp RequestHeader. Tuy nhiên Body có thể có tiềm năng rất dài, do đó cũng không đệm trong bộ nhớ, nhưng thay vì được mô phỏng như một dòng suối. Tuy nhiên, nhiều trọng tải theo yêu cầu cơ thể là nhỏ và có thể được mô hình trong bộ nhớ, và do đó, để lập bản đồ các dòng cơ thể đến một đối tượng trong bộ nhớ, Play cung cấp một trừu tượng BodyParser. Kể từ Play là một khuôn khổ không đồng bộ, InputStream truyền thống không thể được sử dụng để đọc nội dung yêu cầu - dòng đầu vào được ngăn chặn, khi bạn gọi đọc, thread gọi nó phải đợi cho dữ liệu có sẵn. Thay vào đó, Play sử dụng một thư viện trực tuyến không đồng bộ được gọi là Akka Streams. Akka Streams là một thực hiện của Reactive Streams, một SPI cho phép nhiều API đồng bộ trực tuyến để liên tục làm việc với nhau, vì vậy mặc dù công nghệ InputStream truyền thống không phù hợp để sử dụng với Play, Akka Streams và toàn bộ hệ sinh thái của các thư viện không đồng bộ xung quanh phản ứng Streams sẽ cung cấp bạn với tất cả mọi thứ bạn cần.
  • 28. 28 §Using the built in body parsers Most typical web apps will not need to use custom body parsers, they can simply work with Play’s built in body parsers. These include parsers for JSON, XML, forms, as well as handling plain text bodies as Strings and byte bodies as ByteString. §The default body parser The default body parser that’s used if you do not explicitly select a body parser will look at the incoming Content-Type header, and parses the body accordingly. So for example, a Content-Type of type application/json will be parsed as a JsonNode, while a Content-Type of application/x-form-www-urlencoded will be parsed as a Map<String, String[]>. The request body can be accessed through the body() method on Request, and is wrapped in a RequestBody object, which provides convenient accessors to the various types that the body could be. For example, to access a JSON body: -Sử dụng được xây dựng trong bộ phân tích Body ứng dụng web điển hình, hầu hết sẽ không cần phải sử dụng bộ phân tích Body tùy chỉnh, họ chỉ đơn giản có thể làm việc với Play được xây dựng trong bộ phân tích Body. Chúng bao gồm phân tích cú pháp JSON, XML, hình thức, cũng như xử lý các cơ quan văn bản đơn giản như Strings và các cơ quan byte như ByteString. Phân tích cú pháp Body thể mặc định Bộ phân tích cơ thể mặc định mà được sử dụng nếu bạn không chọn một cách rõ ràng một bộ phân tích cơ thể sẽ xem xét đến Content-Type header, và phân tích cơ thể phù hợp. Vì vậy, ví dụ, một Content-Type của ứng dụng loại / json sẽ được phân tích như một JsonNode, trong khi một Content-Type của application / x-form- www-urlencoded sẽ được phân tích như một bản đồ. Cơ thể yêu cầu có thể được truy cập thông qua các phương pháp cơ thể () theo yêu cầu, và được gói trong một đối tượng RequestBody, cung cấp truy cập thông thuận tiện đến các loại mà cơ thể có thể được. Ví dụ, để truy cập vào một cơ thể JSON:
  • 29. 29 public Result index() { JsonNode json = request().body().asJson(); return ok("Got name: " + json.get("name").asText()); } The following is a mapping of types supported by the default body parser:  text/plain: String, accessible via asText().  application/json: com.fasterxml.jackson.databind.JsonNode, accessible via asJson().  application/xml, text/xml or application/XXX+xml: org.w3c.Document, accessible via asXml().  application/form-url-encoded: Map<String, String[]>, accessible via asFormUrlEncoded().  multipart/form-data: MultipartFormData, accessible via asMultipartFormData().  Any other content type: RawBuffer, accessible via asRaw(). The default body parser tries to determine if the request has a body before it tries to parse. According to the HTTP spec, the presence of either the Content-Length or Transfer-Encoding header signals the presence of a body, so the parser will only parse if one of those headers is present, or on FakeRequest when a non-empty body has explicitly been set. If you would like to try to parse a body in all cases, you can use the AnyContent body parser, described below. - Sau đây là một bản đồ của các loại hỗ trợ bởi Body mặc định phân tích cú pháp: • text / plain: String, truy cập qua asText (). • application / json:. Com.fasterxml.jackson.databind.JsonNode, truy cập qua asJson () • Ứng dụng / xml text / xml hoặc ứng dụng / XXX + xml: org.w3c.Document, truy cập qua asXml (). • ứng dụng / form-url-mã hóa: bản đồ, Truy cập qua asFormUrlEncoded ().
  • 30. 30 • multipart / form-data: MultipartFormData, truy cập qua asMultipartFormData (). • Bất kỳ loại nội dung khác:. RawBuffer, truy cập qua asRaw () Bộ phân tích Body mặc định sẽ cố gắng để xác định xem các yêu cầu có một Body trước khi nó sẽ cố gắng để phân tích. Theo spec HTTP, sự hiện diện của một trong hai Content-Length hoặc tiêu đề Transfer-Encoding báo hiệu sự hiện diện của một Body, do đó, các phân tích cú pháp sẽ chỉ phân tích nếu một trong những tiêu đề là hiện tại, hoặc trên FakeRequest khi cơ thể không sản phẩm nào có một cách rõ ràng được thiết lập. Nếu bạn muốn thử để phân tích một Body trong tất cả các trường hợp, bạn có thể sử dụng bộ phân tích Body AnyContent, mô tả dưới đây. §Choosing an explicit body parser If you want to explicitly select a body parser, this can be done using the @BodyParser.Of annotation, for example: -Lựa chọn một trình phân tích Body rõ ràng Nếu bạn muốn chọn một cách rõ ràng một bộ phân tích Body, điều này có thể được thực hiện bằng cách sử dụng chú thích @ BodyParser.Of, ví dụ: @BodyParser.Of(BodyParser.Text.class) public Result index() { RequestBody body = request().body(); return ok("Got text: " + body.asText()); } The body parsers that Play provides out of the box are all inner classes of the BodyParser class. Briefly, they are:  Default: The default body parser.  AnyContent: Like the default body parser, but will parse bodies of GET, HEAD and DELETE requests.  Json: Parses the body as JSON.
  • 31. 31  TolerantJson: Like Json, but does not validate that the Content-Type header is JSON.  Xml: Parses the body as XML.  TolerantXml: Like Xml, but does not validate that the Content-Type header is XML.  Text: Parses the body as a String.  TolerantText: Like Text, but does not validate that the Content-Type is text/plain.  Bytes: Parses the body as a ByteString.  Raw: Parses the body as a RawBuffer. This will attempt to store the body in memory, up to Play’s configured memory buffer size, but fallback to writing it out to a File if that’s exceeded.  FormUrlEncoded: Parses the body as a form.  MultipartFormData: Parses the body as a multipart form, storing file parts to files.  Empty: Does not parse the body, rather it ignores it. - Các bộ phân tích Body Play cung cấp ra khỏi hộp là tất cả các lớp bên trong của lớp BodyParser. Tóm lại, đó là: • Mặc định: Bộ phân tích Body mặc định. • AnyContent: Giống như bộ phân tích Body mặc định, nhưng sẽ phân tích phương thức của GET, HEAD và DELETE yêu cầu. • Json:. Tách Body như JSON • TolerantJson: Giống như JSON, nhưng không xác nhận rằng Content-Type tiêu đề là JSON. • Xml: Tách cơ thể như XML. • TolerantXml:. Giống như Xml, nhưng không xác nhận rằng Content-Type tiêu đề là XML • Tiêu đề: Tách cơ thể như một string. • TolerantText: Giống như văn bản, nhưng không xác nhận rằng Content-Type
  • 32. 32 là text / plain. • Bytes: Tách Body như một ByteString. • Nguyên: Tách Body như một RawBuffer. Điều này sẽ cố gắng để lưu trữ Body trong bộ nhớ, lên đến Play cấu hình kích thước bộ nhớ đệm, nhưng dự phòng để viết nó ra một tập tin nếu đó là vượt quá. • FormUrlEncoded: Tách Body như một hình thức. • MultipartFormData: Tách Body như một multipart hình thức, lưu trữ phần tập tin vào tập tin. • rỗng: không phân tích Body, chứ không phải nó bỏ qua nó. §Content length limits Most of the built in body parsers buffer the body in memory, and some buffer it on disk. If the buffering was unbounded, this would open up a potential vulnerability to malicious or careless use of the application. For this reason, Play has two configured buffer limits, one for in memory buffering, and one for disk buffering. The memory buffer limit is configured using play.http.parser.maxMemoryBuffer, and defaults to 100KB, while the disk buffer limit is configured using play.http.parser.maxDiskBuffer, and defaults to 10MB. These can both be configured in application.conf, for example, to increase the memory buffer limit to 256KB: - Giới hạn chiều dài nội dung Hầu hết được xây dựng trong bộ phân tích Body trong bộ nhớ, và một số bộ đệm trên đĩa. Nếu đệm là vô hạn, điều này sẽ mở ra một lỗ hổng tiềm năng để sử dụng độc hại hoặc bất cẩn của ứng dụng. Vì lý do này, Play có hai giới hạn bộ đệm cấu hình, một cho trong bộ nhớ đệm, và một cho đệm đĩa. Các giới hạn bộ nhớ đệm được cấu hình sử dụng play.http.parser.maxMemoryBuffer, và mặc định là 100KB, trong khi giới hạn đĩa đệm được cấu hình sử dụng play.http.parser.maxDiskBuffer, và mặc định là 10MB.
  • 33. 33 Những cả hai có thể được cấu hình trong application.conf, ví dụ, để tăng giới hạn bộ nhớ đệm 256KB: play.http.parser.maxMemoryBuffer = 256kb You can also limit the amount of memory used on a per action basis by writing a custom body parser, see below for details. - Bạn cũng có thể giới hạn số lượng bộ nhớ được sử dụng trên một cơ sở cho mỗi hành động bằng cách viết một bộ phân tích cơ thể tùy chỉnh, xem dưới đây để biết chi tiết. §Writing a custom body parser A custom body parser can be made by implementing the BodyParser class. This class has one abstract method: -Viết một phân tích cú pháp body tùy chỉnh Một phân tích body tùy chỉnh có thể được thực hiện bằng cách thực hiện các lớp BodyParser. Lớp này có một phương pháp trừu tượng: public abstract Accumulator<ByteString, F.Either<Result, A>> apply(RequestHeader request); The signature of this method may be a bit daunting at first, so let’s break it down. The method takes a RequestHeader. This can be used to check information about the request - most commonly, it is used to get the Content-Type, so that the body can be correctly parsed. The return type of the method is an Accumulator. An accumulator is a thin layer around an Akka Streams Sink. An accumulator asynchronously accumulates streams of elements into a result, it can be run by passing in an Akka Streams Source, this will return a CompletionStage that will be redeemed when the accumulator is complete. It is essentially the same thing as a Sink<E, CompletionStage<A>>, in fact it is nothing more than a wrapper around this type,
  • 34. 34 but the big difference is that Accumulator provides convenient methods such as map, mapFuture, recover etc. for working with the result as if it were a promise, where Sink requires all such operations to be wrapped in a mapMaterializedValue call. The accumulator that the apply method returns consumes elements of type ByteString - these are essentially arrays of bytes, but differ from byte[] in that ByteString is immutable, and many operations such as slicing and appending happen in constant time. The return type of the accumulator is F.Either<Result, A>. This says it will either return a Result, or it will return a body of type A. A result is generally returned in the case of an error, for example, if the body failed to be parsed, if the Content- Type didn’t match the type that the body parser accepts, or if an in memory buffer was exceeded. When the body parser returns a result, this will short circuit the processing of the action - the body parsers result will be returned immediately, and the action will never be invoked. - Chữ ký của phương pháp này có thể là một chút khó khăn lúc đầu, vì vậy hãy phá vỡ nó xuống. Phương pháp này có một RequestHeader. Điều này có thể được sử dụng để kiểm tra thông tin về yêu cầu - thông thường nhất, nó được sử dụng để có được những Content-Type, vì vậy mà cơ thể có thể được phân tích một cách chính xác. Các kiểu trả về của phương pháp này là một Accumulator. Một ắc quy là một lớp mỏng xung quanh một Akka Streams Sink. Một ắc quy không đồng bộ tích lũy suối của các yếu tố vào một kết quả, nó có thể chạy bằng cách đi qua trong một Akka Streams Nguồn, điều này sẽ trả về một CompletionStage đó sẽ được hoàn trả lại khi ắc hoàn tất. Nó chủ yếu là điều tương tự như một Chìm>, Trên thực tế nó là gì khác hơn một bọc xung quanh loại này, nhưng sự khác biệt lớn là Accumulator cung cấp các phương tiện như bản đồ, mapFuture, phục hồi, vv để làm việc với kết quả như thể nó là một lời hứa, nơi Chìm yêu cầu tất cả các hoạt động đó được bao bọc trong một cuộc gọi mapMaterializedValue. các ắc rằng áp dụng trở về phương pháp tiêu thụ các yếu tố của loại ByteString - đây là những yếu mảng byte, nhưng khác nhau từ byte [] trong ByteString đó là
  • 35. 35 không thay đổi, và nhiều hoạt động như cắt và phụ thêm xảy ra trong thời gian liên tục. các kiểu trả về của ắc quy là F.Either. Điều này nói hoặc là nó sẽ trả về một kết quả, hoặc nó sẽ trở lại một cơ thể của loại A. Một kết quả thường được trả lại trong trường hợp có lỗi, ví dụ, nếu cơ thể không được phân tích, nếu Content-Type không phù hợp với các loại mà các phân tích cú pháp cơ thể chấp nhận, hoặc nếu một trong bộ nhớ đệm được vượt quá. Khi phân tích cơ thể trả về một kết quả, điều này sẽ làm chập mạch các xử lý của hành động - các bộ phân tích cơ thể cho kết quả sẽ được trả lại ngay lập tức, và hành động sẽ không bao giờ được gọi. §Composing an existing body parser As a first example, we’ll show how to compose an existing body parser. Let’s say you want to parse some incoming JSON into a class that you have defined, called Item. First we’ll define a new body parser that depends on the JSON body parser: -Soạn một trình phân tích body hiện tại Ví dụ đầu tiên, chúng ta sẽ thấy làm thế nào để soạn một trình phân tích body hiện tại. Hãy nói rằng bạn muốn phân tích một số JSON đến vào một lớp học mà bạn đã xác định, gọi là Item. Đầu tiên chúng ta sẽ xác định một bộ phân tích body mới mà phụ thuộc public static class UserBodyParser implements BodyParser<User> { private BodyParser.Json jsonParser; private Executor executor; @Inject public UserBodyParser(BodyParser.Json jsonParser, Executor executor) { this.jsonParser = jsonParser; this.executor = executor; }
  • 36. 36 Now, in our implementation of the apply method, we’ll invoke the JSON body parser, which will give us back the Accumulator<ByteString, F.Either<Result, JsonNode>> to consume the body. We can then map that like a promise, to convert the parsed JsonNode body to a User body. If the conversion fails, we return a Left of a Result saying what the error was: - Bây giờ, trong việc thực hiện của chúng ta về áp dụng phương pháp, chúng tôi sẽ gọi bộ phân tích cơ thể JSON, mà sẽ cung cấp cho chúng ta trở về Accumulator> Để tiêu thụ trong cơ thể. Sau đó chúng tôi có thể bản đồ đó như một lời hứa, để chuyển đổi body JsonNode phân tích cú pháp cho một cơ quan tài. Nếu chuyển đổi thất bại, chúng tôi trở lại một trái của một quả nói những gì các lỗi là: public Accumulator<ByteString, F.Either<Result, User>> apply(RequestHeader request) { Accumulator<ByteString, F.Either<Result, JsonNode>> jsonAccumulator = jsonParser.apply(request); return jsonAccumulator.map(resultOrJson -> { if (resultOrJson.left.isPresent()) { return F.Either.Left(resultOrJson.left.get()); } else { JsonNode json = resultOrJson.right.get(); try { User user = play.libs.Json.fromJson(json, User.class); return F.Either.Right(user); } catch (Exception e) { return F.Either.Left(Results.badRequest( "Unable to read User from json: " + e.getMessage())); } } }, executor); } The returned body will be wrapped in a RequestBody, and can be accessed using the as method:
  • 37. 37 - Body trả lại sẽ được bọc trong một RequestBody, và có thể được truy cập bằng cách sử dụng như là phương pháp @BodyParser.Of(UserBodyParser.class) public Result save() { RequestBody body = request().body(); User user = body.as(User.class); return ok("Got: " + user.name); } §Writing a custom max length body parser Another use case may be to define a body parser that uses a custom maximum length for buffering. Many of the built in Play body parsers are designed to be extended to allow overriding the buffer length in this way, for example, this is how the text body parser can be extended: - Viết một tùy chỉnh chiều dài body phân tích cú pháp Một trường hợp sử dụng có thể xác định một phân tích cú pháp body mà sử dụng một chiều dài tối đa tùy chỉnh cho đệm. Nhiều người trong số được xây dựng trong bộ phân tích body Play được thiết kế để được mở rộng để cho phép ghi đè chiều dài đệm theo cách này, ví dụ, đây là cách phân tích cú pháp cơ bản có thể được mở rộng // Accept only 10KB of data. public static class Text10Kb extends BodyParser.Text { @Inject public Text10Kb(HttpErrorHandler errorHandler) { super(10 * 1024, errorHandler); } } @BodyParser.Of(Text10Kb.class) public Result index() { return ok("Got body: " + request().body().asText()); }
  • 38. 38 §Directing the body elsewhere So far we’ve shown extending and composing the existing body parsers. Sometimes you may not actually want to parse the body, you simply want to forward it elsewhere. For example, if you want to upload the request body to another service, you could do this by defining a custom body parser: - Điều khiển các body ở nơi khác đến nay chúng tôi đã chứng minh được mở rộng và sáng tác các bộ phân tích body hiện tại. Đôi khi bạn có thể không thực sự muốn phân tích body, bạn chỉ đơn giản là muốn chuyển tiếp nó ở nơi khác. Ví dụ, nếu bạn muốn tải lên body yêu cầu dịch vụ khác, bạn có thể làm điều này bằng cách định nghĩa một bộ phân tích cơ thể tùy chỉnh: public static class ForwardingBodyParser implements BodyParser<WSResponse> { private WSClient ws; private Executor executor; @Inject public ForwardingBodyParser(WSClient ws, Executor executor) { this.ws = ws; this.executor = executor; } String url = "http://example.com"; public Accumulator<ByteString, F.Either<Result, WSResponse>> apply(RequestHeader request) { Accumulator<ByteString, Source<ByteString, ?>> forwarder = Accumulator.source(); return forwarder.mapFuture(source -> { // TODO: when streaming upload has been implemented, pass the source as the body return ws.url(url)
  • 39. 39 .setMethod("POST") // .setBody(source) .execute().thenApply(F.Either::Right); }, executor); } } §Custom parsing using Akka Streams In rare circumstances, it may be necessary to write a custom parser using Akka Streams. In most cases it will suffice to buffer the body in a ByteString first, by composing the Bytes parser as described above, this will typically offer a far simpler way of parsing since you can use imperative methods and random access on the body. However, when that’s not feasible, for example when the body you need to parse is too long to fit in memory, then you may need to write a custom body parser. A full description of how to use Akka Streams is beyond the scope of this documentation - the best place to start is to read the Akka Streams documentation. However, the following shows a CSV parser, which builds on the Parsing lines from a stream of ByteStrings documentation from the Akka Streams cookbook: -Tuỳ chỉnh phân tích sử dụng Akka Streams Trong trường hợp hiếm hoi, nó body là cần thiết để viết một phân tích cú pháp tùy chỉnh sử dụng Akka Streams. Trong hầu hết các trường hợp, nó sẽ đủ để đệm cơ thể trong một ByteString đầu tiên, bằng cách soạn các Bytes phân tích cú pháp như đã mô tả ở trên, điều này thường sẽ cung cấp một cách đơn giản hơn của phân tích kể từ khi bạn có thể sử dụng phương pháp bắt buộc và truy cập ngẫu nhiên trên body. Tuy nhiên, khi . đó là không khả thi, ví dụ như khi body bạn cần phải phân tích là quá dài để phù hợp trong bộ nhớ, sau đó bạn có thể cần phải viết một bộ phân tích body tùy chỉnh mô tả đầy đủ về cách sử dụng Akka Streams là vượt ra ngoài phạm vi của tài liệu này - nơi tốt nhất để bắt đầu là để đọc tài liệu Akka Streams. Tuy nhiên, sau đây
  • 40. 40 cho thấy một phân tích cú pháp CSV, xây dựng trên các dòng phân tích cú pháp từ một dòng của tài liệu ByteStrings từ sách dạy nấu ăn Akka Streams: public static class CsvBodyParser implements BodyParser<List<List<String>>> { private Executor executor; @Inject public CsvBodyParser(Executor executor) { this.executor = executor; } @Override public Accumulator<ByteString, F.Either<Result, List<List<String>>>> apply(RequestHeader request) { // A flow that splits the stream into CSV lines Sink<ByteString, CompletionStage<List<List<String>>>> sink = Flow.<ByteString>create() // We split by the new line character, allowing a maximum of 1000 characters per line .via(Framing.delimiter(ByteString.fromString("n"), 1000, FramingTruncation.ALLOW)) // Turn each line to a String and split it by commas .map(bytes -> { String[] values = bytes.utf8String().trim().split(","); return Arrays.asList(values); }) // Now we fold it into a list .toMat(Sink.<List<List<String>>, List<String>>fold( new ArrayList<>(), (list, values) -> { list.add(values); return list; }), Keep.right()); // Convert the body to a Right either
  • 41. 41 return Accumulator.fromSink(sink).map(F.Either::Right, executor); } } Action composition This chapter introduces several ways to define generic action functionality. -Thành phần hành động Chương này giới thiệu một số cách để xác định chức năng hành động chung. §Reminder about actions Previously, we said that an action is a Java method that returns a play.mvc.Result value. Actually, Play manages internally actions as functions. Because Java doesn’t yet support first class functions, an action provided by the Java API is an instance of play.mvc.Action: - Reminder hành động Trước đây, chúng tôi đã nói rằng một hành động là một phương thức Java mà trả về một giá trị play.mvc.Result. Trên thực tế, Play quản lý trong nội bộ các hành động như chức năng. Bởi vì Java chưa hỗ trợ chức năng hạng nhất, một hành động được cung cấp bởi các API Java là một thể hiện của play.mvc.Action: public abstract class Action { public abstract CompletionStage<Result> call(Context ctx) throws Throwable; } Play builds a root action for you that just calls the proper action method. This allows for more complicated action composition. - Play xây dựng một hành động gốc cho bạn mà chỉ cần gọi phương thức hành động thích hợp. Điều này cho phép phần hành động phức tạp hơn.
  • 42. 42 §Composing actions Here is the definition of the VerboseAction: -Dưới đây là định nghĩa của VerboseAction: public class VerboseAction extends play.mvc.Action.Simple { public CompletionStage<Result> call(Http.Context ctx) { Logger.info("Calling action for {}", ctx); return delegate.call(ctx); } } You can compose the code provided by the action method with another play.mvc.Action, using the @With annotation: - Bạn có thể soạn mã được cung cấp bởi các phương pháp hành động với play.mvc.Action khác, sử dụng các chú thích @With: @With(VerboseAction.class) public Result verboseIndex() { return ok("It works!"); } At one point you need to delegate to the wrapped action using delegate.call(...). You also mix several actions by using custom action annotations: - Tại một thời điểm bạn cần phải uỷ thác cho các hành động được bao bọc bằng delegate.call (...). Bạn cũng có trộn một vài hành động bằng cách sử dụng các chú thích hành động tùy chỉnh: @Security.Authenticated @Cached(key = "index.result") public Result authenticatedCachedIndex() { return ok("It works!");
  • 43. 43 } Note: play.mvc.Security.Authenticated and play.cache.Cached annotations and the corresponding predefined Actions are shipped with Play. See the relevant API documentation for more information. Note: Every request must be served by a distinct instance of your play.mvc.Action. If a singleton pattern is used, requests will be routed incorrectly during multiple request scenarios. For example, if you are using Spring as a DI container for Play, you need to make sure that your action beans are prototype scoped. - Lưu ý: play.mvc.Security.Authenticated và chú thích play.cache.Cached và nhanh được xác định trước tương ứng được phát triển với Play. Xem tài liệu API có liên quan để biết thêm thông tin. Lưu ý: Mỗi yêu cầu phải được phục vụ bởi một trường hợp riêng biệt của play.mvc.Action của bạn.Nếu một mẫu singleton được sử dụng, yêu cầu sẽ được định tuyến không đúng trong nhiều kịch bản yêu cầu. Ví dụ, nếu bạn đang sử dụng Spring như là một container DI cho Play, bạn cần phải chắc chắn rằng đậu hành động của bạn là nguyên mẫu scoped. §Defining custom action annotations You can also mark action composition with your own annotation, which must itself be annotated using @With: -Xác định hành động tùy chỉnh chú thích Bạn cũng có thể đánh dấu thành phần hành động với chú thích của riêng bạn, mà chính nó phải được chú thích bằng @With: @With(VerboseAnnotationAction.class) @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface VerboseAnnotation { boolean value() default true; }
  • 44. 44 Your Action definition retrieves the annotation as configuration: - Định nghĩa hành động của bạn lấy các chú thích như cấu hình: public class VerboseAnnotationAction extends Action<VerboseAnnotation> { public CompletionStage<Result> call(Http.Context ctx) { if (configuration.value()) { Logger.info("Calling action for {}", ctx); } return delegate.call(ctx); } } You can then use your new annotation with an action method: - Sau đó bạn có thể sử dụng chú thích mới của bạn với một phương pháp hành động: @VerboseAnnotation(false) public Result verboseAnnotationIndex() { return ok("It works!"); } §Annotating controllers You can also put any action composition annotation directly on the Controller class. In this case it will be applied to all action methods defined by this controller. -Việc chú thích các bộ điều khiển Bạn cũng có thể đặt bất kỳ chú thích phần hành động trực tiếp trên lớp điều khiển. Trong trường hợp này, nó sẽ được áp dụng cho tất cả các phương pháp hành động được xác định bởi bộ điều khiển này. @Security.Authenticated public class Admin extends Controller { ...
  • 45. 45 } Note: If you want the action composition annotation(s) put on a Controller class to be executed before the one(s) put on action methods set play.http.actionComposition.controllerAnnotationsFirst = true in application.conf. However, be aware that if you use a third party module in your project it may rely on a certain execution order of its annotations. - Lưu ý: Nếu bạn muốn chú thích phần hành động (s) đặt trên một lớp điều khiển được thực hiện trước khi một (s) đặt trên phương pháp hành động thiết play.http.actionComposition.controllerAnnotationsFirst = true trong application.conf. Tuy nhiên, lưu ý rằng nếu bạn sử dụng một mô-đun của bên thứ ba trong dự án của bạn có thể dựa vào một trật tự thực hiện một số chú thích của nó. §Passing objects from action to controller You can pass an object from an action to a controller by utilizing the context args map. -Đi qua các đối tượng từ hành động để điều khiển Bạn có thể vượt qua một đối tượng từ một hành động để một bộ điều khiển bằng cách sử dụng bản đồ args ngữ cảnh. public class PassArgAction extends play.mvc.Action.Simple { public CompletionStage<Result> call(Http.Context ctx) { ctx.args.put("user", User.findById(1234)); return delegate.call(ctx); } } Then in an action you can get the arg like this: @With(PassArgAction.class) public static Result passArgIndex() { Object user = ctx().args.get("user"); return ok(Json.toJson(user));
  • 46. 46 Intercepting HTTP requests Play’s Java APIs provide two ways of intercepting action calls. The first is called ActionCreator, which provides a createAction method that is used to create the initial action used in action composition. It handles calling the actual method for your action, which allows you to intercept requests. The second way is to implement your own HttpRequestHandler, which is the primary entry point for all HTTP requests in Play. This includes requests from both Java and Scala actions. -Chặn các yêu cầu HTTP API Java Play cung cấp hai cách để chặn các cuộc gọi hành động. Việc đầu tiên được gọi ActionCreator, cung cấp một phương pháp createAction được sử dụng để tạo ra các hành động ban đầu được sử dụng trong phần hành động. Nó xử lý gọi là phương pháp thực tế cho hành động của bạn, cho phép bạn chặn yêu cầu. Cách thứ hai là thực hiện HttpRequestHandler riêng của bạn, đó là điểm vào chính cho tất cả các yêu cầu HTTP trong Play. Điều này bao gồm các yêu cầu từ cả hai hành động Java và Scala. §Action creators The ActionCreator interface has two methods that can be implemented:  createAction: Takes the request and the controller’s action method associated with the passed request. The action can either be the first or the last action depending on the configuration setting play.http.actionComposition.executeActionCreatorActionFirst.  wrapAction: Takes the action to be run and allows for a final global interceptor to be added to the action. This method is deprecated since the same can be achieved using createAction and the above setting.
  • 47. 47 There is also a DefaultActionCreator interface you can extend with default implementations. Note: If you are implementing a custom ActionCreator because you need to apply a cross cutting concern to an action before it is executed, creating a filter is a more idiomatic way of achieving the same. A custom action creator can be supplied by creating a class in the root package called ActionCreator that implements play.http.ActionCreator, for example: -Sáng tạo hành động Giao diện ActionCreator có hai phương pháp có thể được thực hiện: • createAction: Đưa các yêu cầu và phương pháp hành động của bộ điều khiển kết hợp với các yêu cầu thông qua. Các hành động hoặc có thể là người đầu tiên hoặc hành động cuối cùng phụ thuộc vào các thiết lập cấu hình play.http.actionComposition.executeActionCreatorActionFirst. • wrapAction: Đưa các hành động được chạy và cho phép một máy bay đánh chặn toàn cầu cuối cùng sẽ được thêm vào hành động. Phương pháp này không được chấp kể từ khi cùng có thể đạt được bằng cách sử createAction và các thiết lập ở trên. Ngoài ra còn có một giao diện DefaultActionCreator bạn có thể mở rộng với các cài đặt mặc định. Lưu ý: Nếu bạn đang thực hiện một ActionCreator tùy chỉnh bởi vì bạn cần phải áp dụng một mối quan tâm xuyên suốt một hành động trước khi nó được thực thi, tạo ra một bộ lọc là một cách nhiều thành ngữ để đạt được như vậy. một người tạo hành động tùy chỉnh có thể được cung cấp bằng cách tạo ra một lớp trong gói gốc gọi là ActionCreator mà thực hiện play.http.ActionCreator, ví dụ: import play.mvc.Action; import play.mvc.Http; import play.mvc.Result; import java.util.concurrent.CompletionStage; import java.lang.reflect.Method; public class ActionCreator implements play.http.ActionCreator {
  • 48. 48 @Override public Action createAction(Http.Request request, Method actionMethod) { return new Action.Simple() { @Override public CompletionStage<Result> call(Http.Context ctx) { return delegate.call(ctx); } }; } } If you don’t want to place this class in the root package, or if you want to be able to configure different action handlers for different environments, you can do this by configuring the play.http.actionCreator configuration property in application.conf: - Nếu bạn không muốn đặt lớp này trong gói gốc, hoặc nếu bạn muốn để có thể cấu hình xử lý hành động khác nhau cho các môi trường khác nhau, bạn có thể làm điều này bằng cách cấu hình các thuộc tính cấu hình play.http.actionCreator trong application.conf: play.http.actionCreator = "com.example.MyActionCreator" Note: If you are also using action composition then the action returned by the createAction method is executed after the action composition ones by default. If you want to change this order set play.http.actionComposition.executeActionCreatorActionFirst = true in application.conf. - Lưu ý: Nếu bạn cũng đang sử dụng phần hành động thì một hành động trả về bởi phương pháp createAction được thực hiện sau khi những thành phần hoạt động theo mặc định. Nếu bạn muốn thay đổi thứ tự này thiết play.http.actionComposition.executeActionCreatorActionFirst = true trong application.conf.
  • 49. 49 §HTTP request handlers Sometimes an application will have more advanced needs that aren’t met by Play’s abstractions. When this is the case, applications can provide custom implementations of Play’s lowest level HTTP pipeline API, the HttpRequestHandler. Providing a custom HttpRequestHandler should be a last course of action. Most custom needs can be met through implementing a custom router or a filter. §Implementing a custom request handler The HttpRequestHandler interface has one method to be implemented, handlerForRequest. This takes the request to get a handler for, and returns a HandlerForRequest instance containing a RequestHeader and a Handler. The reason why a request header is returned is so that information, such as routing information, can be added to the request. In this way, the router is able to tag requests with routing information, such as which route matched the request, which can be useful for monitoring or even for injecting cross cutting functionality. A very simple request handler that simply delegates to a router might look like this: -Yêu cầu HTTP xử lý Đôi khi một ứng dụng sẽ có nhu cầu cao cấp hơn mà không được đáp ứng bằng cách trừu tượng Play.Khi điều này là trường hợp, các ứng dụng có thể cung cấp triển khai thực hiện tùy chỉnh của API đường ống cấp độ HTTP thấp nhất Play, các HttpRequestHandler. Cung cấp một tùy chỉnh HttpRequestHandler nên là một khóa học cuối cùng của hành động. Hầu hết các nhu cầu tùy chỉnh có thể được đáp ứng thông qua việc thực hiện một bộ định tuyến tùy chỉnh hoặc một bộ lọc. §Implementing một xử lý yêu cầu tùy chỉnh giao diện HttpRequestHandler đã có một phương pháp được thực hiện, handlerForRequest. Này có các yêu cầu để có được một xử lý cho, và trả về một thể HandlerForRequest chứa một RequestHeader và một Handler. Lý do tại sao một tiêu đề yêu cầu được trả về là để thông tin, chẳng hạn như thông tin định tuyến, có thể được thêm vào theo yêu cầu. Bằng cách này, các router có thể gắn thẻ yêu cầu với thông tin định tuyến, chẳng hạn như những
  • 50. 50 tuyến đường phù hợp theo yêu cầu, có thể hữu ích để theo dõi hay thậm chí chích năng cắt chéo. Một xử lý yêu cầu rất đơn giản mà chỉ đơn giản là đại biểu cho một bộ định tuyến có thể trông Như thế này: import javax.inject.Inject; import play.routing.Router; import play.api.mvc.Handler; import play.http.*; import play.mvc.*; import play.libs.streams.Accumulator; public class SimpleHttpRequestHandler implements HttpRequestHandler { private final Router router; @Inject public SimpleHttpRequestHandler(Router router) { this.router = router; } public HandlerForRequest handlerForRequest(Http.RequestHeader request) { Handler handler = router.route(request).orElseGet(() -> EssentialAction.of(req -> Accumulator.done(Results.notFound())) ); return new HandlerForRequest(request, handler); } } Note that HttpRequestHandler currently has two legacy methods with default implementations that have since been moved to ActionCreator. §Configuring the http request handler A custom http handler can be supplied by creating a class in the root package called RequestHandler that implements HttpRequestHandler.
  • 51. 51 If you don’t want to place your request handler in the root package, or if you want to be able to configure different request handlers for different environments, you can do this by configuring the play.http.requestHandler configuration property in application.conf: play.http.requestHandler = "com.example.RequestHandler" Content negotiation Content negotiation is a mechanism that makes it possible to serve different representation of a same resource (URI). It is useful e.g. for writing Web Services supporting several output formats (XML, JSON, etc.). Server-driven negotiation is essentially performed using the Accept* requests headers. You can find more information on content negotiation in the HTTP specification. §Language You can get the list of acceptable languages for a request using the play.mvc.Http.RequestHeader#acceptLanguages method that retrieves them from the Accept-Language header and sorts them according to their quality value. Play uses it to set the lang value of request’s HTTP context, so they automatically use the best possible language (if supported by your application, otherwise your application’s default language is used). §Content Similarly, the play.mvc.Http.RequestHeader#acceptedTypes method gives the list of acceptable result’s MIME types for a request. It retrieves them from the Accept request header and sorts them according to their quality factor. You can test if a given MIME type is acceptable for the current request using the play.mvc.Http.RequestHeader#accepts method: -Nội dung thảo luận nội dung thảo luận là một cơ chế mà làm cho nó có thể để phục vụ đại diện
  • 52. 52 khác nhau của một tài nguyên tương tự (URI). Đây là ví dụ hữu ích cho việc viết Dịch vụ web hỗ trợ nhiều định dạng đầu ra (XML, JSON, vv). Thương lượng máy chủ theo định hướng chủ yếu được thực hiện bằng cách sử dụng Chấp nhận * yêu cầu tiêu đề. Bạn có thể tìm thêm thông tin về đàm phán nội dung trong đặc tả HTTP. §Language Bạn có thể nhận được danh sách các ngôn ngữ được chấp nhận cho một yêu cầu bằng cách sử dụng phương pháp play.mvc.Http.RequestHeader # acceptLanguages để lấy chúng từ tiêu đề Accept-Language và sắp xếp chúng theo giá trị chất lượng của họ. Chơi sử dụng nó để thiết lập giá trị lang của bối cảnh HTTP yêu cầu, vì vậy họ sẽ tự động sử dụng ngôn ngữ tốt nhất có thể (nếu được hỗ trợ bởi ứng dụng của bạn, nếu ngôn ngữ mặc định của ứng dụng của bạn đang sử dụng). §Content Tương tự như vậy, các play.mvc.Http.RequestHeader # phương pháp acceptedTypes cho danh sách các kiểu MIME kết quả chấp nhận được đối với một yêu cầu. Nó lấy chúng từ Chấp nhận yêu cầu tiêu đề và sắp xếp chúng theo yếu tố chất lượng của họ. Bạn có thể kiểm tra nếu một loại MIME cho là chấp nhận được đối với các yêu cầu hiện tại bằng cách sử dụng play.mvc.Http.RequestHeader # chấp nhận phương pháp: public Result list() { List<Item> items = Item.find.all(); if (request().accepts("text/html")) { return ok(views.html.Application.list.render(items)); } else { return ok(Json.toJson(items)); } }
  • 53. 53 III. Asynchronous HTTP programming Handling asynchronous results (Xử lí kết quả không đồng bồ ) Make controllers asynchronous (Điếu khiến không đồng bồ) Internally, Play Framework is asynchronous from the bottom up. Play handles every request in an asynchronous, non-blocking way. (Nồi tải của Play Framework không đồng bồ tử dửới lên , Play xử lí mồi yêu cảu không đồng bồ ....) The default configuration is tuned for asynchronous controllers. In other words, the application code should avoid blocking in controllers, i.e., having the controller code wait for an operation. Common examples of such blocking operations are JDBC calls, streaming API, HTTP requests and long computations. (Cảu hình mảc định là điếu hửớng cho điếu khiến không đồng bồ .Nói cách khác , mã của bản nên tránh bị chản trong controllers, streaming AOI , HTTP và sử tính toàn dài hản ) Although it’s possible to increase the number of threads in the default execution context to allow more concurrent requests to be processed by blocking controllers, following the recommended approach of keeping the controllers asynchronous makes it easier to scale and to keep the system responsive under load. (Mảc dù nó có thế tăng thêm thread trong ngử cảnh mảc định đế đế cho phép xử lí nhiếu
  • 54. 54 yêu cảu đồng thới hớn bảng blocking controllers, Theo cách tiếp cản muồn đế nghị ) Creating non-blocking actions (Tảo actions không đồng bồ) Because of the way Play works, action code must be as fast as possible, i.e., non-blocking. So what should we return from our action if we are not yet able to compute the result? We should return the promise of a result! (Bới vì làm viếc Play , các action cản xử lí càng nhanh càng tồt - non-blocking / , chúng ta nên sử dủng 1 promise cho kết quả Vì vảy chsung ta nên trả vế cái gì tử action của chúng ta khi không tính toán đửớc kết quả ) Java 8 provides a generic promise API called CompletionStage. A CompletionStage<Result> will eventually be redeemed with a value of type Result. By using a CompletionStage<Result> instead of a normal Result, we are able to return from our action quickly without blocking anything. Play will then serve the result as soon as the promise is redeemed. (Java 8 cung cảp CompletionStage ,CompletionStage<Result> cuồi sẽ đửớc lảy lải với 1 giá trị của Result , bảng cách sử dủng CompletionStage<Result> thay thế cho Reult bình thửớng ., chúng ta có thế action nhanh hớn vs block ) The web client will be blocked while waiting for the response, but nothing will be blocked on the server, and server resources can be used to serve other clients. (Các khách hàng sẽ bị chản khi trong thới gian chớ đới oharn hồi , nhửng không có sử chản ớ server , và tài nguyên ớ server có thế đửớc sử dủng cho các khách hàng khac ) How to create a CompletionStage<Result> ( làm thế nào đế cài đảt CompletionStage<Result>) To create a CompletionStage<Result> we need another promise first: the promise that will give us the actual value we need to compute the result: (Đế cài tảo ra CompletionStage<Result> chúng ta cản 1 promise đảu tiên : promise này sẽ đửa chúng ta các giá trị thảt đế tính toán kết quả )
  • 55. 55 CompletionStage<Double> promiseOfPIValue = computePIAsynchronously(); CompletionStage<Result> promiseOfResult = promiseOfPIValue.thenApply(pi -> ok("PI value computed: " + pi) ); Play asynchronous API methods give you a CompletionStage. This is the case when you are calling an external web service using the play.libs.WS API, or if you are using Akka to schedule asynchronous tasks or to communicate with Actors using play.libs.Akka. (thuồc tính API không đồng bồ của Play cung caaso 1 CompletionStage , đây là trửớng hớp khi bản gồi đến dịch vủ web ngoài bảng cách sử dủng play .libs.WS API, hoảc nếu bản sử dủng Akka đế tới các task không đồng bồ hoảc đế giao tiếp vs Actor sử dủng play.libs.Akka) A simple way to execute a block of code asynchronously and to get a CompletionStage is to use the CompletableFuture.supplyAsync() helper: (Cách đớn giản đế thửc hiến 1 khồi không đồng bồ và lảy CompletionStage là sử dủng CompletableFuture.supplyAsync( )) : CompletionStage<Integer> promiseOfInt = CompletableFuture.supplyAsync(() -> intensiveComputation()); Note: It’s important to understand which thread code runs on which promises. Here, the intensive computation will just be run on another thread. (Lửu ý : nó rât quan trồng đế hiếu vế luồng , ớ đây viếc tính toán sẽ chỉ chỉ xảy ra trên 1 luồng) You can’t magically turn synchronous IO into asynchronous by wrapping it in a CompletionStage. If you can’t change the application’s architecture to avoid blocking operations, at some point that operation will have to be executed, and that thread is going to block. So in addition to enclosing the operation in a CompletionStage, it’s necessary to configure it to run in a separate execution context that has been configured with enough threads to deal with the expected concurrency. See Understanding Play thread pools for more information.
  • 56. 56 (Bản không thế đửa 1 đồng bồ IO(vào ra) vào trong không đồng bồ bảng cách gói nó trong CompletionState . Nếu bản không thế thay dồi kiến trúc đế tránh khóa viếc tính toán , 1 vài điếm tính toán đó sẽ thửc hiến và luồng sẽ bị chản , It can also be helpful to use Actors for blocking operations. Actors provide a clean model for handling timeouts and failures, setting up blocking execution contexts, and managing any state that may be associated with the service. Also Actors provide patterns like ScatterGatherFirstCompletedRouter to address simultaneous cache and database requests and allow remote execution on a cluster of backend servers. But an Actor may be overkill depending on what you need. Async results We have been returning Result up until now. To send an asynchronous result our action needs to return a CompletionStage<Result>: public CompletionStage<Result> index() { return CompletableFuture.supplyAsync(() -> intensiveComputation()) .thenApply(i -> ok("Got result: " + i)); } Actions are asynchronous by default Play actions are asynchronous by default. For instance, in the controller code below, the returned Result is internally enclosed in a promise: public Result index() { return ok("Got request " + request() + "!"); } Note: Whether the action code returns a Result or a CompletionStage<Result>, both kinds of returned object are handled internally in the same way. There is a single kind of Action, which is asynchronous, and not two kinds (a synchronous one and an asynchronous one). Returning a CompletionStage is a technique for writing non-blocking code.
  • 57. 57 IV. The Twirl template engine The template engine Overview A Play Scala template is a simple text file that contains small blocks of Scala code. Templates can generate any text-based format, such as HTML, XML or CSV. Template play scala là một file text mà chứa code Scala. Template có thể tự sinh ra bất kỳ định dạng như html, xml hoặc csc. The template system has been designed to feel comfortable to those used to working with HTML, allowing front-end developers to easily work with the templates. Hệ thống template này là được thiết kế sao cho dễ dàng làm việc với html, cho phép người phát triển front-end dễ dàng làm việc với template. Templates are compiled as standard Scala functions, following a simple naming convention. If you create a views/Application/index.scala.html template file, it will generate a views.html.Application.index class that has a render() method. Template là được biên dịch theo chuẩn của Scala, cùng với qui ước tên. Nếu như bạn tạo file views/Application/index.scala.html, nó sẽ sinh ra class views.html.Application.index cái mà có phương thức render(). For example, here is a simple template: Ví dụ: @(customer: Customer, orders: List[Order]) <h1>Welcome @customer.name!</h1> <ul>
  • 58. 58 @for(order <- orders) { <li>@order.getTitle()</li> } </ul> You can then call this from any Java code as you would normally call a method on a class: Bạn có thể sau đó gọi nó như java gọi phương thức trên một class Content html = views.html.Application.index.render(customer, orders); Syntax: the magic ‘@’ character The Scala template uses @ as the single special character. Every time this character is encountered, it indicates the beginning of a dynamic statement. You are not required to explicitly close the code block - the end of the dynamic statement will be inferred from your code: Scala template sử dụng @ như là một ký tự đơn đặc biệt. Ký tự này rất hay gặp và nó chỉ cho ta biết bắt đầu một dynamic statement. Bạn không cần phải viết ký tự kết thúc. Hello @customer.getName()! ^^^^^^^^^^^^^^^^^^ Dynamic code Because the template engine automatically detects the end of your code block by analysing your code, this syntax only supports simple statements. If you want to insert a multi-token statement, explicitly mark it using brackets: Bởi vì template tự động phát hiện kết thúc của khối lệnh bằng cách phân tích code của bạn, cú pháp này chỉ hỗ trợ cho một statement, nếu muốn chèn nhiều statement, sử dụng với () hoặc {} để bao khối lệnh Hello @(customer.getFirstName() + customer.getLastName())! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Dynamic Code
  • 59. 59 Note: Make sure not to include whitespaces between keywords of dynamic statements and parentheses. For example, the following code doesn’t work: Chú ý là không có ký tự trắng giữa từ khóa của dynamic content và ngoặc. Ví dụ bên dưới là không làm việc. @for (menu <- menuList) { ... } // Compilation error: '(' expected but ')' found. ^ You can also use curly brackets, to write a multi-statement block: Hello @{val name = customer.getFirstName() + customer.getLastName(); name}! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Dynamic Code Because @ is a special character, you’ll sometimes need to escape it. Do this by using@@: My email is bob@@example.com Template parameters A template is like a function, so it needs parameters, which must be declared at the top of the template file: Template giống như một hàm, do đó nó cần có biến, cái mà phải được định nghĩa ở trên cùng của template file. @(customer: models.Customer, orders: List[models.Order]) You can also use default values for parameters: Cũng có thể sử dụng giá trị mặc định cho biến. @(title: String = "Home") Or even several parameter groups: Hoặc thậm chí là nhóm các biến.
  • 60. 60 @(title:String)(body: Html) Template constructor By default, a template is generated as a static function that can be invoked in any context. If your template has dependencies on components, such as the messages API, you may find it easier to inject it with the components (and other templates) that it needs, and then you can inject that template into the controllers that use it. Mặc định, template là được sinh ra như là một hàm static cái mà có thể gọi ở bất kỳ context. Nếu teamplate có sự phụ thuộc trên các thành phần, như là messages API, bạn có thể tìm thấy dễ dàng để inject các thành phần hoặc template khác, sau đó có thể inject template vào controllers để sử dụng. Twirl supports declaring a constructor for templates, using @this() syntax at the start of the file, before the template parameters. The arguments to the constructor can be defined in the same way as the template parameters: Twirl hỗ trợ định nghĩa hàm khởi tạo cho template, sử dụng @this() để bắt đầu file, trước các khai báo biến. @this(messagesApi: MessagesApi) @(customer: models.Customer, orders: List[models.Order]) Iterating You can use the for keyword, in a pretty standard way: Sử dụng từ khóa for theo như bên dưới để duyệt phần tử. <ul> @for(p <- products) { <li>@p.getName() ($@p.getPrice())</li> } </ul> Note: Make sure that { is on the same line with for to indicate that the expression continues to next line. Chú ý rằng { là cùng dòng với for.
  • 61. 61 If-blocks If-blocks are nothing special. Simply use Scala’s standard if statement: Khối lệnh if thì không có gì đặc biệt, chỉ đơn giản là sử dụng if của scala như bên dưới: @if(items.isEmpty()) { <h1>Nothing to display</h1> } else { <h1>@items.size() items!</h1> } Declaring reusable blocks You can create reusable code blocks: Bạn có thể tạo tái sử dụng khối lệnh. @display(product: models.Product) = { @product.getName() ($@product.getPrice()) } <ul> @for(product <- products) { @display(product) } </ul> Note that you can also declare reusable pure code blocks: @title(text: String) = @{ text.split(' ').map(_.capitalize).mkString(" ") } <h1>@title("hello world")</h1> Note: Declaring code block this way in a template can be sometime useful but keep in mind that a template is not the best place to write complex logic. It is often better to externalize
  • 62. 62 these kind of code in a Java class (that you can store under the views/package as well if you want). Chú ý là việc định nghĩa khối lệnh trong template có thể thỉnh thoảng có ích, nhưng nhớ rằng template không phải là nơi tốt để viết xử lý logic phức tạp. Thông thường chúng ta tạo nó bên ngoài như là các java class. By convention a reusable block defined with a name starting with implicit will be marked as implicit: Tên cho khối lệnh tái sử dụng bắt đầu với implicit sẽ đánh dấu như là implicit: @implicitFieldConstructor = @{ MyFieldConstructor() } Declaring reusable values You can define scoped values using the defining helper: Bạn có thể định nghĩa phạm vi values sử dung defining helper: @defining(user.getFirstName() + " " + user.getLastName()) { fullName => <div>Hello @fullName</div> } Import statements You can import whatever you want at the beginning of your template (or sub- template): Bạn có thể import bất cứ nơi nào bạn muốn để bắt đầu template hoặc sub-template. @(customer: models.Customer, orders: List[models.Order]) @import utils._ ... To make an absolute resolution, use root prefix in the import statement. Sử dụng import tuyệt đối, sử dụng root ở đầu @import _root_.company.product.core._ If you have common imports, which you need in all templates, you can declare inbuild.sbt TwirlKeys.templateImports += "org.abc.backend._"
  • 63. 63 Comments You can write server side block comments in templates using @* *@: Viết comment sử dụng @**@: @********************* * This is a comment * *********************@ You can put a comment on the first line to document your template into the Scala API doc: @************************************* * Home page. * * * * @param msg The message to display * *************************************@ @(msg: String) <h1>@msg</h1> Escaping By default, dynamic content parts are escaped according to the template type’s (e.g. HTML or XML) rules. If you want to output a raw content fragment, wrap it in the template content type. For example to output raw HTML: Bằng mặc định dynamic content là được bao bởi qui tắc của các loại template. Nếu muốn output một content fragment bao nó bởi template content type. <p> @Html(article.content) </p>
  • 64. 64 Common template use cases Templates, being simple functions, can be composed in any way you want. Below are a few examples of some common scenarios. Một vài trường hợp sử dụng template Layout Let’s declare a views/main.scala.html template that will act as a main layout template: Định nghĩa views/main.scala.html tạo main layout. @(title: String)(content: Html) <!DOCTYPE html> <html> <head> <title>@title</title> </head> <body> <section class="content">@content</section> </body> </html> As you can see, this template takes two parameters: a title and an HTML content block. Now we can use it from another views/Application/index.scala.html template: Template định nghĩa 2 biến: tiêu đề và html content. Bây giờ có thể sử dụng chúng từ: @main(title = "Home") { <h1>Home page</h1> } Note: You can use both named parameters (like @main(title = "Home") and positional parameters, like @main("Home"). Choose whichever is clearer in a specific
  • 65. 65 context. Sometimes you need a second page-specific content block for a sidebar or breadcrumb trail, for example. You can do this with an additional parameter: Thỉnh thoảng cần một trang thứ 2 cái mà định danh nội dung cho sidebar hoặc breadcumb train. Ví dụ: @(title: String)(sidebar: Html)(content: Html) <!DOCTYPE html> <html> <head> <title>@title</title> </head> <body> <section class="content">@content</section> <section class="sidebar">@sidebar</section> </body> </html> Using this from our ‘index’ template, we have: @main("Home") { <h1>Sidebar</h1> } { <h1>Home page</h1> } Alternatively, we can declare the sidebar block separately: @sidebar = { <h1>Sidebar</h1> } @main("Home")(sidebar) { <h1>Home page</h1>