【问题标题】:Jersey Returning a 405 on a PUTJersey 在 PUT 上返回 405
【发布时间】:2015-10-05 02:19:50
【问题描述】:

我在使用嵌入式 Jersey 设置和测试 REST 调用时遇到问题。我在 PUT 请求中不断收到 405。

这里有一些请求。我使用了除一个之外的所有查询参数,它有一个默认值。这些 URL 是由我的测试客户端中的 Jersey WebResource 生成的。

curl -i -X PUT "http://localhost:8080/hash/wes?hiddenDirs=false&hiddenFiles=false&id=3edf4867-1211-4ae7-a251-2af28ffac5a2&recursive=false&rehash=false&hashType=SHA1"

curl -i -X PUT "http://localhost:8080/hash/wes?hiddenDirs=false&hiddenFiles=false&id=3edf4867-1211-4ae7-a251-2af28ffac5a2&recursive=false&rehash=false"

这是 curl 的基本响应。它不会列出 PUT,但使用 OPTIONS 请求会列出 PUT。

HTTP/1.1 405 Method Not Allowed
Allow: GET,OPTIONS,HEAD
Content-Length: 0
Server: Jetty(8.1.17.v20150415)

这里是控制器类。我使用了很多常量,因此客户端和服务使用相同的值。您可以在下面的 OPTIONS 输出中看到翻译。

@Path(Constants.HASH_PATH) // "/hash"
public class HashController {

这是我希望处理呼叫的方法。

@PUT
@Path(Constants.PATH_REST) // "{path : .*}" - I want this to accept directory paths.
@Produces(MediaType.APPLICATION_JSON)
public Response hash(@PathParam("path") String path, @QueryParam(Constants.ID_PARAM) String id,
    @QueryParam(Constants.HASH_TYPE_PARAM) @DefaultValue(HashProcessor.DEFAULT_HASH_TYPE) String hashType,
    @QueryParam(Constants.HIDDEN_DIRS_PARAM) @DefaultValue("false") boolean hiddenDirectories,
    @QueryParam(Constants.HIDDEN_FILES_PARAM) @DefaultValue("false") boolean hiddenFiles,
    @QueryParam(Constants.RECURSIVE_PARAM) @DefaultValue("false") boolean recursive,
    @QueryParam(Constants.REHASH_PARAM) @DefaultValue("false") boolean reHashExisting) {
  Response ret = null;

我使用了一个 OPTIONS 请求来获取控制器的详细信息并查看定义的 PUT。

curl -i -X OPTIONS http://localhost.corp.int:8080/hash > fileSync-hash.xml

这是输出。

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://wadl.dev.java.net/2009/02">
  <doc xmlns:jersey="http://jersey.java.net/" jersey:generatedBy="Jersey: 1.19 02/11/2015 03:25 AM" />
  <grammars>
    <include href="http://localhost:8080/application.wadl/xsd0.xsd">
      <doc title="Generated" xml:lang="en" />
    </include>
  </grammars>
  <resources base="http://localhost:8080/">
    <resource path="hash">
      <resource path="{path : .*}">
        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="path" style="template" type="xs:string" />
        <method id="hash" name="PUT">
          <request>
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="id" style="query" type="xs:string" />
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="hashType" style="query" type="xs:string" default="SHA1" />
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="hiddenDirs" style="query" type="xs:boolean" default="false" />
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="hiddenFiles" style="query" type="xs:boolean" default="false" />
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="recursive" style="query" type="xs:boolean" default="false" />
            <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="rehash" style="query" type="xs:boolean" default="false" />
          </request>
          <response>
            <representation mediaType="application/json" />
          </response>
        </method>
      </resource>
      <resource path="{id : [\w\-]*}">
        <param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="id" style="template" type="xs:string" />
        <method id="status" name="GET">
          <response>
            <representation mediaType="application/json" />
          </response>
        </method>
      </resource>
    </resource>
  </resources>
</application>

这是调用的服务器输出。

2015-07-15 15:52:36 DEBUG nio:843 - created SCEP@6319a8b7{l(/10.20.67.195:56641)<->r(/10.20.67.71:8080),s=0,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0}-{AsyncHttpConnection@2993b935,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-14,l=0,c=0},r=0}
2015-07-15 15:52:36 DEBUG HttpParser:281 - filled 271/271
2015-07-15 15:52:36 DEBUG Server:365 - REQUEST /hash/wes on AsyncHttpConnection@2993b935,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-5,l=3,c=0},r=1
2015-07-15 15:52:36 DEBUG ContextHandler:942 - scope null||/hash/wes @ o.e.j.s.ServletContextHandler{/,null}
2015-07-15 15:52:36 DEBUG ContextHandler:1014 - context=||/hash/wes @ o.e.j.s.ServletContextHandler{/,null}
2015-07-15 15:52:36 DEBUG ServletHandler:415 - servlet ||/hash/wes -> com.sun.jersey.spi.container.servlet.ServletContainer-68e816f5
2015-07-15 15:52:36 DEBUG ServletHandler:477 - chain=null
2015-07-15 15:52:36 DEBUG Server:367 - RESPONSE /hash/wes  405 handled=true
2015-07-15 15:52:36 DEBUG AsyncHttpConnection:211 - Enabled read interest SCEP@6319a8b7{l(/10.20.67.195:56641)<->r(/10.20.67.71:8080),s=1,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@2993b935,g=HttpGenerator{s=4,h=0,b=-1,c=-1},p=HttpParser{s=0,l=3,c=0},r=1}
2015-07-15 15:52:36 DEBUG ChannelEndPoint:118 - ishut SCEP@6319a8b7{l(/10.20.67.195:56641)<->r(/10.20.67.71:8080),s=1,open=true,ishut=false,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@2993b935,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=-14,l=0,c=-3},r=1}
2015-07-15 15:52:36 DEBUG HttpParser:281 - filled -1/0
2015-07-15 15:52:36 DEBUG AsyncHttpConnection:145 - Disabled read interest while writing response SCEP@6319a8b7{l(/10.20.67.195:56641)<->r(/10.20.67.71:8080),s=1,open=true,ishut=true,oshut=false,rb=false,wb=false,w=true,i=0r}-{AsyncHttpConnection@2993b935,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=0,l=0,c=-3},r=1}
2015-07-15 15:52:36 DEBUG ChannelEndPoint:209 - close SCEP@6319a8b7{l(/10.20.67.195:56641)<->r(/10.20.67.71:8080),s=1,open=true,ishut=true,oshut=false,rb=false,wb=false,w=true,i=0!}-{AsyncHttpConnection@2993b935,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=0,l=0,c=-3},r=1}
2015-07-15 15:52:36 DEBUG nio:852 - destroyEndPoint SCEP@6319a8b7{l(null)<->r(0.0.0.0/0.0.0.0:8080),s=0,open=false,ishut=true,oshut=true,rb=false,wb=false,w=true,i=0!}-{AsyncHttpConnection@2993b935,g=HttpGenerator{s=0,h=-1,b=-1,c=-1},p=HttpParser{s=0,l=0,c=-3},r=1}

