在这个例子中,使用子资源的PUT请求是如何由JAX-RS运行时处理的? [英] How is the PUT request in this example using subresource is processed by JAX-RS run time?

查看:104
本文介绍了在这个例子中,使用子资源的PUT请求是如何由JAX-RS运行时处理的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读 oracle docs。

我只是无法理解JAX-RS运行时如何解决这个PUT请求?

I am just unable to understand how this PUT request is resolved by JAX-RS runtime?

curl -X PUT http://127.0.0.1:9998/storage/containers/quotes

这里是与此请求对应的代码段(取自上面的链接)。

here is the code snippet that corresponds to this request (taken from above link).

@Path("/containers")
@Produces("application/xml")
public class ContainersResource {
    @Context UriInfo uriInfo;
    @Context Request request;

    @Path("{container}")
    public ContainerResource getContainerResource(@PathParam("container") String container) {
        return new ContainerResource(uriInfo, request, container);
    }

    @GET
    public Containers getContainers() {
        System.out.println("GET CONTAINERS");

        return MemoryStore.MS.getContainers();
    }    
}

但是你可以注意到,没有方法 @PUT注释。但是为 / containers / {container} 调用 getContainerResource 方法。在此方法中,返回 ContainerResource 的新实例。我不确定如何处理上面的 PUT 请求。请解释。

But as you can notice, there is no method with @PUT annotation. But the getContainerResource method is called for /containers/{container}. In this method, a new instance of ContainerResource is returned. I am not sure how the above PUT request is processed. Please explain.

这是 ContainerResource类

@Produces("application/xml")
public class ContainerResource {
    @Context UriInfo uriInfo;
    @Context Request request;
    String container;

    ContainerResource(UriInfo uriInfo, Request request, String container) {
        this.uriInfo = uriInfo;
        this.request = request;
        this.container = container;
    }

    @GET
    public Container getContainer(@QueryParam("search") String search) {
        System.out.println("GET CONTAINER " + container + ", search = " + search);

        Container c = MemoryStore.MS.getContainer(container);
        if (c == null)
            throw new NotFoundException("Container not found");


        if (search != null) {
            c = c.clone();
            Iterator<Item> i = c.getItem().iterator();
            byte[] searchBytes = search.getBytes();
            while (i.hasNext()) {
                if (!match(searchBytes, container, i.next().getName()))
                    i.remove();
            }
        }

        return c;
    }    

    @PUT
    public Response putContainer() {
        System.out.println("PUT CONTAINER " + container);

        URI uri =  uriInfo.getAbsolutePath();
        Container c = new Container(container, uri.toString());

        Response r;
        if (!MemoryStore.MS.hasContainer(c)) {
            r = Response.created(uri).build();
        } else {
            r = Response.noContent().build();
        }

        MemoryStore.MS.createContainer(c);
        return r;
    }

    @DELETE
    public void deleteContainer() {
        System.out.println("DELETE CONTAINER " + container);

        Container c = MemoryStore.MS.deleteContainer(container);
        if (c == null)
            throw new NotFoundException("Container not found");
    } 


    @Path("{item: .+}")
    public ItemResource getItemResource(@PathParam("item") String item) {
        return new ItemResource(uriInfo, request, container, item);
    }

    private boolean match(byte[] search, String container, String item) {
        byte[] b = MemoryStore.MS.getItemData(container, item);

        OUTER: for (int i = 0; i < b.length - search.length + 1; i++) {
            for (int j = 0; j < search.length; j++) {
                if (b[i + j] != search[j])
                    continue OUTER;
            }

            return true;
        }

        return false;
    }
}


推荐答案

这是一个称为子资源定位器的文档功能: https://jersey.java.net /documentation/latest/jaxrs-resources.html

This is a documented feature called sub-resource locators: https://jersey.java.net/documentation/latest/jaxrs-resources.html

@Path("{container}")
public ContainerResource getContainerResource(@PathParam("container") String container) {
    return new ContainerResource(uriInfo, request, container);
}

上面的@Path注释将ContainerResource标识为子资源。请注意,ContainerResource确实具有正在调用的带注释的PUT方法。如果您需要进一步说明,我可以尝试扩展此答案。

The @Path annotation above identifies a ContainerResource as a sub-resource. Note that the ContainerResource does have an annotated PUT method that is being invoked. If you need further explanation, I can try to expand this answer.

更新

这不容易解释,但这里有一个解释它... ...

This is not easy to explain, but here is a stab at explaining it...

让我们通过查看你的类支持的各种URL开始解释这个。由ContainersResource类实现的唯一端点如下:

Let's start explaining this by looking at the various URL's your classes support. The ONLY end-point implemented by ContainersResource class is the following:

GET /containers/

此终点是顶级资源的一部分。显然它返回容器的集合/列表。

