在REST Web服务中设置HTTP状态代码有几种用例,并且至少有一个在现有答案中没有充分记录(例如,当您使用JAXB使用自动魔术JSON / XML序列化时,您想返回一个要序列化的对象,但状态码也不同于默认值200)。
因此,让我尝试枚举每个用例的不同用例和解决方案:
1.错误代码(500、404等)
要返回与200 OK
发生错误时不同的状态代码的最常见用例。
例如:
- 请求了一个实体,但该实体不存在(404)
- 该请求在语义上不正确(400)
- 用户未被授权(401)
- 数据库连接有问题(500)
- 等等..
a)抛出异常
在这种情况下,我认为处理问题的最干净方法是引发异常。此异常将由ExceptionMapper
,将异常转换为带有适当错误代码的响应。
您可以使用ExceptionMapper
Jersey预先配置的默认值(我想与其他实现相同)并抛出的任何现有子类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
。这在其他各种响应中都有描述,例如历史记录可接受的答案,如下所示:
@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 = ...
//set HTTP code to "201 Created"
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
该方法仍返回实体对象,状态码为201。
请注意,要使其正常工作,我必须刷新响应。在我们不错的JAX_RS资源中,这是低级Servlet API代码的令人不快的死灰复燃,而且更糟的是,这导致报头在此之后无法修改,因为它们已经在网络上发送了。
b)将响应对象与实体一起使用
在这种情况下,最好的解决方案是使用Response对象并设置要在此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();
}
在这种情况下,我们使用Response构建器类的已创建方法将状态代码设置为201。我们通过entity()方法将实体对象(用户)传递给响应。
结果是HTTP代码是我们想要的401,并且响应的正文与刚返回User对象时的JSON完全相同。它还添加了位置标头。
Response类具有许多用于不同状态(stati吗?)的构建器方法,例如:
Response.accepted()Response.ok()Response.noContent()Response.notAcceptable()
注意:hateoas对象是我开发的帮助程序类,用于帮助生成资源URI。您将需要在这里提出自己的机制;)
就是这样
我希望这个冗长的回复可以对某人有所帮助:)