在REST Web服务中设置HTTP状态码有几个用例,但至少一个现有答案中没有充分记录(即当您使用自动JSON / XML序列化时使用JAXB,并且要返回要序列化的对象以及与默认200不同的状态代码)。
让我尝试列举不同的用例及每种情况的解决方案:
1.错误代码(500、404等)
当您想返回与 200 OK
不同的状态代码时,最常见的用例是出现错误。
例如:
- 请求实体不存在(404)
- 请求语义不正确(400)
- 用户未经授权(401)
- 数据库连接存在问题(500)
- 等等。
a)抛出异常
在这种情况下,处理问题的最清洁方法是抛出异常。 这个异常将由 ExceptionMapper
处理,它将将异常转换为具有适当错误代码的响应。
您可以使用默认的Jersey ExceptionMapper
(我猜其他实现也是如此),并抛出任何现有的 javax.ws.rs.WebApplicationException
子类之一。 这些是预定义的异常类型,预先映射到不同的错误代码,例如:
- BadRequestException(400)
- InternalServerErrorException(500)
- NotFoundException(404)
等等。 您可以在API中找到列表。
或者,您可以定义自己的自定义异常和 ExceptionMapper
类,并通过 @Provider
注释将这些映射器添加到Jersey(此示例的来源):
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);
}
}
提供者:
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
注意:你也可以为你使用的现有异常类型编写ExceptionMappers。
b) 使用响应构建器
另一种设置状态代码的方法是使用Response构建器来构建预期代码的响应。
在这种情况下,你的方法返回类型必须是javax.ws.rs.core.Response。这在其他不同的答案中已经描述,如hisdrewness的接受答案,并且看起来像这样:
@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. 成功,但不是200
当操作成功时,但您想返回一个不同于200的成功代码以及在正文中返回的内容时,您需要设置返回状态。
常见的用例是创建新实体(POST
请求)并希望返回有关此新实体或可能的实体本身的信息,以及201 Created
状态代码。
一种方法是像上面描述的那样使用响应对象并自己设置请求正文。 但是,这样做会使您失去使用JAXB提供的自动序列化为XML或JSON的功能。
这是返回将由JAXB序列化为JSON的实体对象的原始方法:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
这将返回新创建用户的JSON表示,但返回状态将是200而不是201。
现在的问题是,如果我想使用Response
构建器设置返回代码,我必须在我的方法中返回一个Response
对象。如何仍然返回要序列化的User
对象?
a) 在servlet响应上设置代码
解决此问题的一种方法是获取Servlet请求对象并手动设置响应代码,就像Garett Wilson的回答所演示的那样:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, @Context final HttpServletResponse response){
User newUser = ...
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
该方法仍然返回一个实体对象,状态码将为201。
请注意,为了使其工作,我不得不刷新响应。这是我们漂亮的JAX_RS资源中低级Servlet API代码的不愉快复苏,更糟糕的是,在此之后,它会导致头文件不可修改,因为它们已经被发送到网络中。
在这种情况下,最好的解决方案是使用Response对象,并将实体设置为在此响应对象上序列化。在这种情况下,将Response对象通用化以指示有效负载实体的类型会很好,但目前并不是这种情况。
@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();
}
在这种情况下,我们使用响应生成器类的 created 方法来将状态码设置为201。我们通过 entity() 方法将实体对象(user)传递给响应。
结果是HTTP代码是401,正如我们所希望的那样,响应主体与我们之前返回用户对象时完全相同的JSON格式。它还添加了一个位置头。
响应类有许多不同状态(复数形式可能是“stati”)的生成器方法,例如:
Response.accepted()
Response.ok()
Response.noContent()
Response.notAcceptable()
NB:hateoas对象是我开发的辅助类,用于帮助生成资源URI。您需要自己想出自己的机制;)
大概就是这些。
希望这篇冗长的回答能对某人有所帮助 :)