Is it good practice to use @Produces(“application/json”) on all JSON producing endpoints?

前端 未结 2 518
失恋的感觉
失恋的感觉 2021-01-25 07:10

We started using Jersey/JAX-RS for internal REST endpoints that get used by our front-end code. Endpoints that have to return a result, always send JSON objects.

For deb

相关标签:
2条回答
  • 2021-01-25 07:43

    Is it good practice to use @Produces("application/json") on all JSON producing endpoints?

    If your resource methods produce JSON as representation of your resources, they should be annotated with @Produces(MediaType.APPLICATION_JSON). As a result, the response will have a Content-Type header indicating the media type of the payload.

    The @Produces annotation is also used for request matching: The JAX-RS runtime matches the media type sent in the Accept header with the media type defined in the @Produces annotation.


    If you don't want to annotate every resource method in your application, you can annotate the resource classes instead. It will indicate that all methods defined in such class must produce JSON as representation of your resources.


    The media type defined in the @Produces annotation indicates the media type that will be produced by the MessageBodyWriter instances registered in the application. Consider the following example:

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Foo getFoo() {
        Foo foo = new Foo();
        return Response.ok(foo).build();
    }
    

    Once the getFoo() method is annotated with @Produces(MediaType.APPLICATION_JSON), JAX-RS will write the Foo instance as a JSON document. It's done in a MessageBodyWriter implementation. If your application uses Jackson, for example, the JacksonJsonProvider will be used to convert Java objects to JSON documents.

    0 讨论(0)
  • 2021-01-25 07:47

    You should always declare the @Produces and @Consumes annotations (either at the class level or method level) for the purpose of Content Negotiation and HTTP protocol correctness. Without these annotations, the result will be dependent on the client request and the default behavior of the server (which may be different across implementations), which leads to unpredictable and ambiguous results.

    With these annotations, we advertise what media types we can produce and consume. On Retrieve (GET) requests, the client should send an Accept header with the media type of the resource they expect back. And on Create requests (PUT, POST), the client should send a Content-Type header telling the server what media type the data is that they are sending. If these headers don't match what the server is advertised to handle, then the client will get error responses back telling them what the problem is; with a Retrieve request and a non-matching Accept header, the response will be a 406 Not Acceptable. With a Create request and a non-matching Content-Type header, the response will be a 415 Unsupported Media Type.

    This is how content negotiation works. And to make sure our server behaves as the clients expect, we should declare what we can handle on the server. The annotations do just this.

    As you mentioned, when you left off the @Produces, the client told you you needed to change the response type. This is because the result was that the Content-Type response header was set to application/octet-stream, which is what the answers here conclude. Clients use the Content-Type header to determine how to handle the response.

    That last example was for a Retrieve request. If we left off the @Consumes on a Create endpoint, a lot of different things can go wrong. Take for example we have an endpoint that we want to accept JSON, so we create a POJO to map the JSON to.

    @POST
    public Response create(Customer customer) {}
    

    For this to work, it is dependent on the client setting the Content-Type header on the request to application/json. But without the @Consumes annotation, we are basically advertising this endpoint to be able to accept any media type, which is just ridiculous. The @Consumes annotation acts like a guard saying "If you don't send the right type of data, you cannot pass". But since we don't have the guard, all data is allowed through, and the result is unpredictable, because depending on what the client sets the Content-Type to, we don't know what MessageBodyReader1 will handle the conversion from the entity body to Customer. If the correct MessageBodyReader is not chosen (the one that converts JSON to POPJOs), then most likely it will lead to an exception, and the client will get back a 500 Internal Server Error, which is not as specific as getting a 415 Unsupported Media Type.


    1. See chapter 8 and 9 of the Jersey docs. It will explain how entity bodies are converted to Java objects (and vice versa) using MessageBodyReader and MessageBodyWriter, respectively.

    0 讨论(0)
提交回复
热议问题