在Jersey中限制路径媒体类型映射 [英] Limit path media type mappings in Jersey

查看:241
本文介绍了在Jersey中限制路径媒体类型映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have configured MEDIA_TYPE_MAPPINGS for my Jersey apps. Unfortunately, this causes some trouble with a generic upload service in my app.

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

如果有人上传.../files/file.xml,则扩展名被砍掉.

If someone uploads .../files/file.xml the extension is chopped of.

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

Is there a way to tell Jersey to skip that filtering for this resource?

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

Edit: After peeskillet's answer, my assumption was confirmed. I have filed an improvement request: https://java.net/jira/browse/JERSEY-2780

推荐答案

首先,这绝不是错误.这是预期的行为.媒体类型映射的目的与使用文件无关,而是在无法使用设置标头的情况下(例如在浏览器中)使用内容协商的另一种形式.

First of all, this is in no way a bug. It is the expected behavior. The purpose of media type mappings is not related to working with files, but instead an alternative form of content negotiation for cases where setting headers may not be available, for instance in a browser.

尽管不在正式规范中,但此功能是规范最终发布之前草稿的一部分.大多数实现决定以一种或另一种方式包含它.泽西碰巧让您对其进行配置.因此可以参见此处位于3.7.1请求预处理中的规范

Though not in the official spec, this feature was part of a draft prior to the specs final release. Most implementations decided to include it one way or another. Jersey happens to let you configure it. So can see here in the spec in 3.7.1 Request Preprocessing

  1. 设置

  1. Set

  • M = {config.getMediaTypeMappings().keySet()}
  • L = {config.getLanguageMappings().keySet()}
  • m = null
  • l = null
  • 其中config是ApplicationConfig的应用程序提供的子类的实例.
  • M = {config.getMediaTypeMappings().keySet()}
  • L = {config.getLanguageMappings().keySet()}
  • m = null
  • l = null
  • Where config is an instance of the application-supplied subclass of ApplicationConfig.

对于最后一个路径段中的每个扩展名(.字符,后跟一个或多个字母数字字符),e从右向左扫描:

For each extension (a . character followed by one or more alphanumeric characters) e in the final path segment scanning from right to left:

  • (a)从e
  • 中删除开头的."字符
  • (b)如果mnull并且eM的成员,则从有效请求URI中删除相应的扩展名并设置m = e.
  • (c)否则,如果lnull并且eL的成员,则从有效请求URI中删除相应的扩展名并设置l = e.否则,请转到步骤4
  • (a) Remove the leading ‘.’ character from e
  • (b) If m is null and e is a member of M then remove the corresponding extension from the effective request URI and set m = e.
  • (c) Else if l is null and e is a member of L then remove the corresponding extension from the effective request URI and set l = e. Else go to step 4

如果m不为null,则将Accept标头的值设置为config.getExtensionMappings().get(m)

If m is not null then set the value of the Accept header to config.getExtensionMappings().get(m)

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

3(b) is basically saying that the extension should be removed from the requested URI and 4 is stating that there should be some extension mapping that would map say json (the extension) to application/json and set that as the Accept header. You can see from different tests, this behavior

@POST
@Path("/files/{file}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response doTest(@PathParam("file") String fileName, @Context HttpHeaders headers) {
    String accept = headers.getHeaderString(HttpHeaders.ACCEPT);
    return Response.ok(fileName + "; Accept: " + accept).build();
}
...

Map<String, MediaType> map = new HashMap<>();
map.put("xml", MediaType.APPLICATION_XML_TYPE);
resourceCnfig.property(ServerProperties.MEDIA_TYPE_MAPPINGS, map);

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

curl -v http://localhost:8080/api/mapping/files/file.xml -X POST
Result: file; Accept: application/xml

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

If we comment out that configuration property, you will see that the Accept header hasn't been set.

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

