2017-03-01 120 views
1

我的Web服務API的所有端點都允許以多種格式(如XML和Json)返回響應。響應格式由查詢參數就像這個請求URI的決定:與澤西島查詢參數相關的動態響應格式?

https://example.com/rest/countries?format=xml 
https://example.com/rest/countries?format=json 

我所有的端點rurrently實現就像這個例子類似:

@GET 
@Path("/countries") 
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) 
public Response getCountries(@QueryParam("format") String format) { 

    if(format.equalsIgnoreCase("xml")) { 
     Countries countries = getResponseFromSomewhere(); 
     String xml = toXmlWithJaxB(countries); 
     return Response.ok(xml, MediaType.APPLICATION_XML).build(); 
    } 
    else if(format.equalsIgnoreCase("json")) { 
     Countries countries = getResponseFromSomewhere(); 
     String json = toJsonWithJackson(countries); 
     return Response.ok(json, MediaType.APPLICATION_JSON).build(); 
    } 
    else { 
     return Response.status(415).entity("Invalid format"); 
    } 
} 

是否有此問題的一個更通用的解決方案,以便我不必手動檢查和處理每個端點中的格式?傑克遜或澤西可能已經提供瞭解決方案嗎?

回答

2

,而無需執行任何定製,澤西島,檢查「擴展名」的格式UriConnegFilter。例如

https://example.com/rest/countries.xml 
https://example.com/rest/countries.json 

您需要配置映射

final Map<String, MediaType> mappings = new HashMap<>(); 
mappings.put("json", MediaType.APPLICATION_JSON_TYPE); 
mappings.put("xml", MediaType.APPLICATION_XML_TYPE); 

return new ResourceConfig() 
     .property(ServerProperties.MEDIA_TYPE_MAPPINGS, mappings); 

這裏我們只是映射擴展到媒體類型。澤西島將完成剩下的工作。

如果你真的要堅持只能使用查詢參數,那麼你可以做的是寫一個@PreMatchingContainerRequestFilter在那裏你會檢查查詢參數,然後相應地設置Accept頭。

@Provider 
@PreMatching 
@Priority(3000) 
public static class QueryConnegFilter implements ContainerRequestFilter { 

    private static final Map<String, String> mappings; 

    static { 
     Map<String, String> map = new HashMap<>(); 
     map.put("xml", MediaType.APPLICATION_XML); 
     map.put("json", MediaType.APPLICATION_JSON); 
     mappings = Collections.unmodifiableMap(map); 
    } 

    @Override 
    public void filter(ContainerRequestContext request) throws IOException { 
     final String format = request.getUriInfo().getQueryParameters().getFirst("format"); 
     if (format != null) { 
      final String mediaType = mappings.get(format); 
      if (mediaType != null) { 
       request.getHeaders().putSingle(HttpHeaders.ACCEPT, mediaType); 
      } 
     } 
    } 
} 

然後你只需要註冊它與你的應用程序。現在你可以做

https://example.com/rest/countries?format=xml 
https://example.com/rest/countries?format=json 

這裏是上面

import org.glassfish.jersey.filter.LoggingFilter; 
import org.glassfish.jersey.server.ResourceConfig; 
import org.glassfish.jersey.server.ServerProperties; 
import org.glassfish.jersey.test.JerseyTest; 
import org.junit.Test; 

import javax.annotation.Priority; 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import javax.ws.rs.container.ContainerRequestContext; 
import javax.ws.rs.container.ContainerRequestFilter; 
import javax.ws.rs.container.PreMatching; 
import javax.ws.rs.core.HttpHeaders; 
import javax.ws.rs.core.MediaType; 
import javax.ws.rs.ext.Provider; 
import javax.xml.bind.annotation.XmlRootElement; 
import java.io.IOException; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.logging.Logger; 

import static org.hamcrest.CoreMatchers.containsString; 
import static org.hamcrest.CoreMatchers.is; 
import static org.hamcrest.MatcherAssert.assertThat; 

