限制泽西岛中的路径媒体类型映射

Mic*_*l-O 3 java mapping jersey media-type jersey-2.0

我已经配置了MEDIA_TYPE_MAPPINGS我的 Jersey 应用程序。不幸的是,这会给我的应用程序中的通用上传服务带来一些麻烦。

@PUT
@Path("files/{filename}")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response uploadFile(
    @PathParam("filename") @NotNull @Size(max = 240) String filename, DataSource dataSource)
Run Code Online (Sandbox Code Playgroud)

如果有人上传,.../files/file.xml扩展名就会被删除。

有没有办法告诉 Jersey 跳过对此资源的过滤?

编辑:在 peeskillet 的回答之后,我的假设得到了证实。我已提交改进请求:https ://java.net/jira/browse/JERSEY-2780

Pau*_*tha 5

首先,这绝不是一个错误。这是预期的行为。媒体类型映射的目的与处理文件无关,而是一种内容协商的替代形式,用于设置标头可能不可用的情况(例如在浏览器中)。

\n

尽管官方规范中没有此功能,但该功能是规范最终发布之前草案的一部分。大多数实现决定以某种方式包含它。Jersey 恰好让你配置它。所以可以在规范中看到3.7.1请求预处理

\n
\n
    \n
  1. \n
\n
\n
    \n
  • M={config.getMediaTypeMappings().keySet()}
  • \n
  • L={config.getLanguageMappings().keySet()}
  • \n
  • m=null
  • \n
  • l=null
  • \n
  • 其中 config 是应用程序提供的 ApplicationConfig 子类的实例。
  • \n
\n
\n
    \n
  1. 对于最终路径段中的每个扩展名(一个.字符后跟一个或多个字母数字字符),从右到左扫描:e
  2. \n
\n
\n
    \n
  • (a) 删除开头的 \xe2\x80\x98.\xe2\x80\x99 字符e
  • \n
  • (b) 如果mnull并且e是 的成员,M则从有效请求 URI 中删除相应的扩展并设置m = e
  • \n
  • (c) Else if lisnull并且e是 的成员,L则从有效请求 URI 中删除相应的扩展并设置l = e。否则转到步骤 4
  • \n
\n
\n
    \n
  1. 如果 m 不为空,则将 Accept 标头的值设置为config.getExtensionMappings().get(m)
  2. \n
\n
\n

3(b) 基本上是说应该从请求的 URI 中删除扩展名,而 4 则说明应该有一些扩展名映射将json(扩展名)映射到application/json并将其设置为Accept标头。您可以从不同的测试中看到这种行为

\n
@POST\n@Path("/files/{file}")\n@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})\npublic Response doTest(@PathParam("file") String fileName, @Context HttpHeaders headers) {\n    String accept = headers.getHeaderString(HttpHeaders.ACCEPT);\n    return Response.ok(fileName + "; Accept: " + accept).build();\n}\n...\n\nMap<String, MediaType> map = new HashMap<>();\nmap.put("xml", MediaType.APPLICATION_XML_TYPE);\nresourceCnfig.property(ServerProperties.MEDIA_TYPE_MAPPINGS, map);\n
Run Code Online (Sandbox Code Playgroud)\n
\n

curl -v http://localhost:8080/api/mapping/files/file.xml -X POST
\n结果: file; Accept: application/xml

\n
\n

如果我们注释掉该配置属性,您将看到Accept标头尚未设置。

\n
\n

curl -v http://localhost:8080/api/mapping/files/file.xml -X POST
\n结果: file.xml; Accept: */**

\n
\n

话虽如此...

\n

当您配置 时ServerProperties.MEDIA_TYPE_MAPPINGSorg.glassfish.jersey.server.filter.UriConnegFilter是用于此功能的过滤器。您可以在源代码第 162 和 179 行中看到,过滤器正在剥离扩展名

\n
path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();\n...\nrc.setRequestUri(uriInfo.getRequestUriBuilder().replacePath(path).build(new Object[0]));\n
Run Code Online (Sandbox Code Playgroud)\n

所以没有办法配置它(至少据我从源代码中可以看出),所以我们必须扩展该类,重写该filter方法并至少取出实际上的最后一行进行更换,然后注册过滤器。这就是我为让它发挥作用所做的事情。我只需复制并粘贴过滤器中的代码,并注释掉它替换扩展名的行

\n
import java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\nimport javax.annotation.Priority;\nimport javax.ws.rs.container.ContainerRequestContext;\nimport javax.ws.rs.container.PreMatching;\nimport javax.ws.rs.core.Configuration;\nimport javax.ws.rs.core.Context;\nimport javax.ws.rs.core.MediaType;\nimport javax.ws.rs.core.PathSegment;\nimport javax.ws.rs.core.UriInfo;\nimport org.glassfish.jersey.server.filter.UriConnegFilter;\n\n@PreMatching\n@Priority(3000)\npublic class MyUriConnegFilter extends UriConnegFilter {\n\n    public MyUriConnegFilter(@Context Configuration config) {\n        super(config);\n    }\n    \n    public MyUriConnegFilter(Map<String, MediaType> mediaTypeMappings, \n                             Map<String, String> languageMappings) {\n        super(mediaTypeMappings, languageMappings);\n    }\n\n    @Override\n    public void filter(ContainerRequestContext rc)\n            throws IOException {\n        UriInfo uriInfo = rc.getUriInfo();\n\n        String path = uriInfo.getRequestUri().getRawPath();\n        if (path.indexOf(\'.\') == -1) {\n            return;\n        }\n        List<PathSegment> l = uriInfo.getPathSegments(false);\n        if (l.isEmpty()) {\n            return;\n        }\n        PathSegment segment = null;\n        for (int i = l.size() - 1; i >= 0; i--) {\n            segment = (PathSegment) l.get(i);\n            if (segment.getPath().length() > 0) {\n                break;\n            }\n        }\n        if (segment == null) {\n            return;\n        }\n        int length = path.length();\n\n        String[] suffixes = segment.getPath().split("\\\\.");\n        for (int i = suffixes.length - 1; i >= 1; i--) {\n            String suffix = suffixes[i];\n            if (suffix.length() != 0) {\n                MediaType accept = (MediaType) this.mediaTypeMappings.get(suffix);\n                if (accept != null) {\n                    rc.getHeaders().putSingle("Accept", accept.toString());\n\n                    int index = path.lastIndexOf(\'.\' + suffix);\n                    path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();\n                    suffixes[i] = "";\n                    break;\n                }\n            }\n        }\n        for (int i = suffixes.length - 1; i >= 1; i--) {\n            String suffix = suffixes[i];\n            if (suffix.length() != 0) {\n                String acceptLanguage = (String) this.languageMappings.get(suffix);\n                if (acceptLanguage != null) {\n                    rc.getHeaders().putSingle("Accept-Language", acceptLanguage);\n\n                    int index = path.lastIndexOf(\'.\' + suffix);\n                    path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();\n                    suffixes[i] = "";\n                    break;\n                }\n            }\n        }\n        if (length != path.length()) {\n            //rc.setRequestUri(uriInfo.getRequestUriBuilder().replacePath(path).build(new Object[0]));\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然后配置一下

\n
Map<String, MediaType> map = new HashMap<>();\nmap.put("xml", MediaType.APPLICATION_XML_TYPE);\nmap.put("json", MediaType.APPLICATION_JSON_TYPE);\nresourceConfig.register(new MyUriConnegFilter(map, null));\n
Run Code Online (Sandbox Code Playgroud)\n
\n

curl -v http://localhost:8080/api/mapping/files/file.xml -X POST
\n结果: file.xml; Accept: application/xml

\n
\n
\n

curl -v http://localhost:8080/api/mapping/files/file.json -X POST
\n结果: file.json; Accept: application/json

\n
\n