curl -v http://localhost:8080/api/mapping/files/file.xml -X POST
Result: file.xml; Accept: */**

话虽如此...

配置ServerProperties.MEDIA_TYPE_MAPPINGS时,

That being said...

When you configure the ServerProperties.MEDIA_TYPE_MAPPINGS, the org.glassfish.jersey.server.filter.UriConnegFilter is the filter used for this feature. You can see in the source code in line 204 and 211, where the filter is stripping the extension

path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();
...
rc.setRequestUri(uriInfo.getRequestUriBuilder().replacePath(path).build(new Object[0]));

所以没有办法配置它(至少就我看源代码而言),所以我们将不得不扩展该类,重写filter方法,并至少取出最后一个实际进行替换的行,然后注册过滤器.这是我为使其正常工作所做的.我只需从过滤器中复制并粘贴代码,然后在替换扩展名的行中注释掉

So there's no way to configure this (at least as far as I can tell from looking at the source), so we would have to extend that class, override the filter method and take out, at minimum, that last line that actually does the replacing, then register the filter. Here's what I did to get it to work. I simply copy and pasted the code from the filter, and commented out the line where it replaces the extension

import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.annotation.Priority;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.server.filter.UriConnegFilter;

@PreMatching
@Priority(3000)
public class MyUriConnegFilter extends UriConnegFilter {

    public MyUriConnegFilter(@Context Configuration config) {
        super(config);
    }

    public MyUriConnegFilter(Map<String, MediaType> mediaTypeMappings, 
                             Map<String, String> languageMappings) {
        super(mediaTypeMappings, languageMappings);
    }

    @Override
    public void filter(ContainerRequestContext rc)
            throws IOException {
        UriInfo uriInfo = rc.getUriInfo();

        String path = uriInfo.getRequestUri().getRawPath();
        if (path.indexOf('.') == -1) {
            return;
        }
        List<PathSegment> l = uriInfo.getPathSegments(false);
        if (l.isEmpty()) {
            return;
        }
        PathSegment segment = null;
        for (int i = l.size() - 1; i >= 0; i--) {
            segment = (PathSegment) l.get(i);
            if (segment.getPath().length() > 0) {
                break;
            }
        }
        if (segment == null) {
            return;
        }
        int length = path.length();

        String[] suffixes = segment.getPath().split("\\.");
        for (int i = suffixes.length - 1; i >= 1; i--) {
            String suffix = suffixes[i];
            if (suffix.length() != 0) {
                MediaType accept = (MediaType) this.mediaTypeMappings.get(suffix);
                if (accept != null) {
                    rc.getHeaders().putSingle("Accept", accept.toString());

                    int index = path.lastIndexOf('.' + suffix);
                    path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();
                    suffixes[i] = "";
                    break;
                }
            }
        }
        for (int i = suffixes.length - 1; i >= 1; i--) {
            String suffix = suffixes[i];
            if (suffix.length() != 0) {
                String acceptLanguage = (String) this.languageMappings.get(suffix);
                if (acceptLanguage != null) {
                    rc.getHeaders().putSingle("Accept-Language", acceptLanguage);

                    int index = path.lastIndexOf('.' + suffix);
                    path = new StringBuilder(path).delete(index, index + suffix.length() + 1).toString();
                    suffixes[i] = "";
                    break;
                }
            }
        }
        if (length != path.length()) {
            //rc.setRequestUri(uriInfo.getRequestUriBuilder().replacePath(path).build(new Object[0]));
        }
    }
}

然后配置它

Map<String, MediaType> map = new HashMap<>();
map.put("xml", MediaType.APPLICATION_XML_TYPE);
map.put("json", MediaType.APPLICATION_JSON_TYPE);
resourceConfig.register(new MyUriConnegFilter(map, null));

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

curl -v http://localhost:8080/api/mapping/files/file.xml -X POST
Result: file.xml; Accept: application/xml

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

curl -v http://localhost:8080/api/mapping/files/file.json -X POST
Result: file.json; Accept: application/json

这篇关于在Jersey中限制路径媒体类型映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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