我的网络服务 API 的所有端点都允许以 XML 和 Json 等多种格式返回响应。响应格式由查询参数决定,就像在这个请求 URI 中一样:
https://example.com/rest/countries?format=xml
https://example.com/rest/countries?format=json
我所有的端点都像这个例子中的一样快速实现:
@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");
}
}
这个问题是否有更通用的解决方案,这样我就不必手动检查和处理每个端点中的格式? Jackson 或 Jersey 是否已经为此提供了解决方案?
最佳答案
无需执行任何自定义操作,Jersey 就有 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);
这里我们只是将扩展名映射到媒体类型。 Jersey 将完成剩下的工作。
如果你真的想坚持只使用查询参数,那么你可以做的是编写一个 @PreMatching
ContainerRequestFilter
来检查查询参数,然后相应地设置 Accept
header 。
@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));
}
}
关于java - 动态响应格式依赖于 Jersey 的查询参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42541267/