带有泽西岛的 OPTIONS 请求上的 CORS 标头

Posted

技术标签:

【中文标题】带有泽西岛的 OPTIONS 请求上的 CORS 标头【英文标题】:CORS headers on OPTIONS request with Jersey 【发布时间】:2016-10-26 20:29:26 【问题描述】:

我有一个 REST API,我希望 一些 方法具有特定的 CORS 标头。我在资源方法上有一个注释,还有一个添加标题的过滤器:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface CorsHeaders 

@Path("api")
class MyApi 
  @CorsHeaders
  @GET
  public Response m() 
    return Response.ok().build();
  


@Provider
class CorsFilter implements ContainerResponseFilter 
  @Context private ResourceInfo resourceInfo;

  @Override 
  public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) 
    if (resourceInfo.getResourceMethod().getAnnotation(CorsHeaders.class) != null) 
      responseContext.getHeaders().add(/* appropriate headers here*/);
    
  

这适用于所有 GET、POST 等请求。它不适用于 OPTIONS 请求,因为资源方法将解析为 org.glassfish.jersey.server.wadl.processor.WadlModelProcessor$OptionsHandler 而不是我的方法,因此注释将不存在。

我可以通过向我的 API 类(在同一个 @Path 上)添加一个 @OPTIONS @CorsHeaders public Response options() return Response.ok().build(); 方法来解决这个问题,但我不想对所有方法都这样做。

在处理 OPTIONS 请求时如何找出实际的 (GET/POST) 资源方法?

【问题讨论】:

您是否在“Access-Control-Allow-Methods”中添加了选项来 Rest Api ResponseHeaders? 没关系,因为添加标题的代码甚至没有到达。 这是一个 ASP.NET Web API 服务吗? 好的,球衣。我不知道您在哪里添加标头,但您需要在实际数据处理开始之前一步处理选项请求。由于我没有使用球衣的经验,因此应该有一种方法可以提前一步连接到管道中,例如 ASP.NET Web API 中的 DelegatingHandler 或 MiddleWare 【参考方案1】:

恐怕在不改变泽西岛本身的情况下,使用当前版本以一种很好的方式实现你想要完成的事情实际上是不可能的。

无论如何,我也不确定根据规范规范使用@Provider 请求特定过滤器是否是正确的方法。但我是谁,我实际上是自己做的。当然也可以在ResourceConfig 中注册过滤器。 一般来说,我建议看一下@NameBinding,但对于这种情况,名称绑定 Jersey-style 是不够的。使用 @NameBinding,您不必自己检查注释,因为 Jersey 已经为您完成了。

