泽西路径正则表达式导致PUT不允许使用

时间:2012-08-20 18:46:46

标签: java rest jersey

我正在测试泽西岛,当我打电话给405 Method Not Allowed时,我似乎无法弄清楚为什么会得到PUT

@Singleton @Path("images")
public class ImageResource {
    private static final String IMAGE_ID_PATH_PARAM = "{imageId : [A-Za-z0-9_\\-]+}";
    private static final String EXTENSION_PATH_PARAM = "{extension : (\\.[A-Za-z]+)?}";

    @GET @Path(IMAGE_ID_PATH_PARAM + EXTENSION_PATH_PARAM)
    @Produces("image/*")
    public Response getImage(@PathParam("imageId") String imageId,
        @PathParam("extension") String extension, @QueryParam("mods") String mods) {
        ...
    }

    @PUT @Path(IMAGE_ID_PATH_PARAM)
    @Consumes("image/*")
    public Response putImage(@PathParam("imageId") String imageId, File image) {
        ...
    }
}

PUT仅在我将@GET路径设置为@Path(IMAGE_ID_PATH_PARAM)时才有效。当我添加扩展部分时,我收到405状态代码。 GET似乎在两种情况下均有效。这是失败的PUT

的输出
$ curl -v --header "Content-Type: image/jpeg" --upload-file /Users/andy/Desktop/test.jpg http://localhost:9090/images/abcde
* About to connect() to localhost port 9090 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 9090 (#0)
> PUT /images/abcde HTTP/1.1
> User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5
> Host: localhost:9090
> Accept: */*
> Content-Type: image/jpeg
> Content-Length: 48198
> Expect: 100-continue
> 
< HTTP/1.1 100 Continue
< HTTP/1.1 405 Method Not Allowed
< Content-Type: text/html; charset=iso-8859-1
< Date: Mon, 20 Aug 2012 18:35:59 GMT
< Allow: GET,OPTIONS,HEAD
< Transfer-Encoding: chunked

我也尝试过没有@Produces@Consumes注释的测试,但它也没有用。

1 个答案:

答案 0 :(得分:3)

让我们来看看发送请求时会发生什么。

情况1:没有延期

您的方法如下所示:

    @GET @Path(IMAGE_ID_PATH_PARAM)
    @Produces("image/*")
    public Response getImage(@PathParam("imageId") String imageId,
        @PathParam("extension") String extension, @QueryParam("mods") String mods) {
        ...
    }

    @PUT @Path(IMAGE_ID_PATH_PARAM)
    @Consumes("image/*")
    public Response putImage(@PathParam("imageId") String imageId, File image) {
        ...
    }

发送以下请求时:

PUT http://localhost:9090/images/abcde

首先,Jersey查找具有相关URI的资源: http://localhost:9090/images/abcde

找到资源后,它会检查可以使用哪些方法来访问它。 在这种情况下,您有一个资源,其路径由IMAGE_ID_PATH_PARAM定义。可以通过GETPUT请求访问此资源。就像你用注释一样指定。

情况2:扩展名已添加到getImage

你的方法现在看起来像这样:

@GET @Path(IMAGE_ID_PATH_PARAM + EXTENSION_PATH_PARAM)
@Produces("image/*")
public Response getImage(@PathParam("imageId") String imageId,
    @PathParam("extension") String extension, @QueryParam("mods") String mods) {
    ...
}

@PUT @Path(IMAGE_ID_PATH_PARAM)
@Consumes("image/*")
public Response putImage(@PathParam("imageId") String imageId, File image) {
    ...
}

再次,您发送相同的请求: PUT http://localhost:9090/images/abcde

再一次,泽西岛找到了与URL匹配的第一个资源。资源由getImage方法表示,就像第一次一样。这次,@GET注释与您的请求不符。就像以前一样,Jersey试图找到另一种可用于该资源的方法,以便匹配您的请求。

但是,这次没有找到这样的方法,因此返回405。

发生这种情况的原因是方法getImageputImage现在代表不同的资源。如果仔细观察,可以像这样读取路径(为了清楚起见,我将省略正则表达式):

@Path({imageId}{extension})

getImage

@Path({imageId})

putImage

虽然考虑到正则表达式,这两条路径可以变成同样的东西,但泽西岛仍将它们视为单独资源的标识符。

如果您看一下WADL(如果您不熟悉标准的here,请随意查看)(由http://localhost:9090/application.wadl提供),您可以<application xmlns="http://research.sun.com/wadl/2006/10"> <resources base="http://localhost:9090/"> <resource path="images"> <resource path="{imageId : [A-Za-z0-9_\-]+}"> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="imageId" style="template" type="xs:string"/> <method id="putImage" name="PUT"> <request> <representation mediaType="image/*"/> </request> <response> <representation mediaType="*/*"/> </response> </method> </resource> <resource path="{imageId : [A-Za-z0-9_\-]+}{extension : (\.[A-Za-z]+)?}"> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="extension" style="template" type="xs:string"/> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="imageId" style="template" type="xs:string"/> <method id="getImage" name="GET"> <request> <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="mods" style="query" type="xs:string"/> </request> <response> <representation mediaType="image/*"/> </response> </method> </resource> </resource> </resources> </application> 请注意,这正是发生的事情。

images

注意EXTENSION_PATH_PARAM如何有两个独立的子资源,每个子资源都有一个方法。

解决方案

putImage段添加到@Path的{​​{1}}注释会导致这两种方法再次映射到单个资源,因此问题就会消失。由于正则表达式使该部分可选,因此您可以省略它并假装它不存在。

在生成的WADL中可以清楚地看到差异。

<application xmlns="http://research.sun.com/wadl/2006/10">
  <resources base="http://localhost:9090/">
    <resource path="images">
      <resource path="{imageId : [A-Za-z0-9_\-]+}{extension : (\.[A-Za-z]+)?}">
        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="extension" style="template" type="xs:string"/>
        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="imageId" style="template" type="xs:string"/>
        <method id="putImage" name="PUT">
          <request>
            <representation mediaType="image/*"/>
          </request>
          <response>
            <representation mediaType="*/*"/>
          </response>
        </method>
        <method id="getImage" name="GET">
         <request>
           <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="mods" style="query" type="xs:string"/>
         </request>
         <response>
           <representation mediaType="image/*"/>
         </response>
        </method>
      </resource>
    </resource>
  </resources>
</application>

在这种情况下,images只有一个子资源,而后者又有两种可用的方法。

就我个人而言,我发现WADL的自动生成是Jersey的一个很棒的功能。这是一种很好的方式,可以在不花费太多时间使用curl或其他REST客户端的情况下查看资源方法发生的情况。