JAX-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;
}
但我还想随 JSON 数据一起返回 HTTP 状态代码(500、200、204 等)。
我尝试使用 HttpServletResponse
:
response.sendError("error message", 500);
但这使浏览器认为它是“真正的”500,因此输出网页是常规的 HTTP 500 错误页面。
我想返回一个 HTTP 状态代码,以便我的客户端 JavaScript 可以根据它处理一些逻辑(例如在 HTML 页面上显示错误代码和消息)。这可能吗?或者 HTTP 状态代码不应该用于此类事情吗?
I'm writing a REST web app (NetBeans 6.9, JAX-RS, TopLink Essentials) and trying to return JSON and HTTP status code. I have code ready and working that returns JSON when the HTTP GET method is called from the client. Essentially:
@Path("get/id")
@GET
@Produces("application/json")
public M_機械 getMachineToUpdate(@PathParam("id") String id) {
// some code to return JSON ...
return myJson;
}
But I also want to return an HTTP status code (500, 200, 204, etc.) along with the JSON data.
I tried to use HttpServletResponse
:
response.sendError("error message", 500);
But this made the browser think it's a "real" 500 so the output web page was a regular HTTP 500 error page.
I want to return an HTTP status code so that my client-side JavaScript can handle some logic depending on it (to e.g. display the error code and message on an HTML page). Is this possible or should HTTP status codes not be used for such thing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
这是一个示例:
看一下 Response< /a> 类。
请注意,您应该始终指定内容类型,尤其是在传递多个内容类型时,但如果每条消息都表示为 JSON,则只需使用
@Produces("application/json")
Here's an example:
Take a look at the Response class.
Note that you should always specify a content type, especially if you are passing multiple content types, but if every message will be represented as JSON, you can just annotate the method with
@Produces("application/json")
在 REST Web 服务中设置 HTTP 状态代码有多种用例,并且至少有一种用例在现有答案中没有得到充分记录(即,当您使用 JAXB 使用自动神奇的 JSON/XML 序列化,并且想要返回一个要序列化的对象,而且状态代码也不同于默认的 200)。
因此,让我尝试列举不同的用例以及每个用例的解决方案:
1. 错误代码(500、404...)
当您想要返回与
200 OK 不同的状态代码时,最常见的用例
是发生错误的时候。例如:
请求了一个 异常
在这种情况下,我认为处理问题最干净的方法是抛出异常。此异常将由 ExceptionMapper 处理,它将异常转换为具有适当错误代码的响应。
您可以使用 Jersey 中预先配置的默认 ExceptionMapper(我猜它与其他实现相同)并抛出 javax.ws.rs 的任何现有子类。 WebApplicationException。这些是预定义的异常类型,预先映射到不同的错误代码,例如:
等。您可以在此处找到列表: API
或者,您可以定义自己的自定义异常和
ExceptionMapper< /code> 类,并通过
@Provider
注释将这些映射器添加到 Jersey (此示例的来源):Provider :
注意:您还可以为您使用的现有异常类型编写 ExceptionMappers。
b) 使用响应构建器
设置状态代码的另一种方法是使用
响应
构建器来构建具有预期代码的响应。在这种情况下,您的方法的返回类型必须是
javax.ws.rs.core.Response
。这在各种其他响应中进行了描述,例如 hisdrewness' 接受的答案,如下所示:2. 成功,但不是 200
您想要设置返回状态的另一种情况是操作成功,但您想要返回成功代码与 200 不同,以及您在正文中返回的内容。
一个常见的用例是当您创建一个新实体(
POST
请求)并希望返回有关该新实体或实体本身的信息以及201 Created
状态时代码。一种方法是像上面描述的那样使用响应对象并自己设置请求的正文。但是,通过这样做,您将无法使用 JAXB 提供的自动序列化为 XML 或 JSON 的能力。
这是返回将由 JAXB 序列化为 JSON 的实体对象的原始方法:
这将返回新创建用户的 JSON 表示形式,但返回状态将为 200,而不是 201。
现在的问题是,如果我想使用
Response
构建器设置返回代码,我必须在我的方法中返回一个Response
对象。如何仍然返回要序列化的User
对象?a) 在 servlet 响应上设置代码
解决此问题的一种方法是获取 servlet 请求对象并自己手动设置响应代码,如 Garett Wilson 的回答中所示:
该方法仍然返回实体对象,状态代码将为 201 请注意,
为了使其正常工作,我必须刷新响应。这是我们良好的 JAX_RS 资源中低级 Servlet API 代码的令人不快的死灰复燃,更糟糕的是,它导致标头在此之后不可修改,因为它们已经通过网络发送。
b) 将响应对象与实体一起使用
在这种情况下,最好的解决方案是使用响应对象并设置要在此响应对象上序列化的实体。在这种情况下,最好使 Response 对象通用以指示有效负载实体的类型,但目前情况并非如此。
在这种情况下,我们使用 Response 构建器类的created 方法将状态代码设置为201。我们通过entity() 方法将实体对象(用户)传递给响应。
结果是 HTTP 代码是我们想要的 401,响应正文与我们之前返回 User 对象时的 JSON 完全相同。它还添加了位置标头。
Response 类有许多针对不同状态(stati?)的构建器方法,例如:
Response.accepted()
响应.ok()
Response.noContent()
Response.notAcceptable()
注意:hateoas 对象是我开发的一个帮助程序类,用于帮助生成资源 URI。您需要在这里提出自己的机制;)
仅此而已。
我希望这个冗长的回复对某人有所帮助:)
There are several use cases for setting HTTP status codes in a REST web service, and at least one was not sufficiently documented in the existing answers (i.e. when you are using auto-magical JSON/XML serialization using JAXB, and you want to return an object to be serialized, but also a status code different than the default 200).
So let me try and enumerate the different use cases and the solutions for each one:
1. Error code (500, 404,...)
The most common use case when you want to return a status code different than
200 OK
is when an error occurs.For example:
a) Throw an exception
In that case, I think that the cleanest way to handle the problem is to throw an exception. This exception will be handled by an
ExceptionMapper
, that will translate the exception into a response with the appropriate error code.You can use the default
ExceptionMapper
that comes pre-configured with Jersey (and I guess it's the same with other implementations) and throw any of the existing sub-classes ofjavax.ws.rs.WebApplicationException
. These are pre-defined exception types that are pre-mapped to different error codes, for example:Etc. You can find the list here: API
Alternatively, you can define your own custom exceptions and
ExceptionMapper
classes, and add these mappers to Jersey by the mean of the@Provider
annotation (source of this example):Provider :
Note: you can also write ExceptionMappers for existing exception types that you use.
b) Use the Response builder
Another way to set a status code is to use a
Response
builder to build a response with the intended code.In that case, your method's return type must be
javax.ws.rs.core.Response
. This is described in various other responses such as hisdrewness' accepted answer and looks like this :2. Success, but not 200
Another case when you want to set the return status is when the operation was successful, but you want to return a success code different than 200, along with the content that you return in the body.
A frequent use case is when you create a new entity (
POST
request) and want to return info about this new entity or maybe the entity itself, together with a201 Created
status code.One approach is to use the response object just like described above and set the body of the request yourself. However, by doing this you loose the ability to use the automatic serialization to XML or JSON provided by JAXB.
This is the original method returning an entity object that will be serialized to JSON by JAXB:
This will return a JSON representation of the newly created user, but the return status will be 200, not 201.
Now the problem is if I want to use the
Response
builder to set the return code, I have to return aResponse
object in my method. How do I still return theUser
object to be serialized?a) Set the code on the servlet response
One approach to solve this is to obtain a servlet request object and set the response code manually ourselves, like demonstrated in Garett Wilson's answer :
The method still returns an entity object and the status code will be 201.
Note that to make it work, I had to flush the response. This is an unpleasant resurgence of low-level Servlet API code in our nice JAX_RS resource, and much worse, it causes the headers to be unmodifiable after this because they were already sent on the wire.
b) Use the response object with the entity
The best solution, in that case, is to use the Response object and set the entity to be serialized on this response object. It would be nice to make the Response object generic to indicate the type of the payload entity in that case, but is not the currently the case.
In that case, we use the created method of the Response builder class in order to set the status code to 201. We pass the entity object (user) to the response via the entity() method.
The result is that the HTTP code is 401 as we wanted, and the body of the response is the exact same JSON as we had before when we just returned the User object. It also adds a location header.
The Response class has a number of builder method for different statuses (stati ?) such as :
Response.accepted()
Response.ok()
Response.noContent()
Response.notAcceptable()
NB: the hateoas object is a helper class that I developed to help generate resources URIs. You will need to come up with your own mechanism here ;)
That's about it.
I hope this lengthy response helps somebody :)
hisdrewness 的答案是可行的,但它修改了整个方法,让 Jackson+JAXB 等提供者自动将返回的对象转换为某种输出格式(如 JSON)。受到 Apache CXF post (使用 CXF 特定的类)我找到了一种设置响应代码的方法,该代码应该在任何 JAX-RS 实现中工作:注入 HttpServletResponse 上下文并手动设置响应代码。例如,以下是如何在适当的时候将响应代码设置为
CREATED
。改进:在找到另一个相关的答案后,我了解到可以注入
HttpServletResponse
作为成员变量,即使对于单例服务类(至少在 RESTEasy 中)!这是比用实现细节污染 API 更好的方法。它看起来像这样:The answer by hisdrewness will work, but it modifies the whole approach to letting a provider such as Jackson+JAXB automatically convert your returned object to some output format such as JSON. Inspired by an Apache CXF post (which uses a CXF-specific class) I've found one way to set the response code that should work in any JAX-RS implementation: inject an HttpServletResponse context and manually set the response code. For example, here is how to set the response code to
CREATED
when appropriate.Improvement: After finding another related answer, I learned that one can inject the
HttpServletResponse
as a member variable, even for singleton service class (at least in RESTEasy)!! This is a much better approach than polluting the API with implementation details. It would look like this:如果您希望资源层中没有
Response
对象,那么我建议您使用@NameBinding
并绑定到ContainerResponseFilter
的实现。这是注释的要点:
这是过滤器的要点:
然后资源上的实现就变成了:
If you like to keep your resource layer clean of
Response
objects, then I recommend you use@NameBinding
and binding to implementations ofContainerResponseFilter
.Here's the meat of the annotation:
Here's the meat of the filter:
And then the implementation on your resource simply becomes:
我发现使用重复代码构建 json 消息非常有用,如下所示:
I found it very useful to build also a json message with repeated code, like this:
如果您因为异常而想要更改状态代码,则可以使用 JAX-RS 2.0 实现这样的 ExceptionMapper。这可以处理整个应用程序的此类异常。
In case you want to change the status code because of an exception, with JAX-RS 2.0 you can implement an ExceptionMapper like this. This handles this kind of exception for the whole app.
如果您的 WS-RS 需要引发错误,为什么不直接使用 WebApplicationException?
If your WS-RS needs raise an error why not just use the WebApplicationException?
JAX-RS 支持标准/自定义 HTTP 代码。请参阅 ResponseBuilder 和 ResponseStatus,例如:
http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/Response.ResponseBuilder.html#status %28javax.ws.rs.core.Response.Status%29
请记住,JSON 信息更多的是关于与资源/应用程序关联的数据。 HTTP 代码更多地涉及所请求的 CRUD 操作的状态。 (至少在 REST-ful 系统中应该是这样)
JAX-RS has support for standard/custom HTTP codes. See ResponseBuilder and ResponseStatus, for example:
http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/Response.ResponseBuilder.html#status%28javax.ws.rs.core.Response.Status%29
Keep in mind that JSON information is more about the data associated with the resource/application. The HTTP codes are more about the status of the CRUD operation being requested. (at least that is how it's supposed to be in REST-ful systems)
请查看此处的示例,它最好地说明了该问题以及在最新(2.3.1)版本的 Jersey 中如何解决该问题。
https://jersey.java.net/documentation/latest/representations.html#d0e3586
它基本上涉及定义自定义异常并将返回类型保留为实体。当出现错误时,抛出异常,否则,返回 POJO。
Please look at the example here, it best illustrates the problem and how it is solved in the latest (2.3.1) version of Jersey.
https://jersey.java.net/documentation/latest/representations.html#d0e3586
It basically involves defining a custom Exception and keeping the return type as the entity. When there is an error, the exception is thrown, otherwise, you return the POJO.
我没有使用 JAX-RS,但我有一个类似的场景,我使用:
I'm not using JAX-RS, but I've got a similar scenario where I use:
另请注意,默认情况下,如果 http 代码为 400 或以上,Jersey 将覆盖响应正文。
为了将指定的实体作为响应正文,请尝试将以下 init-param 添加到 web.xml 配置文件中的 Jersey 中:
Also, notice that by default Jersey will override the response body in case of an http code 400 or more.
In order to get your specified entity as the response body, try to add the following init-param to your Jersey in your web.xml configuration file :
以下代码对我有用。通过带注释的 setter 注入 messageContext 并在我的“add”方法中设置状态代码。
The following code worked for me. Injecting the messageContext via annotated setter and setting the status code in my "add" method.
扩展答案的Nthalk 与 Microprofile OpenAPI 您可以使用 < 来将返回代码与您的文档对齐a href="https://github.com/eclipse/microprofile-open-api/blob/master/api/src/main/java/org/eclipse/microprofile/openapi/annotations/responses/APIResponse.java" rel= “nofollow noreferrer”>@APIResponse 注释。
这允许标记 JAX-RS 方法,例如
您可以使用 ContainerResponseFilter
当您在方法上添加多个注释时,会出现警告,例如
Expanding on the answer of Nthalk with Microprofile OpenAPI you can align the return code with your documentation using @APIResponse annotation.
This allows tagging a JAX-RS method like
You can parse this standardized annotation with a ContainerResponseFilter
A caveat occurs when you put multiple annotations on your method like
我正在将 jersey 2.0 与消息正文读取器和写入器一起使用。我将我的方法返回类型作为特定实体,该实体也用于消息正文编写器的实现,并且我返回相同的 pojo,即 SkuListDTO。
@得到
@Consumes({"application/xml", "application/json"})
@Produces({"application/xml", "application/json"})
@Path("/skuResync")
我改变的只是这个,我单独留下了编写器实现,它仍然有效。
I'm using jersey 2.0 with message body readers and writers. I had my method return type as a specific entity which was also used in the implementation of the message body writer and i was returning the same pojo, a SkuListDTO.
@GET
@Consumes({"application/xml", "application/json"})
@Produces({"application/xml", "application/json"})
@Path("/skuResync")
all i changed was this, I left the writer implementation alone and it still worked.