不幸的是,再次使用@NameBinding,它是针对这种情况引入的,存在“自动生成”选项处理程序的问题。我做了相当多的挖掘(一些最相关的类/方法是OptionsMethodProcessorWadlModelProcessorResourceModelConfigurator#initServerRuntime ApplicationHandler#initialize),但没有找到充分融入该过程的方法。以下是处理 CORS 的内容:

@NameBinding
@Target( ElementType.TYPE, ElementType.METHOD )
@Retention(RetentionPolicy.RUNTIME)
public @interface CrossOrigin 



@CrossOrigin
public class CrossOriginResponseFilter implements ContainerResponseFilter 
    public void filter(ContainerRequestContext requestContext,  
                       ContainerResponseContext responseContext)
    throws IOException 
        // do Cross Origin stuff
    


@Path("ress")
public class MyResource 
    @CrossOrigin
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response save(DetailsDTO details) 
         // do something with the details
    

但是,虽然这适用于对资源的任何直接请求,但这也不适用于 CORS-preflight-requests,因为 Jersey 不会将 name-binding-annotation @CrossOrigin 应用于预定义/自动生成的 OPTIONS-处理程序。 您可以看到,在请求上下文中查看资源的运行时表示时(不要让所有文本激怒您,重要的是每个 nameBindings-properties 在每个 ResourceMethod 的末尾):

[ResourceMethod
    httpMethod=POST, consumedTypes=[application/json], 
    producedTypes=[application/json], suspended=false, suspendTimeout=0, 
    suspendTimeoutUnit=MILLISECONDS, invocable=Invocablehandler=ClassBasedMethodHandlerhandlerClass=class de.example.MyResource, 
    handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@2c253414], definitionMethod=public javax.ws.rs.core.Response de.example.MyResource.save(de.example.DetailsDTO),
    parameters=[Parameter [type=class de.example.DetailsDTO, source=null, defaultValue=null]],
    responseType=class javax.ws.rs.core.Response,
    nameBindings=[interface de.example.CrossOrigin],
ResourceMethod
    httpMethod=OPTIONS, consumedTypes=[*/*], 
    producedTypes=[application/vnd.sun.wadl+xml], suspended=false, 
    suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, 
    invocable=Invocablehandler=ClassBasedMethodHandlerhandlerClass=class org.glassfish.jersey.server.wadl.processor.WadlModelProcessor$OptionsHandler, 
    handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor@949030f], 
    definitionMethod=public abstract java.lang.Object org.glassfish.jersey.process.Inflector.apply(java.lang.Object), 
    parameters=[Parameter [type=interface javax.ws.rs.container.ContainerRequestContext, source=null, defaultValue=null]], responseType=class javax.ws.rs.core.Response,
    nameBindings=[],
ResourceMethod
    httpMethod=OPTIONS, consumedTypes=[*/*], producedTypes=[text/plain], 
    suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, 
    invocable=Invocablehandler=ClassBasedMethodHandlerhandlerClass=class org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor$PlainTextOptionsInflector,
    handlerConstructors=[], definitionMethod=public abstract java.lang.Object org.glassfish.jersey.process.Inflector.apply(java.lang.Object), 
    parameters=[Parameter [type=interface javax.ws.rs.container.ContainerRequestContext, source=null, defaultValue=null]], 
    responseType=class javax.ws.rs.core.Response, nameBindings=[],
ResourceMethod
    httpMethod=OPTIONS, consumedTypes=[*/*], producedTypes=[*/*], 
    suspended=false, suspendTimeout=0, suspendTimeoutUnit=MILLISECONDS, 
    invocable=Invocablehandler=ClassBasedMethodHandlerhandlerClass=class org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor$GenericOptionsInflector,
    handlerConstructors=[], definitionMethod=public abstract java.lang.Object org.glassfish.jersey.process.Inflector.apply(java.lang.Object), 
    parameters=[Parameter [type=interface javax.ws.rs.container.ContainerRequestContext, source=null, defaultValue=null]], responseType=class javax.ws.rs.core.Response, 
    nameBindings=[]]

但现在您可以使用名称绑定信息通过创建另一个过滤器自己处理预检请求:

@Provider
@Priority(1)
public class CrossOriginResponseFilter implements ContainerRequestFilter 
    Resource res = ((ContainerRequest)requestContext)
        .getUriInfo().getMatchedResourceMethod().getParent();

    if (res.getResourceMethods().get(0).getNameBindings().contains(CrossOrigin.class)) 
        // handlePreflightRequest and abort: requestContext.abortWith(builder.build());
    

有趣的是,提取的 Resource res 将仅包含与实际请求 URI 和方法匹配的相关资源方法以及自动生成的 OPTIONS 处理程序,正如您在上面资源的运行时表示中看到的那样方法。示例资源实际上还有更多的方法,POST 和 GET。因此,您可以在此处使用.get(0) 访问所需的信息。

但当心!我没有检查这是否是真的,或者只是当你用单独的路径注释你的资源方法时。所以也许比我这里的简单版本有更多的匹配工作要做。

我自己发现该解决方案非常丑陋,最终得到的过滤器不是特定于方法的,而是简单地处理对任何资源的所有请求(类似于家伙here 的解决方案)。但这应该是对如何“在处理 OPTIONS 请求时找出实际的 (GET/POST) 资源方法”的问题的答案。

【讨论】:

以上是关于带有泽西岛的 OPTIONS 请求上的 CORS 标头的主要内容,如果未能解决你的问题,请参考以下文章

markdown 带有CORS和OPTIONS请求的流明

CORS 因请求而失败,带有 OPTIONS(状态为 0 的响应)

cors / ajax 请求的重复 OPTIONS 请求

ZF2中的CORS POST请求变成OPTIONS请求

CORS,防止带有授权标头的请求预检

ASP.NET Core:带有 OPTIONS 异常的 Windows 身份验证(CORS 预检)