RS - 如何将JSON和HTTP状态码一起返回?
我正在编写一个REST Web应用程序(NetBeans 6.9,JAX-RS,TopLink Essentials)并尝试返回JSON 和 HTTP状态代码。 我有准备好的代码并且在从客户端调用HTTP GET方法时返回JSON。 主要有:
@Path("get/id")
@GET
@Produces("application/json")
public M_機械 getMachineToUpdate(@PathParam("id") String id) {
// some code to return JSON ...
return myJson;
}
但是我也想要返回一个HTTP状态码(500,200,204等)以及JSON数据。
我试图使用HttpServletResponse
:
response.sendError("error message", 500);
但是这使得浏览器认为它是一个“真实”的500,所以输出网页是一个普通的HTTP 500错误页面。
我想返回一个HTTP状态代码,以便我的客户端JavaScript可以根据它处理一些逻辑(例如,在HTML页面上显示错误代码和消息)。 这是可能的还是应该HTTP状态码不能用于这样的事情?
这是一个例子:
@GET
@Path("retrieve/{uuid}")
public Response retrieveSomething(@PathParam("uuid") String uuid) {
if(uuid == null || uuid.trim().length() == 0) {
return Response.serverError().entity("UUID cannot be blank").build();
}
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Entity not found for UUID: " + uuid).build();
}
String json = //convert entity to json
return Response.ok(json, MediaType.APPLICATION_JSON).build();
}
看看Response类。
请注意,您应该始终指定一种内容类型,尤其是在您传递多种内容类型的情况下,但是如果每条消息都将表示为JSON,则可以使用@Produces("application/json")
对该方法进行注释
有几种用于在REST Web服务中设置HTTP状态代码的用例,并且至少有一个在现有答案中没有充分记录(即,当您使用JAXB使用自动奇妙的JSON / XML序列化时,并且您想要返回对象被序列化,但也是一个不同于缺省值200的状态码)。
因此,让我尝试并列举每种不同的使用案例和解决方案:
1.错误代码(500,404,...)
当您想要返回不同于200 OK
的状态码时,最常见的用例是发生错误时。
例如:
a)抛出异常
在那种情况下,我认为处理这个问题最简洁的方法是抛出一个异常。 这个异常将由一个ExceptionMapper
处理,它会将异常转换为具有适当错误代码的响应。
您可以使用预先配置了Jersey的默认ExceptionMapper
(并且我猜它与其他实现相同)并抛出javax.ws.rs.WebApplicationException
任何现有子类。 这些是预定义的异常类型,可以预先映射到不同的错误代码,例如:
等你可以在这里找到列表: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构建器
另一种设置状态码的方法是使用“ 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对象通用以指示有效负载实体的类型将会很好,但目前情况并非如此。
@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 builder类创建的方法将状态码设置为201.我们通过entity()方法将实体对象(用户)传递给响应。
其结果是HTTP代码是我们想要的,并且响应的主体与我们刚刚返回User对象时的JSON完全相同。 它还添加了一个位置标题。
Response类具有许多用于不同状态(stati?)的构建器方法,例如:
Response.accepted()Response.ok()Response.noContent()Response.notAcceptable()
NB:仇恨对象是我开发的一个帮助类,用于帮助生成资源URI。 你需要在这里想出你自己的机制;)
就是这样。
我希望这漫长的回应有助于某人:)
hisdrewness的答案会起作用,但它修改了让Jackson + JAXB等提供程序自动将返回的对象转换为某种输出格式(如JSON)的整个方法。 受Apache CXF文章(使用CXF特定类)的启发,我发现了一种方法来设置应该在任何JAX-RS实现中工作的响应代码:注入一个HttpServletResponse上下文并手动设置响应代码。 例如,以下是适当时如何将响应代码设置为CREATED
。
@Path("/foos/{fooId}")
@PUT
@Consumes("application/json")
@Produces("application/json")
public Foo setFoo(@PathParam("fooID") final String fooID, final Foo foo, @Context final HttpServletResponse response)
{
//TODO store foo in persistent storage
if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
{
response.setStatus(Response.Status.CREATED.getStatusCode());
}
return foo; //TODO get latest foo from storage if needed
}
改进:在找到另一个相关答案之后,我了解到可以将HttpServletResponse
作为成员变量注入,即使对于单例服务类(至少在RESTEasy中)也是如此! 这是一个比使用实现细节污染API更好的方法。 它看起来像这样:
@Context //injected response proxy supporting multiple threads
private HttpServletResponse response;
@Path("/foos/{fooId}")
@PUT
@Consumes("application/json")
@Produces("application/json")
public Foo setFoo(@PathParam("fooID") final String fooID, final Foo foo)
{
//TODO store foo in persistent storage
if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
{
response.setStatus(Response.Status.CREATED.getStatusCode());
}
return foo; //TODO get latest foo from storage if needed
}
链接地址: http://www.djcxy.com/p/1227.html