2015-02-06 143 views
3

我已爲我的Jersey應用配置MEDIA_TYPE_MAPPINGS。不幸的是,這在我的應用程序中導致了一些通用上傳服務的麻煩。澤西島中的限制路徑媒體類型映射

@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擴展切碎的。

有沒有辦法告訴Jersey跳過該資源的過濾?

編輯:在peeskillet的回答後,我的假設得到了證實。我提出了一個改進請求:https://java.net/jira/browse/JERSEY-2780

回答

4

首先,這絕不是一個錯誤。這是預期的行爲。媒體類型映射的目的與處理文件無關,而是在設置標題可能不可用的情況下(例如在瀏覽器中)的內容協商的替代形式。

雖然沒有在官方規格中,但該功能是規格最終發佈之前的草稿的一部分。大多數實現決定以這種或那種方式包含它。澤西恰好讓你配置它。所以可以看到here in the spec in 3.7.1 Request Preprocessing

  • 設置

    • M = {config.getMediaTypeMappings().keySet()}
    • L = {config.getLanguageMappings().keySet()}
    • m = null
    • l = null
    • 其中,config是應用程序提供的ApplicationConfig子類的實例。
  • 對於每個延伸部在從右邊的最後路徑段掃描(一個.字符後跟一個或多個字母數字字符)e到左:

    • (a)拆下前導字符「」從e
    • (b)若mnulleM成員然後從有效請求URI中刪除相應的延伸並設置m = e
    • (c)否則,如果lnulleL的成員,則從有效請求URI中刪除相應的擴展並設置l = e。否則到步驟4
  • 如果m不爲空,則Accept首部的值設置爲config.getExtensionMappings().get(m)

  • 圖3(b)基本上是說該擴展應從被刪除所請求的URI和4表明應該有一些擴展映射將映射json(擴展名)映射到application/json,並將其設置爲Accept標題。你可以從不同的測試看,這種行爲

    @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

    如果我們註釋掉配置屬性,你會看到Accept頭有沒有已設置。

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

    話雖這麼說...

    在配置ServerProperties.MEDIA_TYPE_MAPPINGS,在org.glassfish.jersey.server.filter.UriConnegFilter是用於此功能的過濾器。您可以在source code in line 204 and 211,所述過濾器剝離擴展看到

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

    所以沒有辦法(至少據我可以查看源告訴)配置這一點,所以我們將不得不延長該類,覆蓋filter方法並至少取出實際進行替換的最後一行,然後註冊該過濾器。以下是我所做的工作。我簡單地複製和粘貼從過濾器的代碼,並註釋掉它將取代擴展

    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.json -X POST
    結果:file.json; Accept: application/json

    +1

    我知道這不是bug。好的分析,但。我先檢查過濾器,我的審查要求與你一樣。我唯一能做的就是提出一個改進請求。這是一個功能限制。 – 2015-02-09 14:52:23

    +0

    所以我重讀了你的問題,你想跳過那一個資源。我很好奇,但是決定性因素是什麼?我可以看到註釋的使用,但這需要另一個過濾器,因爲當前使用的過濾器是預先匹配的過濾器,所以我們還不能獲取資源信息。頭部是可能的,但這看起來不太優雅,客戶端必須知道/記得設置該頭部。 – 2015-02-10 09:16:00

    +0

    我雖然有一些註釋和預處理的想法。雖然,這是很多模糊。通過一個臨時頭管道配置並不理想,我不希望客戶改變任何事情。這純粹是服務器端問題。 – 2015-02-10 09:28:52