【问题标题】:Servicestack CorsFeature Global Options Handler Not Firing on Certain Routes;Servicestack CorsFeature 全局选项处理程序未在某些路由上触发;
【发布时间】:2016-02-20 02:58:28
【问题描述】:

我有一个使用 CorsFeature 的服务设置,并且正在使用 myz 在其他答案中建议的方法,收集在 appHost 文件中使用的函数中:

private void ConfigureCors(Funq.Container container)
{
    Plugins.Add(new CorsFeature(allowedOrigins: "*",
                                allowedMethods: "GET, POST, PUT, DELETE, OPTIONS",
                                allowedHeaders: "Content-Type, Authorization, Accept",
                                allowCredentials: true));

    PreRequestFilters.Add((httpReq, httpRes) =>
    {
        //Handles Request and closes Responses after emitting global HTTP Headers
        if (httpReq.HttpMethod == "OPTIONS")
        {
            httpRes.EndRequest();
        }
    });
}

但是,预请求过滤器仅在某些服务请求上触发。我们在服务中拥有的基础实体之一是问题实体,自定义路由定义如下:

[Route("/question")]
[Route("/question/{ReviewQuestionId}", "GET,DELETE")]
[Route("/question/{ReviewQuestionId}/{ReviewSectionId}", "GET")]

使用 POSTMAN 触发测试查询(全部使用 OPTIONS 动词),我们可以看到这将触发预请求过滤器:

http://localhost/myservice/api/question/

但这不会:

http://localhost/myservice/api/question/66

大概是因为第二条和第三条路由明确定义了它们接受的动词,而 OPTIONS 不是其中之一。

真的有必要在每个定义的限制支持的动词的路由中拼出 OPTIONS 吗?

【问题讨论】:

  • 我认为是的,有必要在每个 Route "GET,OPTIONS" 中都有 OPTIONS(至少,没有必要为 options 或 any 创建函数)我的代码是 this跨度>
  • 在使用这个太久之后,stefan2410——这对我有用。我只需要在我的 appHost.Configure 方法中添加 CorsFeature 插件,将我的路由更改为“GET,OPTIONS”和“POST,OPTIONS”,然后创建一个“public void Options(Requestar request){}”以使其正常工作。有点噩梦,但这就是你不为(我肯定很棒)v4支付神话的结果:)。

标签: servicestack


【解决方案1】:

PreRequestFilters 仅针对不排除 OPTIONS 的有效路由触发(例如,通过离开 Verbs=null 并允许它改为处理所有动词 - 包括 OPTIONS)。

为了能够处理所有 OPTIONS 请求(即即使是不匹配的路由),您需要使用Config.RawHttpHandlers 处理start of the Request pipeline(即匹配路由之前)的请求。在 ServiceStack 的下一个主要 (v4) 版本中,CorsFeature 为您完成了这项工作:

//Handles Request and closes Response after emitting global HTTP Headers
var emitGlobalHeadersHandler = new CustomActionHandler(
    (httpReq, httpRes) => httpRes.EndRequest());

appHost.RawHttpHandlers.Add(httpReq =>
    httpReq.HttpMethod == HttpMethods.Options
        ? emitGlobalHeadersHandler
        : null); 

CustomActionHandler 在 v3 中不存在,但很容易通过以下方式创建:

public class CustomActionHandler : IServiceStackHttpHandler, IHttpHandler 
{
    public Action<IHttpRequest, IHttpResponse> Action { get; set; }

    public CustomActionHandler(Action<IHttpRequest, IHttpResponse> action)
    {
        if (action == null)
            throw new Exception("Action was not supplied to ActionHandler");

        Action = action;
    }

    public void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
    {            
        Action(httpReq, httpRes);
    }

    public void ProcessRequest(HttpContext context)
    {
        ProcessRequest(context.Request.ToRequest(GetType().Name), 
            context.Response.ToResponse(),
            GetType().Name);
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

使用后备处理程序

匹配所有路由的另一种方法是指定FallbackRoute,例如,要处理所有路由,您可以将通配符添加到后备路由:

[FallbackRoute("/{Path*}")]
public class Fallback
{
    public string Path { get; set; }
}

但是由于它匹配所有未处理的路由,它不再为不匹配的请求提供 404,因为所有未匹配的路由现在都匹配了。但是您可以轻松地手动处理它:

public class FallbackService : Service
{
    public object Any(Fallback request)
    {
        if (base.Request.HttpMethod == "OPTIONS")
            return null;

        throw HttpError.NotFound("{0} was not found".Fmt(request.Path));
    }
}

【讨论】:

  • 我已经 implemented the first solution 使用 v3 但它不起作用。调用 EndRequest 并添加全局标头,但处理程序返回空 HTTP 响应。 Fiddler 说:“ReadResponse() 失败:服务器没有为此请求返回响应。服务器返回 0 个字节。”有什么想法吗?
  • 注释 EndRequest 调用并调用 ApplyGlobalResponseHeaders 代替工作。
【解决方案2】:

您不必将OPTIONS 动词添加到所有路由。相反,您可以执行以下操作:

只需将这条路线放在您的 Question 班级:

[Route("/question/{*}", Verbs = "OPTIONS")]
public class Question
{
}

然后将其添加到您的问题服务类中:

public void Options(Question question)
{
}

现在任何以/question/ 开头的路由都将支持OPTIONS 动词。

不过,如果您要拥有像 /question/something/ 这样的子路由,您可能想要限制这一点。

【讨论】:

    【解决方案3】:

    以下步骤在 ServiceStackV3 中对我有用。

    1.添加了一个新类 CustomActionHandler

    using ServiceStack.ServiceHost;
    using ServiceStack.WebHost.Endpoints.Extensions;
    using ServiceStack.WebHost.Endpoints.Support;
    
    public class CustomActionHandler : IServiceStackHttpHandler, IHttpHandler 
    {
        public Action<IHttpRequest, IHttpResponse> Action { get; set; }
    
        public CustomActionHandler(Action<IHttpRequest, IHttpResponse> action)
        {
            if (action == null)
                throw new Exception("Action was not supplied to ActionHandler");
    
            Action = action;
        }
    
        public void ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, string operationName)
        {            
            Action(httpReq, httpRes);
        }
    
        public void ProcessRequest(HttpContext context)
        {
            ProcessRequest(context.Request.ToRequest(GetType().Name), 
                context.Response.ToResponse(),
                GetType().Name);
        }
    
        public bool IsReusable
        {
            get { return false; }
        }
    }
    

    2。在 AppHostBase.Config.RawHttpHandlers 集合中添加 CustomHandler(此语句可以写在 Configure(Container container) 方法中)。

    // Handles Request and closes Response after emitting global HTTP Headers 
    var emitGlobalHeadersHandler = new CustomActionHandler((httpReq, httpRes) => httpRes.EndRequest());
    Config.RawHttpHandlers.Add(httpReq => httpReq.HttpMethod == HttpMethods.Options ? emitGlobalHeadersHandler : null);
    

    【讨论】:

      猜你喜欢
      • 2019-02-27
      • 1970-01-01
      • 1970-01-01
      • 2010-10-17
      • 1970-01-01
      • 1970-01-01
      • 2017-11-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多