Technical note , Playframework Documentation
"Working with play - java" Translation
Tài liệu ghi chép kĩ thuật về Playframework. Do không có tài liệu về playframwork bằng tiếng Việt, nên chúng tôi đã tạo tài liệu này với mục đích để học tập.
Về nội dung nếu có gì sai sót xin hãy chỉ ra cho chúng tôi. Nếu bản dịch này có giá trị, chúng tôi sẽ đăng tải nó lên cộng đồng.
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>