JAX-RS(Jersey 2 实现)内容协商,带有 URL 扩展名 .xml 或 .json
Posted
技术标签:
【中文标题】JAX-RS(Jersey 2 实现)内容协商,带有 URL 扩展名 .xml 或 .json【英文标题】:JAX-RS (Jersey 2 implementation) content negotiation with URL extension .xml or .json 【发布时间】:2018-05-08 20:05:14 【问题描述】:我见过一个 Java RESTFUL 网络服务,它允许在 URL 中请求内容类型,并在末尾带有扩展名,例如
.xml
.json
这是我在自己的 Web 服务中努力实现的内容协商风格。
我知道@Produces
注释,并且一个方法可以使用(value = )
语法解析多种类型,方法是添加一个Accept 标头,例如使用Chrome 扩展程序Postman。
但我不确定如何在一种方法中有效地提取信息,并委托给另一种方法。
我假设 REGEX 可以与 @Path
和 @PathParam
一起使用,但我这样做的尝试尚未取得成果。
谁能举个例子?
这是我迄今为止的尝试:
package com.extratechnology.caaews;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.extratechnology.caaews.model.Log;
@Path("et")
@Produces(MediaType.APPLICATION_JSON)
public class CAAEWS
@GET
@Path("\\.format")
@Produces(value = MediaType.APPLICATION_JSON, MediaType.TEXT_XML)
public Log getLog(
@PathParam("format") String format
)
Log result = null;
switch (format)
case "json":
result = this.getJSON();
case "xml":
result = this.getXML();
return result;
@GET
@Produces(MediaType.APPLICATION_JSON)
public Log getJSON()
return new Log("JSON!");
@GET
@Produces(MediaType.TEXT_XML)
public Log getXML()
return new Log("XML!");
package com.extratechnology.caaews.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Log
private String log;
public Log(String log)
this.log = log;
public String getLog()
return log;
public void setLog(String log)
this.log = log;
可以通过 Spring Tool Suite/Eclipse 设置项目,方法是使用以下命令创建一个 Maven 项目(类似,但比 here circa 4:50 更新):
org.glassfish.jersey.archetypes jersey.quickstart.webapp 2.26然后取消注释为启用 JSON 支持而提供的 pom.xml 部分,这有效地为您的 WAR 添加了更多 JARS。
我发现我也有一些令人讨厌的 BCEL 错误,并且不得不在 catalina.properties 文件的键下附加一些条目:
tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\
....
javax.json-api-1.1.jar, javax.json.bind-api-1.0.jar, javax.json-1.1.jar, \
yasson-1.0.jar
http://localhost:18080/caaews/webapi/et
产量:
"log":"JSON!"
http://localhost:18080/caaews/webapi/et.xml 或
http://localhost:18080/caaews/webapi/et.json
产量:
HTTP Status 404 - Not Found
我也想知道是否有某种 HTTP 拦截器类型的方法来解决这个问题。我的 Java 有点生疏了,但它是 servlet 过滤器,还是类似于 AOP 之前的建议。
感谢@user1803551,我在 switch 语句中添加了中断。
感谢@callmepills,我稍微调整了代码。
类级别的@Path 注解现在有了这个。 @Produces(value = MediaType.APPLICATION_JSON, MediaType.TEXT_XML)
getLog @Path 注解是“.format”。
为了调用和委托 getLog,您必须对 URL 使用以下语法:
http://localhost:18080/caaews/webapi/et
http://localhost:18080/caaews/webapi/et/.xml
http://localhost:18080/caaews/webapi/et/.json
在路径中需要一个“/”不是我所追求的,所以我想我可能不得不解决 servlet 过滤器而不是 @PathParam 方法..
【问题讨论】:
抱歉跑题了,但是恕我直言……对 xml 和 json 的支持通常是浪费时间。此外,为此目的在 URL 中使用某种后缀是更大的错误。 提供示例输入和您的尝试。见minimal reproducible example。本网站不是编码服务。 @user1803551 - 你没有给我机会!无论如何要去! 没给你机会?您应该在发布之前准备好问题,而不是通过编辑不断地添加细节。 注意:case
没有break
的语句。启用标准 Java 编译器警告并注意它们,您将不会再犯此错误。
【参考方案1】:
当我搜索“带有 jax-rs 示例的 servlet 过滤器”时,this 位于列表顶部。粗略扫描一下代码,我认为这符合我的需求。
这是我的解决方案(到目前为止...见脚注警告)
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<filter>
<filter-name>accept-filter</filter-name>
<filter-class>com.extratechnology.filters.AcceptFilter</filter-class>
<init-param>
<param-name>xml</param-name>
<param-value>text/xml</param-value>
</init-param>
<init-param>
<param-name>json</param-name>
<param-value>application/json</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>accept-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.extratechnology.caaews</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
</web-app>
AcceptFilter.java
package com.extratechnology.filters;
import java.io.IOException;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class AcceptFilter implements Filter
private final Map<String,String> extensions = new HashMap<String,String>();
public void init(FilterConfig config) throws ServletException
Enumeration<String> exts = config.getInitParameterNames();
while (exts.hasMoreElements())
String ext = exts.nextElement();
if (ext != null && !ext.isEmpty())
this.extensions.put(ext.toLowerCase(), config.getInitParameter(ext));
public void destroy()
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
HttpServletRequest httpRequest = (HttpServletRequest)request;
String uri = httpRequest.getRequestURI();
String ext = this.getExtension(uri);
String accept = this.extensions.get(ext);
if (accept == null)
accept = httpRequest.getHeader("accept");
if (accept != null && accept.indexOf("text/html") > 0)
// patch WebKit-style Accept headers by elevating "text/html"
accept = "text/html,"+accept;
request = new RequestWrapper(httpRequest, uri, accept);
else
// remove extension and remap the Accept header
uri = uri.substring(0, uri.length() - ext.length()-1);
request = new RequestWrapper(httpRequest, uri, accept);
// add "Vary: accept" to the response headers
HttpServletResponse httpResponse = (HttpServletResponse)response;
httpResponse.addHeader("Vary", "accept");
chain.doFilter(request, response);
private String getExtension(String path)
String result = "";
int index = path.lastIndexOf('.');
if (!(index < 0 || path.lastIndexOf('/') > index))
result = path.substring(index+1).toLowerCase();
return result;
private static class RequestWrapper extends HttpServletRequestWrapper
private final String uri;
private final String accept;
public RequestWrapper(HttpServletRequest request, String uri, String accept)
super(request);
this.uri = uri;
this.accept = accept;
@Override
public String getRequestURI()
return this.uri;
@Override
public Enumeration<String> getHeaders(String name)
Enumeration<String> result;
if ("accept".equalsIgnoreCase(name))
Vector<String> values = new Vector<String>(1);
values.add(this.accept);
result = values.elements();
else
result = super.getHeaders(name);
return result;
CAAEWS.java
package com.extratechnology.caaews;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.extratechnology.caaews.model.Log;
@Path("et")
@Produces(value = MediaType.APPLICATION_JSON, MediaType.TEXT_XML)
public class CAAEWS
@GET
@Produces(MediaType.APPLICATION_JSON)
public Log getJSON()
return new Log("JSON!");
@GET
@Produces(MediaType.TEXT_XML)
public Log getXML()
return new Log("XML!");
Log.java
package com.extratechnology.caaews.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Log
private String log;
public Log(String log)
this.log = log;
public String getLog()
return log;
public void setLog(String log)
this.log = log;
唯一让我感兴趣的是 HTTP 有两种 XML 内容类型。
文本/xml 应用程序/xml它可以在 web.xml 中配置,但我必须调整注释。为什么是两个?
--
脚注:
写完这篇文章后,我发现我收到了 HTTP 500 错误。 当您在 Eclipse 中运行服务器时,日志似乎位于某个不起眼的文件夹中:
Documents\workspace-sts-3.8.3.RELEASE\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\logs
我把这个写到日志中:
0:0:0:0:0:0:0:1 - - [25/Nov/2017:16:56:00 +0000] "GET /caaews/webapi/et.xml HTTP/1.1" 500 1082
有没有人知道如何获取更合理的日志信息?或者我需要做什么来捕获更有意义的堆栈跟踪?
看来 Log 类需要一个无参数构造函数来克服这个问题。但我承认,@peeskillet 的答案远没有那么繁琐,而且使用了 Jersey 的内置功能。
查看示例here...
根据此问题的其他相关答案/cmets,我最终实现了一个异常处理程序,因此您可以获得有关 Jersey 中 HTTP 500 消息的更多信息..
这里的代码有助于指向需要无参数构造函数的 Log.java..
错误消息
package com.extratechnology.caaews.model;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class ErrorMessage
private String errorMessage;
private String errorStackTrace;
private String cause;
private String causeStackTrace;
private int errorCode;
public ErrorMessage()
public ErrorMessage(
String errorMessage,
String errorStackTrace,
String cause,
String causeStackTrace,
int errorCode
)
this.errorMessage = errorMessage;
this.errorStackTrace = errorStackTrace;
this.cause = cause;
this.causeStackTrace = causeStackTrace;
this.errorCode = errorCode;
public String getErrorMessage()
return errorMessage;
public void setErrorMessage(String errorMessage)
this.errorMessage = errorMessage;
public String getErrorStackTrace()
return errorStackTrace;
public void setErrorStackTrace(String errorStackTrace)
this.errorStackTrace = errorStackTrace;
public String getCause()
return cause;
public void setCause(String cause)
this.cause = cause;
public String getCauseStackTrace()
return causeStackTrace;
public void setCauseStackTrace(String causeStackTrace)
this.causeStackTrace = causeStackTrace;
public int getErrorCode()
return errorCode;
public void setErrorCode(int errorCode)
this.errorCode = errorCode;
GenericExceptionMapper.java
package com.extratechnology.caaews.exception;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import com.extratechnology.caaews.model.ErrorMessage;
@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable>
@Override
public Response toResponse(Throwable ex)
System.out.println("Stack Trace:");
ex.printStackTrace();
System.out.println("Cause:");
Throwable cause = ex.getCause();
if (cause != null)
cause.printStackTrace();
ErrorMessage message = new ErrorMessage(
ex.getMessage(),
GenericExceptionMapper.getStackTrack(ex),
cause.getMessage(),
GenericExceptionMapper.getStackTrack(cause),
Status.INTERNAL_SERVER_ERROR.getStatusCode()
);
return Response
.status(Status.INTERNAL_SERVER_ERROR)
.entity(message)
.build();
private static String getStackTrack(Throwable ex)
StringBuilder sb = new StringBuilder();
String ls = System.lineSeparator();
if (ex != null)
StackTraceElement[] steAll = ex.getStackTrace();
for (StackTraceElement ste : steAll)
sb.append(ste.toString());
sb.append(ls);
return sb.toString();
system.out.println 会在调试时提供控制台消息,并且您也会在出现错误时在 Web 浏览器中返回有效负载。
例如:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<errorMessage>
<cause>1 counts of IllegalAnnotationExceptions</cause>
<causeStackTrace>
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(Unknown Source) com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(Unknown Source) com.sun.xml.internal.bind.v2.ContextFactory.createContext(Unknown Source) sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source) sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) java.lang.reflect.Method.invoke(Unknown Source) javax.xml.bind.ContextFinder.newInstance(Unknown Source) javax.xml.bind.ContextFinder.newInstance(Unknown Source) javax.xml.bind.ContextFinder.find(Unknown Source) javax.xml.bind.JAXBContext.newInstance(Unknown Source) javax.xml.bind.JAXBContext.newInstance(Unknown Source) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getStoredJaxbContext(AbstractJaxbProvider.java:312) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getJAXBContext(AbstractJaxbProvider.java:297) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:264) org.glassfish.jersey.jaxb.internal.AbstractJaxbProvider.getMarshaller(AbstractJaxbProvider.java:231) org.glassfish.jersey.jaxb.internal.AbstractRootElementJaxbProvider.writeTo(AbstractRootElementJaxbProvider.java:175) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:266) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:251) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:109) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:85) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1135) org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:662) org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:395) org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:385) org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:280) org.glassfish.jersey.internal.Errors$1.call(Errors.java:272) org.glassfish.jersey.internal.Errors$1.call(Errors.java:268) org.glassfish.jersey.internal.Errors.process(Errors.java:316) org.glassfish.jersey.internal.Errors.process(Errors.java:298) org.glassfish.jersey.internal.Errors.process(Errors.java:268) org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289) org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256) org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703) org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416) org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:217) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Unknown Source)
</causeStackTrace>
<errorCode>500</errorCode>
<errorMessage>HTTP 500 Internal Server Error</errorMessage>
<errorStackTrace>
org.glassfish.jersey.jaxb.internal.AbstractRootElementJaxbProvider.writeTo(AbstractRootElementJaxbProvider.java:183) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:266) org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:251) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:109) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:85) org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:163) org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1135) org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:662) org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:395) org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:385) org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:280) org.glassfish.jersey.internal.Errors$1.call(Errors.java:272) org.glassfish.jersey.internal.Errors$1.call(Errors.java:268) org.glassfish.jersey.internal.Errors.process(Errors.java:316) org.glassfish.jersey.internal.Errors.process(Errors.java:298) org.glassfish.jersey.internal.Errors.process(Errors.java:268) org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289) org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256) org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:703) org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416) org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342) org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:217) org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500) org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456) java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) java.lang.Thread.run(Unknown Source)
</errorStackTrace>
</errorMessage>
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review 嗯,我正在努力适应它。我会发布答案。因此,删除和否决是不合适的。解决问题后,我将在此处发布带有代码的解决方案。所以我很感激人们在那之前没有删除答案或进一步投票。谢谢! 正如我在问题中解释的那样,您应该在准备好而不是之前发布答案。根据当前状态判断答案是否删除和投票。正确的做法是删除此答案并在完成后发布一个。 好吧。我从这个角度来看,那里有一个解决方案。无需其他人花费精力试图回答。我会尽快回复,很明显过滤器,而不是 @Path 和 @PathParams 是要走的路。【参考方案2】:您的 JAX-RS 代码存在几个问题:
@Path
中的正则表达式
@Path
注释的value 仅在参数模板内和:
字符之后解析正则表达式。您正在尝试在参数模板 "\\.format"
之外使用正则表达式,因此它不会将其解析为正则表达式。
路径分辨率
方法的路径包括类路径段,后面跟着它自己的路径段。当您尝试调用 /et.format
时,您的代码会建议路径 /et/.format
和 /et
,这在任何地方都没有定义,因此是 404。
这是一个可以根据您的代码工作的示例:
@Path("et")
public class Resource
private static final String JSON = "json";
private static final String XML = "xml";
@GET
@Path(".format:(" + JSON + "|" + XML + ")")
@Produces(value = MediaType.APPLICATION_JSON, MediaType.TEXT_XML ) // not XML?
public String getLog(@PathParam("format") String format)
switch (format)
case JSON:
this.getJSON();
break;
case XML:
this.getXML();
return format;
@GET
@Produces(MediaType.APPLICATION_JSON)
public void getJSON()
System.out.println("in JSON");
@GET
@Path("otherPath")
@Produces(MediaType.APPLICATION_XML)
public void getXML()
System.out.println("in XML");
您的有效请求现在将是:
http://localhost:18080/caaews/webapi/et(JSON 到getJSON
)
http://localhost:18080/caaews/webapi/et/otherPath(XML 到 getXML
)
http://localhost:18080/caaews/webapi/et/.xml(XML 到 getLog
)
http://localhost:18080/caaews/webapi/et/.json(JSON 到 getLog
)
根据您的需要更改路径。我将"otherPath"
用于XML 方法,因为它不能与空路径JSON 方法冲突。我不推荐这种约定。
注意事项:
在switch
语句中使用break
。
为减少出现错误的机会,可将常量用于可重用字符串等,就像我对自定义格式类型所做的那样。 enum
会更好。
编辑:
请求现在有一个路径/et/<something>.format
。如果我们将路径参数的范围扩大到包括整个段<something>.format
,然后以编程方式提取格式,则可以实现这一点:
@GET
@Path("segment:[a-zA-Z0-9_]*\\.(" + JSON + "|" + XML + ")")
@Produces(value = MediaType.APPLICATION_JSON, MediaType.TEXT_XML )
public String getLog(@PathParam("segment") String segment)
String format = segment.substring(segment.indexOf('.') + 1);
switch (format)
case JSON:
this.getJSON();
break;
case XML:
this.getXML();
return format;
正则表达式[a-zA-Z0-9_]*
表示任何字母数字或下划线一次或多次。您可以用您想要的任何限制替换该部分。有关允许的字符,请参阅 URL 规范。
【讨论】:
感谢正则表达式的评论。我不确定以正则表达式格式“以”结尾的方式。我想这是涉及插入符号或美元符号的东西,并转义了点。今天在我的待办事项清单上进行试验。我还添加了关于可能的 servlet 过滤器的评论,因此它可以有效地用于所有 Web 服务调用并在一个地方进行编码。 @JGFMK 查看我的编辑。下次请明确说明想要的结果。 您好!引用:“我见过一个 Java RESTFUL 网络服务,它允许在 URL 中请求内容类型,并在末尾带有扩展名,例如 .xml .json” @JGFMK 这并不具体,我的初始代码就是这样做的,而这并不是您想要的。请参阅How to Ask 和minimal reproducible example。无论如何,现在答案解决了您发布的问题。 很好的答案,除了你对生产/消费有误。他没有派人去;资源方法可以生成 XML 或 JSON。@Produces
实际上是这里的正确注解。【参考方案3】:
即使您没有标记jersey,您的问题也表明您正在使用泽西岛,因此我将发布针对泽西岛的解决方案。 Jersey 提供的是property that you can use to set the media type mappings
ServerPropeties.MEDIA_TYPE_MAPPINGS
public static final String MEDIA_TYPE_MAPPINGS
定义 URI 扩展到媒体类型的映射。该属性由
UriConnegFilter
使用。有关媒体类型映射的更多信息,请参阅它的 javadoc。属性值必须是
String
、String[]
或Map<String, MediaType>
的实例。每个 String 实例代表一个或多个 uri-extension-to-media-type 映射条目,由逗号 (",") 分隔。每个映射条目都是由冒号 (":") 分隔的键值对。下面是一个可接受的字符串值映射 txt 扩展名到 text/plain 和 xml 扩展名到 application/xml 的示例:txt : text/plain, xml : application/xml
未设置默认值。
配置属性的名称是“jersey.config.server.mediaTypeMappings”。
Java 配置示例
final Map<String, MediaType> mediaTypeMappings = new HashMap<>();
mediaTypeMappings.put("xml", MediaType.APPLICATION_XML_TYPE);
mediaTypeMappings.put("json", MediaType.APPLICATION_JSON_TYPE);
final ResourceConfig rc = new ResourceConfig()
.packages("com.example.jersey")
.property(ServerProperties.MEDIA_TYPE_MAPPINGS, mediaTypeMappings);
web.xml 配置示例
<servlet>
<servlet-name>JerseyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.example</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.mediaTypeMappings</param-name>
<param-value>xml:application/xml, json:application/json</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
【讨论】:
这是一个比我的方法更简洁的解决方案,但由于某种原因,它会产生类似的 HTTP 500 错误:0:0:0:0:0:0:0:1 - - [26/Nov/2017:07:57:12 +0000] "GET /caaews/webapi/et HTTP/1.1" 500 1082 0:0:0:0:0:0:0:1 - - [26/Nov/2017:07:58:26 +0000] "GET /caaews/webapi/et.xml HTTP/1.1" 500 1082 0:0:0:0:0:0:0:1 - - [26/Nov/2017:07:58:39 +0000] "GET /caaews/webapi/et.json HTTP/1.1" 200 15
即。只有 .JSON 有效...
我还尝试将 getXML 和 getJSON 合并到一个 getLog 方法中,而不是各有一个,并且在方法级别有双重 @Produces 注释。但这也没有什么不同。
我正在尝试将更好的错误处理集成到解决方案中。我正在为任何追随我脚步的人查看这些线程.. ***.com/questions/31289470/… ***.com/questions/25420697/jersey-2-exception-handing ***.com/questions/31501110/…
您的 Log 类中需要一个无参数构造函数。这对于带有 JAXB 的 XML 是必需的。你可能会遇到一个被吞噬的错误
这确实是正确的。那解决了它。我想我唯一剩下的问题是“默认”是 xml,如何将其更改为 JSON?【参考方案4】:
您是否尝试过在班级级别摆脱 @Path
注释?您的方法级别注释将是:
@Path("et.format")
我认为您当前的实施正在创建一个与以下路径匹配的子资源:
/et/format
【讨论】:
您对子资源的看法可能是对的......但是当我尝试这个并在 getXML 和 getJSON 方法上使用 @Path("et") 时,我突然得到了 404 ... 我还注意到我从未在 @Produces 的班级级别提供双重内容类型。当我这样做的时候。如果我使用该格式,它确实可以正确委托。 localhost:18080/caaews/webapi/et/.json 或 localhost:18080/caaews/webapi/et/.xml。所以这就解释了为什么代码不起作用(我还删除了路径中的 \\ 并只使用了“.format”。所以我猜@Path/@PathParam 方法毕竟不是我需要的URL 的扩展样式。我会更多地研究 servlet 过滤器。 为什么需要单独的方法?假设 Log 是可编组和可序列化的,您应该只需要根据您支持的内容(例如 application/json、application/xml、text/xml 等)更新@Produces
注释。跨度>
如果需要单独的方法,那么为什么不为每个@Path("et.xml")
和@Path("et.json")
明确定义路径?以上是关于JAX-RS(Jersey 2 实现)内容协商,带有 URL 扩展名 .xml 或 .json的主要内容,如果未能解决你的问题,请参考以下文章
是否可以定义一个与其实现分离的 jax-rs 服务接口(使用 eclipse 和 jersey)?
JAX-RS (Jersey 2) - 使用 JSR 250 注释的授权
Jersey 框架如何在 REST 中实现 JAX-RS API?