This end-point is part of a top-level resource. Obviously it returns the collection/list of containers.

现在如果我们想要一个端点来获取id的特定容器怎么办?正常的REST端点会在集合上使用GET操作公开端点,然后将id作为路径参数(PathParam),因此对于ID为27的容器,调用可能如下所示:

Now what if we want an end-point to get a specific container by id? Normal REST end-points would expose the end-point with a GET operation on the collection followed by the id as a path parameter (PathParam), so a call may look like this for container with id 27:

GET /containers/27/

执行此操作的一个解决方案如下:

One solution to do this would be as follows:

@Path("/containers")
@Produces("application/xml")
public class ContainersResource {
    @Context UriInfo uriInfo;
    @Context Request request;

    @GET
    public Containers getContainers() {
        System.out.println("GET CONTAINERS");

        return MemoryStore.MS.getContainers();
    }

    //
    // This is a solution WITHOUT sub-resource...
    // Note that the Path annotation is same as you have it, but
    // now the HTTP method annotation is provided.  Also, the
    // method returns Container instead of ContainerResource
    //
    @Path("{container}")
    @GET
    public Container getContainerResource(@PathParam("container") String container) {
        // Go to memory store and get specific container...
        Container x = findContainer(container);
        return x;
    }

    //
    // Again, without sub-resource, we can define PUT method
    // on specific container id and again define the path param
    //
    @Path("{container}")
    @PUT
    public Response putContainer(@PathParam("container") String container) {
        // Process payload to build container, put into memory store
        Response r = putContainer(container, ...);
        return r;
    }

}

不使用子资源导致我们必须将多个GET,PUT,POST,DELETE方法放入同一个类中,因为我们将方法从较低级别的资源提升到最顶层的资源类。它还使我们必须多次为容器ID定义路径参数。这只是两个级别的资源(容器集合和特定容器)所以它看起来并不太糟糕,但随着我们的路径变得更深入呢?容器具有项目,因此完全可访问的API可以实现端点,该端点允许您仅从特定集合中检索项目,甚至从集合中检索特定项目。这些调用分别如下所示:

Not using sub-resources causes us to have to put multiple GET, PUT, POST, DELETE methods into the same class as we promote methods from lower level resources up to the top-most resource class. It also causes us to have to define the path param for the container id multiple times. This is just 2 levels of resources (the container collection, and specific container) so it does not seem too bad, but what about as our paths grow deeper? Containers have items, so a fully accessible API may implement an endpoint that allows you to retrieve just the items from a specific collection, or even a specific item from a collection. These calls would respectively look like this:

GET /containers/27/items/ (to get the items collection)
GET /containers/27/items/9/ (to get specific item from collection)

所以现在,我们的父资源需要定义4个单独的GET方法。

So now, our parent resource would need to define 4 separate GET methods.

为了避免在同一个类中有多个GET / POST / PUT / DELETE方法,我们仍然可以将它们分解为4个不同的类,每个类都有唯一的Path注释,但如果路径中的某些术语(如容器)需要重命名,我们必须在4个地方而不是一个地方更新代码。此外,必须在每个方法上定义路径上的所有路径参数。

To avoid multiple GET/POST/PUT/DELETE methods in the same class we could still break these into 4 different classes each with unique Path annotations on the class, but then if some term in the path such as "containers" needed renamed, we would have to update the code in 4 places instead of one place. Also, all the path parameters along the path would have to be defined on each method.

为了说明这一点,请考虑您提供的使用子资源的ContainersResource类: / p>

To illustrate this, consider the ContainersResource class you provided that uses sub-resource:

@Path("/containers")
@Produces("application/xml")
public class ContainersResource {
    @Context UriInfo uriInfo;
    @Context Request request;

    @Path("{container}")
    public ContainerResource getContainerResource(@PathParam("container") String container) {
        return new ContainerResource(uriInfo, request, container);
    }

}

getContainerResource方法声明有由路径参数container标识的子资源。如果我们不使用子资源,可以直接在ContainerResource类中实现,如下所示:

The getContainerResource method declares that there is a sub-resource identified by the path parameter "container". This could be implemented directly in the ContainerResource class as follows if we were not using sub-resources:

@Path("containers/{container}")
@Produces("application/xml")
public class ContainerResource {

    @GET
    public Container getContainer(
    @PathParam("container") String container,
    @QueryParam("search") String search) {
        System.out.println("GET CONTAINER " + container + ", search = " + search);

        Container c = MemoryStore.MS.getContainer(container);
    // do work
        return c;
    }    
}

请注意,我必须将路径注释添加到用于定义端点位置的类以及存在路径参数的事实。另外,我必须向GET方法添加一个PathParam参数(并且还必须将其添加到我的其他方法中)以了解容器值是什么。

