Có một số trường hợp sử dụng để đặt mã trạng thái HTTP trong dịch vụ web REST và ít nhất một trường hợp không được ghi lại đầy đủ trong các câu trả lời hiện có (nghĩa là khi bạn đang sử dụng tuần tự hóa JSON / XML tự động bằng JAXB và bạn muốn trả về đối tượng được tuần tự hóa, nhưng cũng là một mã trạng thái khác với 200 mặc định).
Vì vậy, hãy để tôi thử và liệt kê các trường hợp sử dụng khác nhau và các giải pháp cho từng trường hợp:
1. Mã lỗi (500, 404, ...)
Trường hợp sử dụng phổ biến nhất khi bạn muốn trả về mã trạng thái khác với 200 OK
khi xảy ra lỗi.
Ví dụ:
- một thực thể được yêu cầu nhưng nó không tồn tại (404)
- yêu cầu không đúng về mặt ngữ nghĩa (400)
- người dùng không được ủy quyền (401)
- có vấn đề với kết nối cơ sở dữ liệu (500)
- Vân vân..
a) Ném một ngoại lệ
Trong trường hợp đó, tôi nghĩ rằng cách sạch nhất để xử lý vấn đề là ném ngoại lệ. Ngoại lệ này sẽ được xử lý bởi một ExceptionMapper
, sẽ chuyển ngoại lệ thành phản hồi với mã lỗi thích hợp.
Bạn có thể sử dụng mặc định ExceptionMapper
được cấu hình sẵn với Jersey (và tôi đoán nó giống với các triển khai khác) và ném bất kỳ lớp con hiện có nào javax.ws.rs.WebApplicationException
. Đây là các loại ngoại lệ được xác định trước được ánh xạ trước tới các mã lỗi khác nhau, ví dụ:
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
V.v. Bạn có thể tìm thấy danh sách ở đây: API
Ngoài ra, bạn có thể xác định các ngoại lệ và ExceptionMapper
lớp tùy chỉnh của riêng mình và thêm các trình ánh xạ này vào Jersey bằng phương tiện của @Provider
chú thích ( nguồn của ví dụ này ):
public class MyApplicationException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public MyApplicationException() {
super();
}
public MyApplicationException(String msg) {
super(msg);
}
public MyApplicationException(String msg, Exception e) {
super(msg, e);
}
}
Các nhà cung cấp :
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Lưu ý: bạn cũng có thể viết ExceptionMappers cho các loại ngoại lệ hiện có mà bạn sử dụng.
b) Sử dụng trình xây dựng Phản hồi
Một cách khác để đặt mã trạng thái là sử dụng trình Response
xây dựng để tạo phản hồi với mã dự định.
Trong trường hợp đó, kiểu trả về của phương thức của bạn phải là javax.ws.rs.core.Response
. Điều này được mô tả trong nhiều câu trả lời khác như câu trả lời được chấp nhận của anh ấy và trông như thế này:
@GET
@Path("myresource({id}")
public Response retrieveSomething(@PathParam("id") String id) {
...
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Resource not found for ID: " + uuid).build();
}
...
}
2. Thành công, nhưng không phải 200
Một trường hợp khác khi bạn muốn đặt trạng thái trả về là khi thao tác thành công, nhưng bạn muốn trả về mã thành công khác 200, cùng với nội dung mà bạn trả về trong phần thân.
Trường hợp sử dụng thường xuyên là khi bạn tạo một thực thể ( POST
yêu cầu) mới và muốn trả về thông tin về thực thể mới này hoặc có thể là chính thực thể đó, cùng với 201 Created
mã trạng thái.
Một cách tiếp cận là sử dụng đối tượng phản hồi giống như được mô tả ở trên và tự đặt cơ thể của yêu cầu. Tuy nhiên, bằng cách này, bạn mất khả năng sử dụng tuần tự hóa tự động sang XML hoặc JSON do JAXB cung cấp.
Đây là phương thức ban đầu trả về một đối tượng thực thể sẽ được tuần tự hóa thành JSON bởi JAXB:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
Điều này sẽ trả về một đại diện JSON của người dùng mới được tạo, nhưng trạng thái trả về sẽ là 200, không phải 201.
Bây giờ vấn đề là nếu tôi muốn sử dụng trình Response
xây dựng để đặt mã trả về, tôi phải trả về một Response
đối tượng trong phương thức của mình. Làm thế nào để tôi vẫn trả lại User
đối tượng được nối tiếp?
a) Đặt mã trên phản hồi của servlet
Một cách tiếp cận để giải quyết vấn đề này là lấy một đối tượng yêu cầu servlet và tự đặt mã phản hồi theo cách thủ công, như thể hiện trong câu trả lời của Garett Wilson:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, @Context final HttpServletResponse response){
User newUser = ...
//set HTTP code to "201 Created"
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
Phương thức vẫn trả về một đối tượng thực thể và mã trạng thái sẽ là 201.
Lưu ý rằng để làm cho nó hoạt động, tôi đã phải trả lời. Đây là sự hồi sinh khó chịu của mã API Servlet cấp thấp trong tài nguyên JAX_RS tốt đẹp của chúng tôi và tệ hơn nữa, nó khiến các tiêu đề không thể thay đổi được sau khi chúng được gửi trên dây.
b) Sử dụng đối tượng phản hồi với thực thể
Giải pháp tốt nhất, trong trường hợp đó là sử dụng đối tượng Phản hồi và đặt thực thể được tuần tự hóa trên đối tượng phản hồi này. Sẽ tốt hơn nếu làm cho đối tượng Phản hồi chung chung để chỉ ra loại thực thể tải trọng trong trường hợp đó, nhưng không phải là trường hợp hiện tại.
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response addUser(User user){
User newUser = ...
return Response.created(hateoas.buildLinkUri(newUser, "entity")).entity(restResponse).build();
}
Trong trường hợp đó, chúng tôi sử dụng phương thức đã tạo của lớp Trình tạo phản hồi để đặt mã trạng thái thành 201. Chúng tôi chuyển đối tượng thực thể (người dùng) cho phản hồi thông qua phương thức thực thể ().
Kết quả là mã HTTP là 401 như chúng ta muốn và phần thân của phản hồi là chính xác JSON như chúng ta đã có trước đây khi chúng ta vừa trả về đối tượng Người dùng. Nó cũng thêm một tiêu đề vị trí.
Lớp Phản hồi có một số phương thức xây dựng cho các trạng thái khác nhau (stati?), Như:
Feedback.accepted () Feedback.ok () Feedback.noContent () Feedback.notAcceptable ()
NB: đối tượng ghét là một lớp trợ giúp mà tôi đã phát triển để giúp tạo các URI tài nguyên. Bạn sẽ cần phải đưa ra cơ chế của riêng mình ở đây;)
Đó là về nó.
Tôi hy vọng câu trả lời dài dòng này sẽ giúp được ai đó :)