JAX-RS — How to return JSON and HTTP status code t

2019-01-03 00:52发布

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?

12条回答
beautiful°
2楼-- · 2019-01-03 01:00

Here's an example:

@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();
}

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")

查看更多
手持菜刀,她持情操
3楼-- · 2019-01-03 01:00

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 :

    <init-param>
        <!-- used to overwrite default 4xx state pages -->
        <param-name>jersey.config.server.response.setStatusOverSendError</param-name>
        <param-value>true</param-value>
    </init-param>
查看更多
劳资没心,怎么记你
4楼-- · 2019-01-03 01:03

I found it very useful to build also a json message with repeated code, like this:

@POST
@Consumes("application/json")
@Produces("application/json")
public Response authUser(JsonObject authData) {
    String email = authData.getString("email");
    String password = authData.getString("password");
    JSONObject json = new JSONObject();
    if (email.equalsIgnoreCase(user.getEmail()) && password.equalsIgnoreCase(user.getPassword())) {
        json.put("status", "success");
        json.put("code", Response.Status.OK.getStatusCode());
        json.put("message", "User " + authData.getString("email") + " authenticated.");
        return Response.ok(json.toString()).build();
    } else {
        json.put("status", "error");
        json.put("code", Response.Status.NOT_FOUND.getStatusCode());
        json.put("message", "User " + authData.getString("email") + " not found.");
        return Response.status(Response.Status.NOT_FOUND).entity(json.toString()).build();
    }
}
查看更多
地球回转人心会变
5楼-- · 2019-01-03 01:04

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)

查看更多
家丑人穷心不美
6楼-- · 2019-01-03 01:05

If your WS-RS needs raise an error why not just use the WebApplicationException?

@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Path("{id}")
public MyEntity getFoo(@PathParam("id") long id,  @QueryParam("lang")long idLanguage) {

if (idLanguage== 0){
    // No URL parameter idLanguage was sent
    ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST);
    builder.entity("Missing idLanguage parameter on request");
    Response response = builder.build();
    throw new WebApplicationException(response);
    }
... //other stuff to return my entity
return myEntity;
}
查看更多
我命由我不由天
7楼-- · 2019-01-03 01:10

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.

@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
}

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:

@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
}
查看更多
登录 后发表回答