Notice that I had to add the Path annotation to the class to define the end-point location and the fact that there is a path parameter. Additionally I had to add a PathParam argument to the GET method (and would have to add it to my other methods as well) to know what the container value is.

To进一步证明这一点,考虑我们是否在不使用子资源的情况下实现ContainerItemsResource:

To further demonstrate this, consider if we implement the ContainerItemsResource without using sub-resources:

@Path("containers/{container}/items")
@Produces("application/xml")
public class ContainerItemsResource {

    @GET
    public ContainerItems getContainerItems(
    @PathParam("container") String container) {
        Container c = MemoryStore.MS.getContainer(container);
        return c.getItems();
    }    
}

和ContainerItemResource

And the ContainerItemResource

@Path("containers/{container}/items/{item}")
@Produces("application/xml")
public class ContainerItemResource {

    @GET
    public ContainerItem getContainerItem(
    @PathParam("container") String container,
        @PathParam("item" String item) {
        Container c = MemoryStore.MS.getContainer(container);
        return c.getItems();
    }    
}

这里我们再次重复完整路径,并且必须在每个方法上重新定义容器路径参数。

Here we go again repeating the full Path and having to redefine the container path parameter on each and every method.

子资源提供了一个优雅的解决方案:(1)允许路径中的每个级别使用单个GET / PUT / POST / DELETE方法拥有自己的类,(2)不需要在多个位置重新定义路径中的级别,和(3)不需要在每个方法上重新定义查询参数。这是四个使用子资源方法的资源文件(仅提供GET方法用于说明):

Sub-resources provide an elegant solution that (1) allows each level in the path to have it's own class with single GET/PUT/POST/DELETE methods, (2) does not require redefining levels in the path in multiple places, and (3) does not require redefining query parameters on every method. Here are the four resource files using sub-resource approach (only providing GET method for illustration):

@Path("/containers")
@Produces("application/xml")
public class ContainersResource {
    @Context UriInfo uriInfo;
    @Context Request request;

    // Define sub-resource for specific container
    @Path("{container}")
    public ContainerResource getContainerResource(@PathParam("container") String container) {
        return new ContainerResource(container);
    }

    // Provide @GET, @PUT, @POST, @DELETE to get collection of containers

    @GET
    public Containers getContainers() {
        return MemoryStore.MS.getContainers();
    }
}


@Produces("application/xml")
public class ContainerResource {
    @Context UriInfo uriInfo;
    @Context Request request;
    String container;

    // Constructor allowing it to be used as sub-resource
    ContainerResource(String container) {
        this.container = container;
    }

    // Define sub-resource for items collection
    @Path("items")
    public ContainerItemsResource getContainerItemsResource() {
        return new ContainerItemsResource(container);
    }

    // Provide @GET, @PUT, @POST, @DELETE to get specific container

    // Notice that path params are not redefined...
    @GET
    public Container getContainer() {
        Container c = MemoryStore.MS.getContainer(container);
        return c;
    }    

}


@Produces("application/xml")
public class ContainerItemsResource {
    @Context UriInfo uriInfo;
    @Context Request request;
    String container;

    // Constructor allowing it to be used as sub-resource
    ContainerItemsResource(String container) {
        this.container = container;
    }

    // Define sub-resource for specific item
    @Path("{item}")
    public ContainerItemsResource getContainerItemsResource(@PathParam("container") String container, @PathParam("item") String item) {
        return new ContainerItemResource(container, item);
    }

    // Provide @GET, @PUT, @POST, @DELETE to get specific container items collection

    // Notice that path params are not redefined...
    @GET
    public ContainerItems getContainerItems() {
        Container c = MemoryStore.MS.getContainer(container);
        return c.getItems();
    }    

}


@Produces("application/xml")
public class ContainerItemResource {
    @Context UriInfo uriInfo;
    @Context Request request;
    String container;
    String item;

    // Constructor allowing it to be used as sub-resource
    ContainerItemResource(String container, String item) {
        this.container = container;
        this.item = item;
    }

    // Provide @GET, @PUT, @POST, @DELETE to get specific container item

    // Notice that path params are not redefined...
    @GET
    public ContainerItem getContainerItem() {
        Container c = MemoryStore.MS.getContainer(container);
        return c.getItem(item);
    }    

}

通过提供四个级别的示例深度资源使用子资源,希望它澄清您的代码正在做什么。子资源方法在整个资源中消除了重复的路径和路径参数定义,使代码更易于维护,并且(意见)更易于阅读。

By providing this example of four level deep resource using sub-resource, hopefully it clarifies what your code is doing. The sub-resource approach eliminates duplicated path and path param definitions through-out your resources making the code more maintainable and (opinion) easier to read.

这篇关于在这个例子中,使用子资源的PUT请求是如何由JAX-RS运行时处理的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