Web API 2 CORS仅停止OPTION,而不是POST

时间:2013-11-20 21:11:51

标签: asp.net-web-api cors

我有一个来自VS 2013的Web API 2项目,使用Nuget的5.0.0.0 DLL(最近已经加载)。

我还创建了一个限制起源的自定义CorsPolicy。这似乎工作正常,我按照这里的指示:http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

我和Fiddler注意到的是,虽然OPTIONS动词被400 Bad Request正确阻止,但POST动词直接传递给控制器​​,然后然后调用CorsPolicy ,但到那时,Post动作已经成功,并且客户获得了200 OK的回报。

我期待POST被400 Bad Request以及OPTION动词阻止。如果我正确理解CORS TR,它应该被阻止。

以下是来自单个POST的Web API的诊断跟踪,其中我使用Fiddler将Origin标头设置为http://localhose。请记住 - 完全相同的情况正确地被OPTION动词阻止。

w3wp.exe Information: 0 : Request, Method=POST, Url=http://myhost/myvdir/api/v1/MyCntrllr/MyAction, Message='http://myhost/myvdir/api/v1/MyCntrllr/MyAction'
w3wp.exe Information: 0 : Message='MyCntrllr', Operation=DefaultHttpControllerSelector.SelectController
w3wp.exe Information: 0 : Message='MyNamespace.Controllers.MyCntrllrController', Operation=DefaultHttpControllerActivator.Create
w3wp.exe Information: 0 : Message='MyNamespace.Controllers.MyCntrllrController', Operation=HttpControllerDescriptor.CreateController
w3wp.exe Information: 0 : Message='Selected action 'MyAction(Submission submission)'', Operation=ApiControllerActionSelector.SelectAction
w3wp.exe Information: 0 : Message='Value read='{ MyValue1: 24000, MyValue2: 2, MyValues3: 24,34, MyValue4: 0, MyValue5: 90001, MyValue6: c0, MyValue7: 16 }'', Operation=JsonMediaTypeFormatter.ReadFromStreamAsync
w3wp.exe Information: 0 : Message='Parameter 'submission' bound to the value '{ MyValue1: 24000, MyValue2: 2, MyValues3: 24,34, MyValue4: 0, MyValue5: 90001, MyValue6: c0, MyValue7: 16 }'', Operation=FormatterParameterBinding.ExecuteBindingAsync
w3wp.exe Information: 0 : Message='Model state is valid. Values: submission={ MyValue1: 24000, MyValue2: 2, MyValues3: 24,34, MyValue4: 0, MyValue5: 90001, MyValue6: c0, MyValue7: 16 }', Operation=HttpActionBinding.ExecuteBindingAsync
w3wp.exe Information: 0 : Message='Action returned 'MyNamespace.MyConclusion'', Operation=ReflectedHttpActionDescriptor.ExecuteAsync
w3wp.exe Information: 0 : Message='Will use same 'JsonMediaTypeFormatter' formatter', Operation=JsonMediaTypeFormatter.GetPerRequestFormatterInstance
w3wp.exe Information: 0 : Message='Selected formatter='JsonMediaTypeFormatter', content-type='application/json; charset=utf-8'', Operation=DefaultContentNegotiator.Negotiate
w3wp.exe Information: 0 : Operation=ApiControllerActionInvoker.InvokeActionAsync, Status=200 (OK)
w3wp.exe Information: 0 : Operation=MyCntrllrController.ExecuteAsync, Status=200 (OK)
w3wp.exe Information: 0 : Message='CorsPolicyProvider selected: 'MyNamespace.WhiteListOriginPolicyProvider'', Operation=CorsPolicyProviderFactory.GetCorsPolicyProvider
w3wp.exe Information: 0 : Message='CorsPolicy selected: 'AllowAnyHeader: True, AllowAnyMethod: False, AllowAnyOrigin: False, PreflightMaxAge: null, SupportsCredentials: False, Origins: {https://www.example.com,http://localhost:22221}, Methods: {POST,OPTIONS}, Headers: {}, ExposedHeaders: {}'', Operation=WhiteListOriginPolicyProvider.GetCorsPolicyAsync
w3wp.exe Information: 0 : Message='CorsResult returned: 'IsValid: False, AllowCredentials: False, PreflightMaxAge: null, AllowOrigin: , AllowExposedHeaders: {}, AllowHeaders: {}, AllowMethods: {}, ErrorMessages: {The origin 'http://localhose' is not allowed.}'', Operation=CorsEngine.EvaluatePolicy
w3wp.exe Information: 0 : Operation=CorsMessageHandler.SendAsync, Status=200 (OK)
w3wp.exe Information: 0 : Response, Status=200 (OK), Method=POST, Url=http://myhost/myvdir/api/v1/MyCntrllr/MyAction, Message='Content-type='application/json; charset=utf-8', content-length=unknown'
w3wp.exe Information: 0 : Operation=JsonMediaTypeFormatter.WriteToStreamAsync
w3wp.exe Information: 0 : Operation=MyCntrllrController.Dispose

代码:

WhiteListOriginPolicy

public class WhiteListOriginPolicy 
    : CorsPolicy
{
    public WhiteListOriginPolicy()
    {
        AllowAnyHeader = true;
        AllowAnyMethod = false;
        Methods.Add(HttpMethod.Post.ToString());
        Methods.Add(HttpMethod.Options.ToString());
        foreach (var origin in Settings.Default.WhiteListOrigins)
        {
            Origins.Add(origin);
        }
    }
}

WhiteListOrigins是来自web.config文件的StringCollection

WhiteListOriginPolicyProvider

public class WhiteListOriginPolicyProvider 
    : ICorsPolicyProvider
{
    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return Task.FromResult((CorsPolicy) new WhiteListOriginPolicy());
    }
}