/** 
* Run it like any other JUnit test. Only two required dependencies: 
* 
* <dependency> 
* <groupId>org.glassfish.jersey.test-framework.providers</groupId> 
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId> 
* <version>${jersey2.version}</version> 
* </dependency> 
* <dependency> 
* <groupId>org.glassfish.jersey.media</groupId> 
* <artifactId>jersey-media-json-jackson</artifactId> 
* <version>${jersey2.version}</version> 
* </dependency> 
* 
* @author Paul Samsotha. 
*/ 
public class UriConnegTests extends JerseyTest { 

    @XmlRootElement 
    public static class Model { 
     private String message; 
     public Model() {} 
     public Model(String message) { this.message = message; } 
     public String getMessage() { return this.message; } 
     public void setMessage(String message) { this.message = message; } 
    } 


    @Path("test") 
    public static class TestResource { 
     @GET 
     @Produces({"application/json", "application/xml"}) 
     public Model get() { 
      return new Model("Hello World"); 
     } 
    } 

    @Provider 
    @PreMatching 
    @Priority(3000) 
    public static class QueryConnegFilter implements ContainerRequestFilter { 

     private static final Map<String, String> mappings; 

     static { 
      Map<String, String> map = new HashMap<>(); 
      map.put("xml", MediaType.APPLICATION_XML); 
      map.put("json", MediaType.APPLICATION_JSON); 
      mappings = Collections.unmodifiableMap(map); 
     } 

     @Override 
     public void filter(ContainerRequestContext request) throws IOException { 
      final String format = request.getUriInfo().getQueryParameters().getFirst("format"); 
      if (format != null) { 
       final String mediaType = mappings.get(format); 
       if (mediaType != null) { 
        request.getHeaders().putSingle(HttpHeaders.ACCEPT, mediaType); 
       } 
      } 
     } 
    } 

    @Override 
    public ResourceConfig configure() { 
     final Map<String, MediaType> mappings = new HashMap<>(); 
     mappings.put("json", MediaType.APPLICATION_JSON_TYPE); 
     mappings.put("xml", MediaType.APPLICATION_XML_TYPE); 
     return new ResourceConfig() 
       .register(TestResource.class) 
       .register(QueryConnegFilter.class) 
       .register(new LoggingFilter(Logger.getAnonymousLogger(), true)) 
       .property(ServerProperties.MEDIA_TYPE_MAPPINGS, mappings); 
    } 

    @Test 
    public void returnsXmlFromExtension() { 
     final String expected = "<message>Hello World</message>"; 
     final String data = target("test.xml") 
       .request() 
       .get(String.class); 
     assertThat(data, containsString(expected)); 
    } 

    @Test 
    public void returnsJsonFromExtension() { 
     final String expected = "{\"message\":\"Hello World\"}"; 
     final String data = target("test.json") 
       .request() 
       .get(String.class); 
     assertThat(data, is(expected)); 
    } 

    @Test 
    public void returnsXmlFromQuery() { 
     final String expected = "<message>Hello World</message>"; 
     final String data = target("test") 
       .queryParam("format", "xml") 
       .request() 
       .get(String.class); 
     assertThat(data, containsString(expected)); 
    } 

    @Test 
    public void returnsJsonFromQuery() { 
     final String expected = "{\"message\":\"Hello World\"}"; 
     final String data = target("test") 
       .queryParam("format", "json") 
       .request() 
       .get(String.class); 
     assertThat(data, is(expected)); 
    } 
} 
1

Jersey允許將對象轉換爲xml/json。

  1. 與XmlRootElement將標註您的國家豆
  2. 傳遞註釋對象的實體法,讓球衣構建它
  3. 你需要球衣JSON的依賴添加到類路徑
  4. 客戶端在這種情況下,應該指定他在頭中接受的內容,而不再需要查詢參數。即accept:application/json或application/xml

    Countries countries = getResponseFromSomewhere();
    Response.ok()。entity(countries).build();

+0

需要作爲請求參數傳遞的格式列出的兩個選項一個完整的工作測試。傳遞標題內的格式不適合我。 – eztam

+1

沒有使用標題,我認爲你已經得到它。您可以進一步將toxml/tojson方法抽象爲通用「toFormat」方法背後的實用程序。這將推回邏輯並使代碼在服務類中看起來更清潔。或者,也許這篇文章會幫助http://stackoverflow.com/questions/20212405/how-to-set-accept-header-on-the-server-side-in-jersey-1-x-for-an-incoming- reques。 – caburse