Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit d0b6f23

Browse files
javiercnJunTaoLuo
authored andcommitted
Refactor CORS support out of MVC Core
1 parent 3dba0bb commit d0b6f23

File tree

8 files changed

+483
-24
lines changed

8 files changed

+483
-24
lines changed

src/Microsoft.AspNetCore.Mvc.Core/Internal/HttpMethodActionConstraint.cs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ public class HttpMethodActionConstraint : IActionConstraint
1515

1616
private readonly IReadOnlyList<string> _httpMethods;
1717

18-
private readonly string OriginHeader = "Origin";
19-
private readonly string AccessControlRequestMethod = "Access-Control-Request-Method";
20-
private readonly string PreflightHttpMethod = "OPTIONS";
21-
2218
// Empty collection means any method will be accepted.
2319
public HttpMethodActionConstraint(IEnumerable<string> httpMethods)
2420
{
@@ -70,18 +66,6 @@ public bool Accept(ActionConstraintContext context)
7066
var request = context.RouteContext.HttpContext.Request;
7167
var method = request.Method;
7268

73-
// Perf: Check http method before accessing the Headers collection.
74-
if (string.Equals(method, PreflightHttpMethod, StringComparison.OrdinalIgnoreCase) &&
75-
request.Headers.ContainsKey(OriginHeader))
76-
{
77-
// Update the http method if it is preflight request.
78-
var accessControlRequestMethod = request.Headers[AccessControlRequestMethod];
79-
if (!StringValues.IsNullOrEmpty(accessControlRequestMethod))
80-
{
81-
method = accessControlRequestMethod;
82-
}
83-
}
84-
8569
for (var i = 0; i < _httpMethods.Count; i++)
8670
{
8771
var supportedMethod = _httpMethods[i];

src/Microsoft.AspNetCore.Mvc.Cors/Internal/CorsApplicationModelProvider.cs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Linq;
66
using Microsoft.AspNetCore.Cors.Infrastructure;
77
using Microsoft.AspNetCore.Mvc.ApplicationModels;
8+
using Microsoft.AspNetCore.Mvc.Internal;
9+
using Microsoft.Extensions.Options;
810

911
namespace Microsoft.AspNetCore.Mvc.Cors.Internal
1012
{
@@ -28,23 +30,25 @@ public void OnProvidersExecuting(ApplicationModelProviderContext context)
2830
throw new ArgumentNullException(nameof(context));
2931
}
3032

31-
IEnableCorsAttribute enableCors;
32-
IDisableCorsAttribute disableCors;
33+
var isCorsEnabledGlobally = context.Result.Filters.OfType<ICorsAuthorizationFilter>().Any() ||
34+
context.Result.Filters.OfType<CorsAuthorizationFilterFactory>().Any();
3335

3436
foreach (var controllerModel in context.Result.Controllers)
3537
{
36-
enableCors = controllerModel.Attributes.OfType<IEnableCorsAttribute>().FirstOrDefault();
38+
var enableCors = controllerModel.Attributes.OfType<IEnableCorsAttribute>().FirstOrDefault();
3739
if (enableCors != null)
3840
{
3941
controllerModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName));
4042
}
4143

42-
disableCors = controllerModel.Attributes.OfType<IDisableCorsAttribute>().FirstOrDefault();
44+
var disableCors = controllerModel.Attributes.OfType<IDisableCorsAttribute>().FirstOrDefault();
4345
if (disableCors != null)
4446
{
4547
controllerModel.Filters.Add(new DisableCorsAuthorizationFilter());
4648
}
4749

50+
var corsOnController = enableCors != null || disableCors != null || controllerModel.Filters.OfType<ICorsAuthorizationFilter>().Any();
51+
4852
foreach (var actionModel in controllerModel.Actions)
4953
{
5054
enableCors = actionModel.Attributes.OfType<IEnableCorsAttribute>().FirstOrDefault();
@@ -58,6 +62,29 @@ public void OnProvidersExecuting(ApplicationModelProviderContext context)
5862
{
5963
actionModel.Filters.Add(new DisableCorsAuthorizationFilter());
6064
}
65+
66+
var corsOnAction = enableCors != null || disableCors != null || actionModel.Filters.OfType<ICorsAuthorizationFilter>().Any();
67+
68+
if (isCorsEnabledGlobally || corsOnController || corsOnAction)
69+
{
70+
UpdateHttpMethodActionConstraint(actionModel);
71+
}
72+
}
73+
}
74+
}
75+
76+
private static void UpdateHttpMethodActionConstraint(ActionModel actionModel)
77+
{
78+
for (var i = 0; i < actionModel.Selectors.Count; i++)
79+
{
80+
var selectorModel = actionModel.Selectors[i];
81+
for (var j = 0; j < selectorModel.ActionConstraints.Count; j++)
82+
{
83+
var httpConstraint = selectorModel.ActionConstraints[j] as HttpMethodActionConstraint;
84+
if (httpConstraint != null)
85+
{
86+
selectorModel.ActionConstraints[j] = new CorsHttpMethodActionConstraint(httpConstraint);
87+
}
6188
}
6289
}
6390
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Collections.ObjectModel;
7+
using Microsoft.AspNetCore.Mvc.ActionConstraints;
8+
using Microsoft.Extensions.Primitives;
9+
using Microsoft.AspNetCore.Mvc.Internal;
10+
11+
namespace Microsoft.AspNetCore.Mvc.Cors.Internal
12+
{
13+
public class CorsHttpMethodActionConstraint : HttpMethodActionConstraint, IActionConstraint
14+
{
15+
private readonly string OriginHeader = "Origin";
16+
private readonly string AccessControlRequestMethod = "Access-Control-Request-Method";
17+
private readonly string PreflightHttpMethod = "OPTIONS";
18+
19+
public CorsHttpMethodActionConstraint(HttpMethodActionConstraint constraint)
20+
: base(constraint.HttpMethods)
21+
{
22+
}
23+
24+
bool IActionConstraint.Accept(ActionConstraintContext context)
25+
{
26+
if (context == null)
27+
{
28+
throw new ArgumentNullException(nameof(context));
29+
}
30+
31+
var methods = (ReadOnlyCollection<string>)HttpMethods;
32+
if (methods.Count == 0)
33+
{
34+
return true;
35+
}
36+
37+
var request = context.RouteContext.HttpContext.Request;
38+
var method = request.Method;
39+
if (string.Equals(
40+
request.Method,
41+
PreflightHttpMethod,
42+
StringComparison.OrdinalIgnoreCase) &&
43+
request.Headers.ContainsKey(OriginHeader))
44+
{
45+
// Update the http method if it is preflight request.
46+
var accessControlRequestMethod = request.Headers[AccessControlRequestMethod];
47+
if (!StringValues.IsNullOrEmpty(accessControlRequestMethod))
48+
{
49+
for (var i = 0; i < methods.Count; i++)
50+
{
51+
var supportedMethod = methods[i];
52+
if (string.Equals(supportedMethod, accessControlRequestMethod, StringComparison.OrdinalIgnoreCase))
53+
{
54+
return true;
55+
}
56+
}
57+
58+
return false;
59+
}
60+
}
61+
62+
return base.Accept(context);
63+
}
64+
}
65+
}

test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/HttpMethodActionConstraintTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class HttpMethodActionConstraintTest
2626

2727
[Theory]
2828
[MemberData(nameof(AcceptCaseInsensitiveData))]
29-
public void HttpMethodActionConstraint_Accept_Preflight_CaseInsensitive(IEnumerable<string> httpMethods, string accessControlMethod)
29+
public void HttpMethodActionConstraint_IgnoresPreflightRequests(IEnumerable<string> httpMethods, string accessControlMethod)
3030
{
3131
// Arrange
3232
var constraint = new HttpMethodActionConstraint(httpMethods);
@@ -37,7 +37,7 @@ public void HttpMethodActionConstraint_Accept_Preflight_CaseInsensitive(IEnumera
3737
var result = constraint.Accept(context);
3838

3939
// Assert
40-
Assert.True(result, "Request should have been accepted.");
40+
Assert.False(result, "Request should have been rejected.");
4141
}
4242

4343
[Theory]

0 commit comments

Comments
 (0)