我可以对同一个控制器进行 GET 调用。由于 PUT 失败,它按预期返回 404,但返回 404 而不是 Jersey 的是我的代码。

curl -i -X GET "http://dsk-107312:8080/hash/3edf4867-1211-4ae7-a251-2af28ffac5a2"

这是一些服务器输出,因为 Jersey 会根据第一个请求进行自我配置。如果 Jersey 没有发现控制器,GET 也会失败。

Jul 15, 2015 3:46:13 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
  com.wstrater.server.fileSync.server.handlers
Jul 15, 2015 3:46:13 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
  class com.wstrater.server.fileSync.server.handlers.FileController
  class com.wstrater.server.fileSync.server.handlers.HelloWorldController
  class com.wstrater.server.fileSync.server.handlers.HashController
  class com.wstrater.server.fileSync.server.handlers.DirectoryController
Jul 15, 2015 3:46:13 PM com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
Jul 15, 2015 3:46:14 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.19 02/11/2015 03:25 AM'

我已经在其他控制器中成功使用了 PUT,所以它一定很简单。事实上,我希望我能偶然发现写这篇文章的频率如此之高,但这次不会。

谢谢,韦斯。

【问题讨论】:

    标签: java rest jersey


    【解决方案1】:

    在我看来,Jersey (2.19) 在运行您的路由示例时存在错误。 我试过这个控制器模仿你的例子:

    @Path("/hash")
    public class HashController
    {
        @PUT @Produces("text/plain")
        @Path("{path: .+}")
        public String m1(@PathParam("path") String path) {
            return "m1 called with path " + path;
        }
    
        @GET @Produces("text/plain")
        @Path("{path: [\\w\\-]+}")
        public String m2(@PathParam("path") String path) {
            return "m2 called with path " + path;
        }
    }
    

    运行 CURL 时,我得到与您报告的相同的行为:GET 请求被罚款,PUT 返回 405。

    但是,当您在两种方法上使用相同的路径模板时(即在两种方法上使用@Path("{path: .+}")@Path("{path: [\\w\\-]+}"),一切正常。

    祝你好运。

    【讨论】:

    • 看起来这就是问题所在。我猜泽西岛不喜欢同一个控制器中的不同正则表达式模式。谢谢。
    【解决方案2】:

    路径“/hash/wes”匹配您的路径方案“/hash/{path : .}”和“/hash/{id : [\w-]}”和jersey 似乎选择了第二种控制器方法——它支持 GET 但不支持 PUT。

    编辑:根据spec (3.7.2) 这不应该发生,所以要么我的理论是错误的,要么是泽西岛的一个错误。

    【讨论】:

    • Method 应该在两个类方法之间有所不同。我发现有趣的是Allow 与 405 返回的列表没有列出 PUT,但 WADL 列出了。
    • 是的,方法应该有所作为。 Allow 返回的 GET 非常适合其他控制器方法,因此是我最初的想法。您不能通过第二个控制器方法允许“PUT”方法,看看会发生什么?
    • 我不确定我还能做些什么来允许 PUT,也许这就是我所缺少的。我有使用 PUT 注释的方法,并且 OPTIONS 请求确实返回了 PUT 的详细信息。我会怀疑该方法的路径中的正则表达式,但在其他控制器中成功使用该常量。我需要正则表达式,因为我试图允许包含子目录的目录路径。默认行为是只返回路径分隔符之间的内容,我想要包括路径分隔符在内的所有内容。
    猜你喜欢
    • 2012-08-29
    • 2014-10-04
    • 1970-01-01
    • 2014-09-03
    • 2014-04-09
    • 2012-03-14
    • 1970-01-01
    • 2012-07-18
    • 2020-12-15
    相关资源
    最近更新 更多