CorsPolicyProviderFactory

public class CorsPolicyProviderFactory
    : ICorsPolicyProviderFactory
{
    private readonly ICorsPolicyProvider _whiteListOriginsPolicyProvider = new WhiteListOriginPolicyProvider();

    public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
    {
        return _whiteListOriginsPolicyProvider;
    }
}

WebApiConfig

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.EnableSystemDiagnosticsTracing();
        config.SetCorsPolicyProviderFactory(new CorsPolicyProviderFactory());
        config.EnableCors();
        // Web API routes
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { action = RouteParameter.Optional }
            );
    }
}

WhiteListOriginPolicyAttribute

我认为这是多余的,但无论我是否使用

都无关紧要
public class WhiteListOriginPolicyAttribute
    : Attribute
    , ICorsPolicyProvider
{
    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return Task.FromResult((CorsPolicy) new WhiteListOriginPolicy());
    }
}

MyCntrllrController

[RoutePrefix("api/v1/MyCntrllr")]
public class MyCntrllrController
    : ApiController
{
    [HttpPost]
    [HttpOptions]
    [WhiteListOriginPolicy]
    [Route("MyAction")]
    public IMyConclusion MyAction([FromBody] Submission submission)
    {
        if (Request.Method == HttpMethod.Options)
        {
            return null;
        }
        if (null == submission)
        {
            var response = new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent("Request content body does not contain recognizable Submission data")
            };
            throw new HttpResponseException(response);
        }
        var engine = new CalcEngine(new DataLocatorService());
        var eligibility = engine.GetEligibility(submission);
        return eligibility;
    }
}

操作中的OPTIONS动词。

请求

OPTIONS http://myhost/myvdir/api/v1/MyCntrller/MyAction HTTP/1.1
Accept: */*
Origin: http://localhose
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, accept
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: myhost
Content-Length: 0
DNT: 1
Connection: Keep-Alive
Pragma: no-cache

响应

HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 20 Nov 2013 20:32:03 GMT
Content-Length: 59

{"Message":"The origin 'http://localhose' is not allowed."}

动作中的动词。

请求

POST http://myhost/myvdir/api/v1/MyCntrller/MyAction HTTP/1.1
Accept: */*
Origin: http://localhose
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, accept
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: myhost
Content-Length: 121
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
Content-Type: application/json

{"myvalue1":24000,"myvalue2":"2","myvalues3":["24","34"],"myvalue4":0,"myvalue5":"90001","myvalue6":"c0","myvalue7":"16"}

响应

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 20 Nov 2013 20:32:33 GMT
Content-Length: 460

_omitted_

1 个答案:

答案 0 :(得分:4)

这不是CORS的目的。浏览器阻止了交叉源Ajax调用,因此CORS将允许目标服务器放宽这些规则。默认情况下,浏览器会阻止调用JS获取POST的结果,但它不会阻止POST(或GET)到端点。您需要实施标准授权方法,以确保只允许客户端请求端点。