JAX-RS(Jersey 2实现)使用URL扩展名.xml或.json进行内容协商。

3
我看到过一个Java RESTFUL webservice,可以允许在URL中使用扩展名请求内容类型,例如:
  • .xml
  • .json
这是我自己的Web服务所追求的内容协商风格。
我知道@Produces注释,以及方法可以用(value = {})语法解决多种类型,通过添加一个Accept头,比如使用Postman、Chrome扩展程序等。但我不确定如何有效地从一个方法中提取信息,并委托给另一个方法。
我假设可以使用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项目来设置,方法类似于这里大约在4:50左右的视频,使用以下内容:

  • org.glassfish.jersey.archetypes
  • jersey.quickstart.webapp
  • 2.26

然后取消注释提供的pom.xml部分以启用JSON支持,这将有效地向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语句中加入了break。

感谢@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中使用某种后缀甚至更大的错误。 - G. Demecki
提供一个输入示例和你的尝试。请参见[mcve]。本网站不是编码服务。 示例输入: def add_numbers(x, y): return x + y print(add_numbers(5, 10))我的翻译:请提供一个输入示例和您的尝试。请参见[mcve]。本网站不提供编码服务。 - user1803551
@user1803551 - 你没给我机会!我本来就要做的! - JGFMK
没有给你机会?在发布问题之前,你应该准备好问题,而不是不断地通过编辑添加细节。 - user1803551
1
注意:没有 break 语句的 case 语句。启用标准的Java编译器警告并遵守它们,您就不会再犯这种错误了。 - VGR
@VGR - 您说得对。我最初尝试了多个返回和重构代码,并忘记插入breaks。感谢您的敏锐眼力。但是,回想起来,这只会对性能产生微不足道的影响,因为代码会落在不匹配第二个条件的位置。 - JGFMK
4个回答

1

你的JAX-RS代码存在几个问题:

@Path中的正则表达式

@Path注释中的value仅解析参数模板内部和:字符后面的正则表达式。您试图在参数模板之外使用正则表达式"\\.{format}",因此它不会将其解析为正则表达式。

路径解析

方法的路径包括类路径段和自己的路径段。您的代码建议路径/et/.{format}/et,而您正在尝试调用未定义在任何地方的/et.{format},因此出现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");
    }
}

您现在可以使用以下有效请求:

根据您的要求更改路径。我在XML方法中使用了"otherPath",因为它不会与空路径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 webservice,它允许在URL中使用扩展名请求内容类型,例如 .xml .json” - JGFMK
2
除了您对“生产/消耗”有所错误外,您的回答非常出色。他没有发送任何正文;资源方法可以生成XML或JSON。在这种情况下,“@Produces”是正确的注释。 - VGR
对于GET请求,请求体将被忽略,因此无需消耗任何内容。 - callmepills
@VGR 好的,我已经改回来了。 - user1803551
显示剩余5条评论

1
尽管您没有标记,但您的问题表明您正在使用Jersey,因此我将发布一个特定于Jersey的解决方案。Jersey提供的是一个属性,您可以使用它来设置媒体类型映射

ServerPropeties.MEDIA_TYPE_MAPPINGS

public static final String MEDIA_TYPE_MAPPINGS

Defines mapping of URI extensions to media types. The property is used by UriConnegFilter. See it's javadoc for more information on media type mappings.

The property value MUST be an instance of String, String[] or Map<String, MediaType>. Each String instance represents one or more uri-extension-to-media-type map entries separated by a comma (","). Each map entry is a key-value pair separated by a colon (":"). Here is an example of an acceptable String value mapping txt extension to text/plain and xml extension to application/xml:

txt : text/plain, xml : application/xml

A default value is not set.

The name of the configuration property is "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 10820:0:0:0:0:0:0:1 - - [26/Nov/2017:07:58:26 +0000] "GET /caaews/webapi/et.xml HTTP/1.1" 500 10820: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 可以工作... - JGFMK
我正在尝试将更好的错误处理集成到解决方案中。 我正在查看这些线程,以供跟随我步伐的任何人参考.. https://dev59.com/ol0Z5IYBdhLWcg3wlBJU https://stackoverflow.com/questions/25420697/jersey-2-exception-handing https://dev59.com/6Y3da4cB1Zd3GeqPwCoe - JGFMK
1
你的Log类需要一个无参构造函数。这是为了使用JAXB处理XML所必需的。你可能会遇到被忽略的错误。 - Paul Samsotha
那确实是正确的。那解决了问题。我想我唯一剩下的问题是,“默认”是xml,我该如何将其更改为JSON? - JGFMK
此外,按照Jersey文档更改注释中的顺序毫无影响。同样地,在web.xml中进行更改也是如此。对我来说始终默认为xml。 - JGFMK
显示剩余3条评论

0

你尝试过在类级别上去掉@Path注释吗?然后你的方法级别注释将会是:

@Path("et.{format}")

我认为你目前的实现正在创建一个与路径匹配的子资源,例如:

/et/{format}

你可能对子资源是正确的...... 但是当我尝试这样做,并在getXML和getJSON方法上使用@Path("et")时,突然出现了404错误... - JGFMK
我还注意到在@Produces的类级别上从未给出双重内容类型。当我这样做时,如果我使用格式http://localhost:18080/caaews/webapi/et/.json或http://localhost:18080/caaews/webapi/et/.xml,它确实正确委派。所以这就解释了为什么代码不起作用(我还删除了路径中的\\,只使用了“.{format}”)。所以我想@Path/@PathParam方法并不是我需要的扩展URL风格。我将更深入地研究servlet过滤器。 - JGFMK
为什么需要分开的方法?假设Log既可序列化又可编组,您只需要根据所支持的内容类型(例如application/json、application/xml、text/xml等)更新@Produces注释即可。 - callmepills
如果需要分别的方法,为什么不明确定义每个 @Path("et.xml")@Path("et.json") 的路径呢? - callmepills

-1
当我在谷歌上搜索“servlet filter with jax-rs example”时,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内容类型。

  • text/xml
  • application/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 功能。


我也在想,如果查看 这里 的示例后,javax.servlet.filters 是否与 JAX-RS 2.0 不兼容...


根据此问题的其他相关答案/评论,我最终实现了一个异常处理程序,以便在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>

1
虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅链接的答案可能会失效。- 来自审查 - Bill the Lizard
我正在努力适应它,一旦完善我将发布答案。因此,删除和投票都是不恰当的。在问题得到解决之后,我会在这里贴出代码和解决方案。因此,在此期间,我希望大家不要删除回答或继续投反对票。谢谢! - JGFMK
1
正如我在问题中所解释的那样,您应该在答案准备好之后而不是之前发布答案。答案将根据当前状态进行删除和投票评估。正确的做法是删除此答案并在完成后发布一个新的答案。 - user1803551
好的,我从这个角度来看,有一个解决方案。没有必要让其他人浪费精力去回答。我很快就会回复,并且很明显过滤器而不是@Path和@PathParams是正确的方法。 - JGFMK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接