简体   繁体   中英

Jersey 2.x - Conflicting route priority of PUT and GET

I'm working with Dropwizard, which uses Jersey internally. I have two methods on a controller:

PUT /garbage/[id1,id2,...idN] is intended to take a path parameter that's a list of numeric IDs representing resources to be updated. I'm using a regex-based PathParam here. I've fudged the regex in this example because I don't think it matters, but the point is that a single numeric ID should match the regex.

GET /garbage/[id] fetches data about a single piece of garbage.

Jersey seems to get confused, despite the difference in method. When I query with something like

curl localhost:8080/garbage/1

Jersey gives me a 405 error. If I take the PUT out of the picture (for example, sabotage the path param regex, or remove it entirely), the GET endpoint works fine.

I assume there is some detail in JAX-RS 3.7.2 I'm missing that explains why this should be the case, but I can't figure out what it is.

Here's the code:

@Path("/garbage")
@Produces(MediaType.APPLICATION_JSON)
public class GarbageController {
    private static final Logger LOG = LoggerFactory.getLogger(GarbageController.class);

    @PUT
    @Path("/{params: [\\d,]+}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Timed
    public Response updateGarbage(@PathParam("params") List<PathSegment> params) {
        LOG.warn("updateGarbage");
        return Response.status(Response.Status.OK).build();
    }

    @GET
    @Path("/{garbageId}")
    public Response getGarbageById(@PathParam("garbageId") long garbageId) {
        LOG.warn("getGarbage");
        return Response.status(Response.Status.OK).build();
    }
}

The main purpose of @PathSegment is to handle fragments of the URI which is useful to retrieve Matrix Parameters. For example the method below:

@GET
@Path("/book/{id}")
public String getBook(@PathParam("id") PathSegment id) {...}

Should be able to handle this request:

GET /book;name=EJB 3.0;author=Bill Burke

Because the @PathSegment intercepts the entire URL fragment the GET method seems to be ignored. You can handle the comma-separated IDs on the PUT request with a simple String split:

@PUT
@Path("/{params}")
@Consumes(MediaType.APPLICATION_JSON)
@Timed
public Response updateGarbage(@PathParam("params") String params) {
    LOG.warn("updateGarbage ", params.split(","));
    return Response.status(Response.Status.OK).build();
}

You can also change the request format to query parameters or implement a Converter/Provider to handle a custom object. All of them should solve the GET not implemented issue.

I believe this is not a case of route priorities between GET and PUT but instead this is related to the @Consumes annotation which cannot be used on a GET request. Either DW is ignoring this endpoint or is converting it into the default POST method, which would explain the 405 response for the GET request.

I figured this out, although I have not traced far enough into Jersey to know why it works. The solution is to rewrite the @GET method to use the same regex syntax as the @PUT . Jersey will handle the type conversion in the method signature, with the note that it will return a 404 if the type conversion fails (ie, GET /garbage/xyz ).

@PUT
@Path("/{params: .+}")
@Consumes(MediaType.APPLICATION_JSON)
public Response updateGarbage(@PathParam("params") List<PathSegment> params) {
    LOG.warn("updateGarbage");
    return Response.status(Response.Status.OK).build();
}

@GET
@Path("/{params: .+}")
public Response getGarbageById(@PathParam("params") long garbageId) {
    LOG.warn("getGarbage {}", garbageId);
    return Response.status(Response.Status.OK).build();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM