Jersey RESTful Services: Resources & Responses

2019-05-01 12:47发布

问题:

I see a lot of Jersey-based web services consisting of 1+ WebResources that have 1+ endpoints/methods like so:

package com.myws.fizz;
public class FizzResource {
    @GET
    @Path("/fizz/{id}")
    public Response getFizzById(@PathParam("id") Long id) {
        // ...etc.
    }

    @GET
    @Path("/fizz")
    public Fizz getFizzByFoo(Foo foo) {
        // ...etc.
    }
}

package com.myws.buzz;
public class BuzzResource {
    @POST
    @Path("/buzz")
    public Response createBuzz(Buzz buzz) {
        // ...etc.
    }
}
  • I'm confused with what Jersey considers a "resource". Is there a relationship between a "resource" and a database? A table? A POJO?
  • When do you return a Response vs. a POJO? Look at my 2 getFizz methods above. When do I return a Fizz and when do I return a Response?

回答1:

The term "Resource" isn't so much just a Jersey term, as it is a REST term. When dealing with REST, we have Resources and Representations. A resource can be anything, and in this context, it is some object located on the server, with a URL location. When a client requests for a resource, we send back a representation of it. You ask:

Is there a relationship between a "resource" and a database? A table? A POJO?

It could be a database (that's a thing). And we could simply represent it as a JSON object with the name of the database. Also it could be a table (that's a thing). And we could represent it as a JSON object with the name and column names. It could be a row in a table, and we could represent that with a JSON object with the column names as the keys and the row values as the JSON values. It could be a web page, an image, whatever. So hopefully you get the point that a resource can be anything. What we send back is its representation.

But the term resource is not just limited to returning something to a client request. The client could also send us a representation of a resource, to say create a new resource (POST) or change an existing resource (PUT). The client may send us a JSON representation of a row (resource) in our database.

When do you return a Response vs. a POJO? Look at my 2 getFizz methods above. When do I return a Fizz and when do I return a Response?

Returning Response allows you to fine tune the Response. When the client make a request, they always get back a response. Responses have headers, and entity bodies. When the return type of the resource method is Fizz, you are saying the entity body type will be a Fizz type. When the method returns, what actually happens is that the Fizz object isn't directly returned to the requesting client, all by itself. Behind the scenes, it gets wrapped in a Response that get's sent back to the client. The framework will set the headers it feels appropriate.

So whether we decide to return a Response or Fizz, it will get wrapped in a Response. And like I said, when we return a Response, it allows us to fine tune the Response, adding our own headers, status codes and such. For example, say someone make a POST. You could do something like

@POST
@Path("/buzz")
@Produces(...)
public Response createBuzz(Buzz buzz, @Context UriInfo uriInfo) {
    int buzzID = // create buzz and get the resource id
    UriBuilder builder = uriInfo.getAbsolutePathBuilder();
    builder.path(Integer.toString(buzzId));  // concatenate the id.
    return Response.created(builder.build()).build();
}

Basically what this does is create the resource, say in the database, and we get a return id. We can use the id to concatenate to the URI to the id, this will be the new resource location. In terms of the Response, .created(...) is saying that the status code should be 201 Created, and the value we pass to the created method is the location of the newly created resource. This location will be set as the Location header in the response. So lets say that the path to the POST request is http://blah.com/buzz. What we would send back is http://blah.com/buzz/100, where 100 is the buzzId, and this complete URL is how we will access the buzz resource say with a GET requesst to a resource method annotated with say @GET @PATH("/buzz/{id}")

In terms of a GET, with a Response, we could do

Fizz newFizz = fizzService.getFizz();
return Response.ok(newFizz).build();  // sends the Fizz as the entity body

This is really not so much different than just returning the newFizz from the method, because we aren't doing anything special with the Response. We're just saying the status code should be 200 OK and attaching the entity body. This is what normally happens with a successful GET request. So if we just return the Fizz, instead of a Response, in the case of a successful GET, the framework will implicitly attach a 200 OK status.

Personally, I prefer to return Responses, because of the fine tuning factor.