"No 'Access-Control-Allow-Origin' header" JAX-RS 请求异常期间出错

"No 'Access-Control-Allow-Origin' header" error during exceptions on JAX-RS requests

我可以通过 restful 调用与我的 AngularJS 应用程序的后端通信。 JAX-RS 调用工作,例如登录或从后端获取数据。这是因为我在应用程序中确实有一个CORSResponseFilter

@Provider    
public class CORSResponseFilter implements ContainerResponseFilter { 
    @Override
    public void filter( ContainerRequestContext requestCtx, ContainerResponseContext responseCtx ) throws IOException {
        responseCtx.getHeaders().add( "Access-Control-Allow-Origin", "*" );
        responseCtx.getHeaders().add( "Access-Control-Allow-Credentials", "true" );
        responseCtx.getHeaders().add( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT" );
        responseCtx.getHeaders().add( "Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
    }
}

我遇到的问题是 JAX-RS 调用出现问题。如果存在内部服务器错误或抛出 BadRequest 异常,则前端响应:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. The response had HTTP status code 500.

但是在 GlassFish 日志中我得到:

Warning: StandardWrapperValve[ServletAdaptor]: Servlet.service() for servlet ServletAdaptor threw exception org.project.dashboard.exceptions.BadRequestException: (400) : Invalid date format should be yyyy-MM-dd

正如您在 GlassFish 日志中看到的那样,异常被映射到正确的错误和响应代码,但不会渗透到前端。我只收到 "No 'Access-Control-Allow-Origin' header" 错误。当响应代码为 200 时,我没有收到此错误。


更新

ResponseFilter 在没有抛出异常时使用,但当有异常时 ResponseFilter 未被触及。

这是我的异常映射器的代码,它可能会导致问题:

@Provider
public class DashboardExceptionMapper implements ExceptionMapper<DashboardException> {

@Override
public Response toResponse(DashboardException e) {
    ErrorMessage error = new ErrorMessage();
    error.setStatus(e.getHttpStatusCode());
    error.setCode(e.getClass().getSimpleName());
    error.setMessage(e.getShortMessage());

    return Response.status(e.getHttpStatusCode()).entity(error)
            .build();
}
}

更新 2

抛出的异常代码:

public class BadRequestException extends DashboardException{
private static final long serialVersionUID = 1L;

public BadRequestException(String message) {
    super(HttpURLConnection.HTTP_BAD_REQUEST, message);
}

}

更新 3

请求异常之一的完整堆栈跟踪:

        Warning:   StandardWrapperValve[ServletAdaptor]: Servlet.service() for servlet ServletAdaptor threw exception
    org..dashboard.exceptions.AuthenticationException: (403) : An invalid sessionID has been provided
        at org..dashboard.exposed.DashboardREST.getRoutes(DashboardREST.java:330)
        at sun.reflect.GeneratedMethodAccessor191.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1081)
        at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1153)
        at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4695)
        at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:630)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
        at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:46)
        at sun.reflect.GeneratedMethodAccessor170.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:582)
        at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doCall(SystemInterceptorProxy.java:163)
        at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:140)
        at sun.reflect.GeneratedMethodAccessor172.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:883)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
        at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4667)
        at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4655)
        at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:212)
        at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
        at com.sun.proxy.$Proxy414.getRoutes(Unknown Source)
        at org.dashboard.exposed.__EJB31_Generated__DashboardREST__Intf____Bean__.getRoutes(Unknown Source)
        at sun.reflect.GeneratedMethodAccessor191.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.invoke(ResourceMethodInvocationHandlerFactory.java:81)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:125)
        at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:195)
        at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:91)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:346)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:341)
        at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:101)
        at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:224)
        at org.glassfish.jersey.internal.Errors.call(Errors.java:271)
        at org.glassfish.jersey.internal.Errors.call(Errors.java:267)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
        at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
        at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
        at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:198)
        at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:946)
        at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:323)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:372)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:335)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:218)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
        at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
        at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
        at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
        at org.glassfish.grizzly.filterchain.ExecutorResolver.execute(ExecutorResolver.java:119)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
        at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
        at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
        at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access0(WorkerThreadIOStrategy.java:55)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
        at java.lang.Thread.run(Thread.java:745)

一般来说ContainerResponseFilter会一直执行,见Server filters:

The filter will be executed for every response which is in most cases after the resource method is executed. Response filters are executed even if the resource method is not run, for example when the resource method is not found and 404 "Not found" response code is returned by the Jersey runtime. In this case the filter will be executed and will process the 404 response.

另请参阅:Filter and interceptor execution order and JAX_RS_SPEC-230

但提供者必须是 public,参见 JERSEY-2094, JERSEY-2096 and JERSEY-2097

在某些情况下 @PreMatching 禁用 ContainerResponseFilter,参见 ContainerResponseFilter not working

此外,您必须知道,如果响应已经是 committed,则不能添加 headers。

您的过滤器不起作用,因为您的自定义 ExceptionMapper 不起作用。用 @Provider 注释对其进行注释是不够的,您还应该将它所在的包包含到提供程序包配置中或手动注册此自定义异常映射器。

例如:

<servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
         <param-name>jersey.config.server.provider.packages</param-name>
         <param-value>org.dashboard.exposed,org.dashboard.exceptions</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

请注意 <init-param> 名称的确切值取决于您使用的版本。