Cors Support in MVC.

This commit is contained in:
Harsh Gupta 2015-02-20 11:42:10 -08:00
parent ee4ffea294
commit 015edefa96
32 changed files with 1227 additions and 6 deletions

15
Mvc.sln
View File

@ -152,6 +152,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TempDataWebSite", "test\Web
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TestCommon", "test\Microsoft.AspNet.Mvc.TestCommon\Microsoft.AspNet.Mvc.TestCommon.xproj", "{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CorsWebSite", "test\WebSites\CorsWebSite\CorsWebSite.xproj", "{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -894,6 +896,18 @@ Global
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|x86.ActiveCfg = Release|Any CPU
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|x86.Build.0 = Release|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|x86.ActiveCfg = Debug|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|x86.Build.0 = Debug|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Any CPU.Build.0 = Release|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|x86.ActiveCfg = Release|Any CPU
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -968,5 +982,6 @@ Global
{BCDB13A6-7D6E-485E-8424-A156432B71AC} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{8AEB631E-AB74-4D2E-83FB-8931EE10D9D3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
EndGlobalSection
EndGlobal

View File

@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Cors;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Mvc.Description;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Routing;
@ -21,9 +23,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
private readonly AuthorizationOptions _authorizationOptions;
public DefaultActionModelBuilder(IOptions<AuthorizationOptions> options)
public DefaultActionModelBuilder(IOptions<AuthorizationOptions> authorizationOptions)
{
_authorizationOptions = options?.Options ?? new AuthorizationOptions();
_authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions();
}
/// <inheritdoc />
@ -266,6 +268,18 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
AddRange(actionModel.ActionConstraints, attributes.OfType<IActionConstraintMetadata>());
AddRange(actionModel.Filters, attributes.OfType<IFilter>());
var enableCors = attributes.OfType<IEnableCorsAttribute>().SingleOrDefault();
if (enableCors != null)
{
actionModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName));
}
var disableCors = attributes.OfType<IDisableCorsAttribute>().SingleOrDefault();
if (disableCors != null)
{
actionModel.Filters.Add(new DisableCorsAuthorizationFilter());
}
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
if (policy != null)
{

View File

@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Cors;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Mvc.Description;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Routing;
@ -31,11 +33,11 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public DefaultControllerModelBuilder(
IActionModelBuilder actionModelBuilder,
ILoggerFactory loggerFactory,
IOptions<AuthorizationOptions> options)
IOptions<AuthorizationOptions> authorizationOptions)
{
_actionModelBuilder = actionModelBuilder;
_logger = loggerFactory.CreateLogger<DefaultControllerModelBuilder>();
_authorizationOptions = options?.Options ?? new AuthorizationOptions();
_authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions();
}
/// <inheritdoc />
@ -80,6 +82,18 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
AddRange(controllerModel.Filters, attributes.OfType<IFilter>());
AddRange(controllerModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
var enableCors = attributes.OfType<IEnableCorsAttribute>().SingleOrDefault();
if (enableCors != null)
{
controllerModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName));
}
var disableCors = attributes.OfType<IDisableCorsAttribute>().SingleOrDefault();
if (disableCors != null)
{
controllerModel.Filters.Add(new DisableCorsAuthorizationFilter());
}
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
if (policy != null)
{

View File

@ -11,5 +11,11 @@ namespace Microsoft.AspNet.Mvc
/// User code should order at bigger than 0 or smaller than -2000.
/// </summary>
public static readonly int DefaultFrameworkSortOrder = -1000;
/// <summary>
/// The default order for <see cref="CorsAuthorizationFilter"/>, <see cref="CorsAuthorizationFilterFactory"/>
/// and <see cref="DisableCorsAuthorizationFilter"/>.
/// </summary>
public static readonly int DefaultCorsSortOrder = int.MaxValue - 100;
}
}

View File

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// An <see cref="ICorsAuthorizationFilter"/> which ensures that an action does not run for a pre-flight request.
/// </summary>
public class DisableCorsAuthorizationFilter : ICorsAuthorizationFilter
{
/// <inheritdoc />
public int Order
{
get
{
return DefaultOrder.DefaultCorsSortOrder;
}
}
/// <inheritdoc />
public Task OnAuthorizationAsync([NotNull] AuthorizationContext context)
{
var accessControlRequestMethod =
context.HttpContext.Request.Headers.Get(CorsConstants.AccessControlRequestMethod);
if (string.Equals(
context.HttpContext.Request.Method,
CorsConstants.PreflightHttpMethod,
StringComparison.Ordinal) &&
accessControlRequestMethod != null)
{
// Short circuit if the request is preflight as that should not result in action execution.
context.Result = new HttpStatusCodeResult(StatusCodes.Status200OK);
}
// Let the action be executed.
return Task.FromResult(true);
}
}
}

View File

@ -0,0 +1,94 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A filter which applies the given <see cref="CorsPolicy"/> and adds appropriate response headers.
/// </summary>
public class CorsAuthorizationFilter : ICorsAuthorizationFilter
{
private ICorsService _corsService;
private ICorsPolicyProvider _corsPolicyProvider;
/// <summary>
/// Creates a new instace of <see cref="CorsAuthorizationFilter"/>.
/// </summary>
/// <param name="corsService">The <see cref="ICorsService"/>.</param>
/// <param name="policyProvider">The <see cref="ICorsPolicyProvider"/>.</param>
public CorsAuthorizationFilter(ICorsService corsService, ICorsPolicyProvider policyProvider)
{
_corsService = corsService;
_corsPolicyProvider = policyProvider;
}
/// <summary>
/// The policy name used to fetch a <see cref="CorsPolicy"/>.
/// </summary>
public string PolicyName { get; set; }
/// <inheritdoc />
public int Order
{
get
{
return DefaultOrder.DefaultCorsSortOrder;
}
}
/// <inheritdoc />
public async Task OnAuthorizationAsync([NotNull] AuthorizationContext context)
{
// If this filter is not closest to the action, it is not applicable.
if (!IsClosestToAction(context.Filters))
{
return;
}
var httpContext = context.HttpContext;
var request = httpContext.Request;
if (request.Headers.ContainsKey(CorsConstants.Origin))
{
var policy = await _corsPolicyProvider.GetPolicyAsync(httpContext, PolicyName);
var result = _corsService.EvaluatePolicy(context.HttpContext, policy);
_corsService.ApplyResult(result, context.HttpContext.Response);
var accessControlRequestMethod =
httpContext.Request.Headers.Get(CorsConstants.AccessControlRequestMethod);
if (string.Equals(
request.Method,
CorsConstants.PreflightHttpMethod,
StringComparison.Ordinal) &&
accessControlRequestMethod != null)
{
// If this was a preflight, there is no need to run anything else.
// Also the response is always 200 so that anyone after mvc can handle the pre flight request.
context.Result = new HttpStatusCodeResult(StatusCodes.Status200OK);
await Task.FromResult(true);
}
// Continue with other filters and action.
}
}
private bool IsClosestToAction(IEnumerable<IFilter> filters)
{
// If there are multiple ICorsAuthorizationFilter which are defined at the class and
// at the action level, the one closest to the action overrides the others.
// Since filterdescriptor collection is ordered (the last filter is the one closest to the action),
// we apply this constraint only if there is no ICorsAuthorizationFilter after this.
return filters.Last(filter => filter is ICorsAuthorizationFilter) == this;
}
}
}

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A filter factory which creates a new instance of <see cref="CorsAuthorizationFilter"/>.
/// </summary>
public class CorsAuthorizationFilterFactory : IFilterFactory, IOrderedFilter
{
private readonly string _policyName;
/// <summary>
/// Creates a new insntace of <see cref="CorsAuthorizationFilterFactory"/>.
/// </summary>
/// <param name="policyName"></param>
public CorsAuthorizationFilterFactory(string policyName)
{
_policyName = policyName;
}
/// <inheritdoc />
public int Order
{
get
{
return DefaultOrder.DefaultCorsSortOrder;
}
}
public IFilter CreateInstance([NotNull] IServiceProvider serviceProvider)
{
var filter = serviceProvider.GetRequiredService<CorsAuthorizationFilter>();
filter.PolicyName = _policyName;
return filter;
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.AspNet.Cors.Core;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc
@ -54,8 +55,22 @@ namespace Microsoft.AspNet.Mvc
}
var request = context.RouteContext.HttpContext.Request;
var method = request.Method;
if (request.Headers.ContainsKey(CorsConstants.Origin))
{
// Update the http method if it is preflight request.
var accessControlRequestMethod = request.Headers.Get(CorsConstants.AccessControlRequestMethod);
if (string.Equals(
request.Method,
CorsConstants.PreflightHttpMethod,
StringComparison.Ordinal) &&
accessControlRequestMethod != null)
{
method = accessControlRequestMethod;
}
}
return (HttpMethods.Any(m => m.Equals(request.Method, StringComparison.Ordinal)));
return (HttpMethods.Any(m => m.Equals(method, StringComparison.Ordinal)));
}
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A filter which can be used to enable/disable cors support for a resource.
/// </summary>
public interface ICorsAuthorizationFilter : IAsyncAuthorizationFilter, IOrderedFilter
{
}
}

View File

@ -7,6 +7,7 @@
"dependencies": {
"Microsoft.AspNet.Authentication": "1.0.0-*",
"Microsoft.AspNet.Authorization": "1.0.0-*",
"Microsoft.AspNet.Cors.Core": "1.0.0-*",
"Microsoft.AspNet.DataProtection": "1.0.0-*",
"Microsoft.AspNet.Diagnostics.Interfaces": "1.0.0-*",
"Microsoft.AspNet.FileProviders": "1.0.0-*",

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.Internal;
@ -87,6 +88,7 @@ namespace Microsoft.Framework.DependencyInjection
services.AddOptions();
services.AddDataProtection();
services.AddRouting();
services.AddCors();
services.AddAuthorization();
services.AddWebEncoders();
services.Configure<RouteOptions>(

View File

@ -75,6 +75,7 @@ namespace Microsoft.AspNet.Mvc
services.AddTransient<IFilterProvider, DefaultFilterProvider>();
services.AddTransient<FormatFilter, FormatFilter>();
services.AddTransient<CorsAuthorizationFilter, CorsAuthorizationFilter>();
// Dataflow - ModelBinding, Validation and Formatting
//

View File

@ -6,6 +6,7 @@
},
"dependencies": {
"Microsoft.AspNet.Authorization": "1.0.0-*",
"Microsoft.AspNet.Cors": "1.0.0-*",
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
"Microsoft.AspNet.Mvc.Razor": "6.0.0-*",
"Microsoft.Framework.Caching.Memory": "1.0.0-*",

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Cors.Core;
using Microsoft.Framework.Internal;
using Microsoft.Framework.OptionsModel;
using Moq;
@ -284,6 +285,38 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
Assert.False(isValid);
}
[Fact]
public void BuildActionModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory()
{
// Arrange
var builder = new DefaultActionModelBuilder(authorizationOptions: null);
var typeInfo = typeof(EnableCorsController).GetTypeInfo();
var method = typeInfo.GetMethod("Action");
// Act
var actions = builder.BuildActionModels(typeInfo, method);
// Assert
var action = Assert.Single(actions);
Assert.Single(action.Filters, f => f is CorsAuthorizationFilterFactory);
}
[Fact]
public void BuildActionModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
{
// Arrange
var builder = new DefaultActionModelBuilder(authorizationOptions: null);
var typeInfo = typeof(DisableCorsController).GetTypeInfo();
var method = typeInfo.GetMethod("Action");
// Act
var actions = builder.BuildActionModels(typeInfo, method);
// Assert
var action = Assert.Single(actions);
Assert.True(action.Filters.Any(f => f is DisableCorsAuthorizationFilter));
}
[Fact]
public void GetActions_ConventionallyRoutedAction_WithoutHttpConstraints()
{
@ -927,6 +960,22 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public void Invalid() { }
}
private class EnableCorsController
{
[EnableCors("policy")]
public void Action()
{
}
}
private class DisableCorsController
{
[DisableCors]
public void Action()
{
}
}
// Here the constraints on the methods are acting as an IActionHttpMethodProvider and
// not as an IRouteTemplateProvider given that there is no RouteAttribute
// on the controller and the template for all the constraints on a method is null.

View File

@ -6,8 +6,11 @@ using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.Framework.Internal;
using Microsoft.Framework.OptionsModel;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ApplicationModels
@ -47,6 +50,48 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
Assert.True(model.Filters.Any(f => f is AuthorizeFilter));
}
[Fact]
public void BuildControllerModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory()
{
// Arrange
var corsOptions = new CorsOptions();
corsOptions.AddPolicy("policy", new CorsPolicy());
var mockOptions = new Mock<IOptions<CorsOptions>>();
mockOptions.SetupGet(o => o.Options)
.Returns(corsOptions);
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
authorizationOptions: null);
var typeInfo = typeof(CorsController).GetTypeInfo();
// Act
var model = builder.BuildControllerModel(typeInfo);
// Assert
Assert.Single(model.Filters, f => f is CorsAuthorizationFilterFactory);
}
[Fact]
public void BuildControllerModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
{
// Arrange
var corsOptions = new CorsOptions();
corsOptions.AddPolicy("policy", new CorsPolicy());
var mockOptions = new Mock<IOptions<CorsOptions>>();
mockOptions.SetupGet(o => o.Options)
.Returns(corsOptions);
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
NullLoggerFactory.Instance,
authorizationOptions: null);
var typeInfo = typeof(DisableCorsController).GetTypeInfo();
// Act
var model = builder.BuildControllerModel(typeInfo);
// Assert
Assert.True(model.Filters.Any(f => f is DisableCorsAuthorizationFilter));
}
// This class has a filter attribute, but doesn't implement any filter interfaces,
// so ControllerFilter is not present.
[Fact]
@ -113,6 +158,16 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{
}
[EnableCors("policy")]
public class CorsController
{
}
[DisableCors]
public class DisableCorsController
{
}
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
{
public Task OnActionExecutionAsync(

View File

@ -9,6 +9,7 @@ using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Http.Core.Collections;
using Microsoft.AspNet.Mvc.ActionConstraints;
using Microsoft.AspNet.Mvc.ApplicationModels;
using Microsoft.AspNet.Mvc.Core;
@ -867,7 +868,7 @@ namespace Microsoft.AspNet.Mvc
var request = new Mock<HttpRequest>(MockBehavior.Strict);
request.SetupGet(r => r.Method).Returns(httpMethod);
request.SetupGet(r => r.Path).Returns(new PathString());
request.SetupGet(r => r.Headers).Returns(new HeaderDictionary());
httpContext.SetupGet(c => c.Request).Returns(request.Object);
httpContext.SetupGet(c => c.RequestServices).Returns(serviceContainer);

View File

@ -0,0 +1,281 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Moq;
using Newtonsoft.Json.Utilities;
using Xunit;
namespace Microsoft.AspNet.Mvc.Test
{
public class CorsAuthorizationFilterTest
{
[Fact]
public async Task PreFlightRequest_SuccessfulMatch_WritesHeaders()
{
// Arrange
var mockEngine = GetPassingEngine(supportsCredentials:true);
var filter = GetFilter(mockEngine);
var authorizationContext = GetAuthorizationContext(
new[] { new FilterDescriptor(filter, FilterScope.Action) },
GetRequestHeaders(true),
isPreflight: true);
// Act
await filter.OnAuthorizationAsync(authorizationContext);
await authorizationContext.Result.ExecuteResultAsync(authorizationContext);
// Assert
var response = authorizationContext.HttpContext.Response;
Assert.Equal(200, response.StatusCode);
Assert.Equal("http://example.com", response.Headers[CorsConstants.AccessControlAllowOrigin]);
Assert.Equal("header1,header2", response.Headers[CorsConstants.AccessControlAllowHeaders]);
// Notice: GET header gets filtered because it is a simple header.
Assert.Equal("PUT", response.Headers[CorsConstants.AccessControlAllowMethods]);
Assert.Equal("exposed1,exposed2", response.Headers[CorsConstants.AccessControlExposeHeaders]);
Assert.Equal("123", response.Headers[CorsConstants.AccessControlMaxAge]);
Assert.Equal("true", response.Headers[CorsConstants.AccessControlAllowCredentials]);
}
[Fact]
public async Task PreFlight_FailedMatch_Writes200()
{
// Arrange
var mockEngine = GetFailingEngine();
var filter = GetFilter(mockEngine);
var authorizationContext = GetAuthorizationContext(
new[] { new FilterDescriptor(filter, FilterScope.Action) },
GetRequestHeaders(),
isPreflight: true);
// Act
await filter.OnAuthorizationAsync(authorizationContext);
await authorizationContext.Result.ExecuteResultAsync(authorizationContext);
// Assert
Assert.Equal(200, authorizationContext.HttpContext.Response.StatusCode);
Assert.Empty(authorizationContext.HttpContext.Response.Headers);
}
[Fact]
public async Task CorsRequest_SuccessfulMatch_WritesHeaders()
{
// Arrange
var mockEngine = GetPassingEngine(supportsCredentials: true);
var filter = GetFilter(mockEngine);
var authorizationContext = GetAuthorizationContext(
new[] { new FilterDescriptor(filter, FilterScope.Action) },
GetRequestHeaders(true),
isPreflight: true);
// Act
await filter.OnAuthorizationAsync(authorizationContext);
await authorizationContext.Result.ExecuteResultAsync(authorizationContext);
// Assert
var response = authorizationContext.HttpContext.Response;
Assert.Equal(200, response.StatusCode);
Assert.Equal("http://example.com", response.Headers[CorsConstants.AccessControlAllowOrigin]);
Assert.Equal("exposed1,exposed2", response.Headers[CorsConstants.AccessControlExposeHeaders]);
}
[Fact]
public async Task CorsRequest_FailedMatch_Writes200()
{
// Arrange
var mockEngine = GetFailingEngine();
var filter = GetFilter(mockEngine);
var authorizationContext = GetAuthorizationContext(
new[] { new FilterDescriptor(filter, FilterScope.Action) },
GetRequestHeaders(),
isPreflight: false);
// Act
await filter.OnAuthorizationAsync(authorizationContext);
// Assert
Assert.Equal(200, authorizationContext.HttpContext.Response.StatusCode);
Assert.Empty(authorizationContext.HttpContext.Response.Headers);
}
private CorsAuthorizationFilter GetFilter(ICorsService corsService)
{
var policyProvider = new Mock<ICorsPolicyProvider>();
policyProvider
.Setup(o => o.GetPolicyAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
.Returns(Task.FromResult(new CorsPolicy()));
return new CorsAuthorizationFilter(corsService, policyProvider.Object)
{
PolicyName = string.Empty
};
}
private AuthorizationContext GetAuthorizationContext(
FilterDescriptor[] filterDescriptors,
RequestHeaders headers = null,
bool isPreflight = false)
{
// HttpContext
var httpContext = new DefaultHttpContext();
if (headers != null)
{
httpContext.Request.Headers.Add(CorsConstants.AccessControlRequestHeaders, headers.Headers.Split(','));
httpContext.Request.Headers.Add(CorsConstants.AccessControlRequestMethod, new[] { headers.Method });
httpContext.Request.Headers.Add(CorsConstants.AccessControlExposeHeaders, headers.ExposedHeaders.Split(','));
httpContext.Request.Headers.Add(CorsConstants.Origin, new[] { headers.Origin });
}
var method = isPreflight ? CorsConstants.PreflightHttpMethod : "GET";
httpContext.Request.Method = method;
// AuthorizationContext
var actionContext = new ActionContext(
httpContext: httpContext,
routeData: new RouteData(),
actionDescriptor: new ActionDescriptor() { FilterDescriptors = filterDescriptors });
var authorizationContext = new AuthorizationContext(
actionContext,
filterDescriptors.Select(filter => filter.Filter).ToList()
);
return authorizationContext;
}
private ICorsService GetFailingEngine()
{
var mockEngine = new Mock<ICorsService>();
var result = GetCorsResult("http://example.com");
mockEngine
.Setup(o => o.EvaluatePolicy(It.IsAny<HttpContext>(), It.IsAny<CorsPolicy>()))
.Returns(result);
return mockEngine.Object;
}
private ICorsService GetPassingEngine(bool supportsCredentials = false)
{
var mockEngine = new Mock<ICorsService>();
var result = GetCorsResult(
"http://example.com",
new List<string> { "header1", "header2" },
new List<string> { "PUT" },
new List<string> { "exposed1", "exposed2" },
123,
supportsCredentials);
mockEngine
.Setup(o => o.EvaluatePolicy(It.IsAny<HttpContext>(), It.IsAny<CorsPolicy>()))
.Returns(result);
mockEngine
.Setup(o => o.ApplyResult(It.IsAny<CorsResult>(), It.IsAny<HttpResponse>()))
.Callback<CorsResult, HttpResponse>((result1, response1) =>
{
var headers = response1.Headers;
headers.Set(
CorsConstants.AccessControlMaxAge,
result1.PreflightMaxAge.Value.TotalSeconds.ToString());
headers.Add(CorsConstants.AccessControlAllowOrigin, new[] { result1.AllowedOrigin });
if (result1.SupportsCredentials)
{
headers.Add(CorsConstants.AccessControlAllowCredentials, new[] { "true" });
}
headers.Add(CorsConstants.AccessControlAllowHeaders, result1.AllowedHeaders.ToArray());
headers.Add(CorsConstants.AccessControlAllowMethods, result1.AllowedMethods.ToArray());
headers.Add(CorsConstants.AccessControlExposeHeaders, result1.AllowedExposedHeaders.ToArray());
});
return mockEngine.Object;
}
private RequestHeaders GetRequestHeaders(bool supportsCredentials = false)
{
return new RequestHeaders
{
Origin = "http://example.com",
Headers = "header1,header2",
Method = "GET",
ExposedHeaders = "exposed1,exposed2",
};
}
private CorsResult GetCorsResult(
string origin = null,
IList<string> headers = null,
IList<string> methods = null,
IList<string> exposedHeaders = null,
long? preFlightMaxAge = null,
bool? supportsCredentials = null)
{
var result = new CorsResult();
if (origin != null)
{
result.AllowedOrigin = origin;
}
if (headers != null)
{
AddRange(result.AllowedHeaders, headers);
}
if (methods != null)
{
AddRange(result.AllowedMethods, methods);
}
if (exposedHeaders != null)
{
AddRange(result.AllowedExposedHeaders, exposedHeaders);
}
if (preFlightMaxAge != null)
{
result.PreflightMaxAge = TimeSpan.FromSeconds(preFlightMaxAge.Value);
}
if (supportsCredentials != null)
{
result.SupportsCredentials = supportsCredentials.Value;
}
return result;
}
private void AddRange(IList<string> target, IList<string> source)
{
foreach (var item in source)
{
target.Add(item);
}
}
private class RequestHeaders
{
public string Origin { get; set; }
public string Headers { get; set; }
public string ExposedHeaders { get; set; }
public string Method { get; set; }
}
}
}

View File

@ -0,0 +1,104 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Http;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class CorsMiddlewareTests
{
private const string SiteName = nameof(CorsMiddlewareWebSite);
private readonly Action<IApplicationBuilder> _app = new CorsMiddlewareWebSite.Startup().Configure;
[Theory]
[InlineData("GET")]
[InlineData("HEAD")]
[InlineData("POST")]
public async Task ResourceWithSimpleRequestPolicy_Allows_SimpleRequests(string method)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
var origin = "http://example.com";
var requestBuilder = server
.CreateRequest("http://localhost/CorsMiddleware/GetExclusiveContent")
.AddHeader(CorsConstants.Origin, origin);
// Act
var response = await requestBuilder.SendAsync(method);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("exclusive", content);
var responseHeaders = response.Headers;
var header = Assert.Single(response.Headers);
Assert.Equal(CorsConstants.AccessControlAllowOrigin, header.Key);
Assert.Equal(new[] { "http://example.com" }, header.Value.ToArray());
}
[Theory]
[InlineData("GET")]
[InlineData("HEAD")]
[InlineData("POST")]
[InlineData("PUT")]
public async Task PolicyFailed_Disallows_PreFlightRequest(string method)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Adding a custom header makes it a non simple request.
var requestBuilder = server
.CreateRequest("http://localhost/CorsMiddleware/GetExclusiveContent")
.AddHeader(CorsConstants.Origin, "http://example.com")
.AddHeader(CorsConstants.AccessControlRequestMethod, method)
.AddHeader(CorsConstants.AccessControlRequestHeaders, "Custom");
// Act
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
// Assert
// Middleware applied the policy and since that did not pass, there were no access control headers.
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
Assert.Empty(response.Headers);
// It should short circuit and hence no result.
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(string.Empty, content);
}
[Fact]
public async Task PolicyFailed_Allows_ActualRequest_WithMissingResponseHeaders()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Adding a custom header makes it a non simple request.
var requestBuilder = server
.CreateRequest("http://localhost/CorsMiddleware/GetExclusiveContent")
.AddHeader(CorsConstants.Origin, "http://example2.com");
// Act
var response = await requestBuilder.SendAsync("PUT");
// Assert
// Middleware applied the policy and since that did not pass, there were no access control headers.
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Empty(response.Headers);
// It still has executed the action.
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("exclusive", content);
}
}
}

View File

@ -0,0 +1,231 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Http;
using Xunit;
namespace Microsoft.AspNet.Mvc.FunctionalTests
{
public class CorsTests
{
private const string SiteName = nameof(CorsWebSite);
private readonly Action<IApplicationBuilder> _app = new CorsWebSite.Startup().Configure;
[Theory]
[InlineData("GET")]
[InlineData("HEAD")]
[InlineData("POST")]
public async Task ResourceWithSimpleRequestPolicy_Allows_SimpleRequests(string method)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
var origin = "http://example.com";
var requestBuilder = server
.CreateRequest("http://localhost/Cors/GetBlogComments")
.AddHeader(CorsConstants.Origin, origin);
// Act
var response = await requestBuilder.SendAsync(method);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("[\"comment1\",\"comment2\",\"comment3\"]", content);
var responseHeaders = response.Headers;
var header = Assert.Single(response.Headers);
Assert.Equal(CorsConstants.AccessControlAllowOrigin, header.Key);
Assert.Equal(new[] { "*" }, header.Value.ToArray());
}
[Theory]
[InlineData("GET")]
[InlineData("HEAD")]
[InlineData("POST")]
[InlineData("PUT")]
public async Task PolicyFailed_Disallows_PreFlightRequest(string method)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Adding a custom header makes it a non simple request.
var requestBuilder = server
.CreateRequest("http://localhost/Cors/GetBlogComments")
.AddHeader(CorsConstants.Origin, "http://example.com")
.AddHeader(CorsConstants.AccessControlRequestMethod, method)
.AddHeader(CorsConstants.AccessControlRequestHeaders, "Custom");
// Act
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
// Assert
// MVC applied the policy and since that did not pass, there were no access control headers.
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Empty(response.Headers);
// It should short circuit and hence no result.
var content = await response.Content.ReadAsStringAsync();
Assert.Equal(string.Empty, content);
}
[Fact]
public async Task SuccessfulCorsRequest_AllowsCredentials_IfThePolicyAllowsCredentials()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Adding a custom header makes it a non simple request.
var requestBuilder = server
.CreateRequest("http://localhost/Cors/EditUserComment?userComment=abcd")
.AddHeader(CorsConstants.Origin, "http://example.com")
.AddHeader(CorsConstants.AccessControlExposeHeaders, "exposed1,exposed2");
// Act
var response = await requestBuilder.SendAsync("PUT");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var responseHeaders = response.Headers;
Assert.Equal(
new[] { "http://example.com" },
responseHeaders.GetValues(CorsConstants.AccessControlAllowOrigin).ToArray());
Assert.Equal(
new[] { "true" },
responseHeaders.GetValues(CorsConstants.AccessControlAllowCredentials).ToArray());
Assert.Equal(
new[] { "exposed1", "exposed2" },
responseHeaders.GetValues(CorsConstants.AccessControlExposeHeaders).ToArray());
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("abcd", content);
}
[Fact]
public async Task SuccessfulPreflightRequest_AllowsCredentials_IfThePolicyAllowsCredentials()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Adding a custom header makes it a non simple request.
var requestBuilder = server
.CreateRequest("http://localhost/Cors/EditUserComment?userComment=abcd")
.AddHeader(CorsConstants.Origin, "http://example.com")
.AddHeader(CorsConstants.AccessControlRequestMethod, "PUT")
.AddHeader(CorsConstants.AccessControlRequestHeaders, "header1,header2");
// Act
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var responseHeaders = response.Headers;
Assert.Equal(
new[] { "http://example.com" },
responseHeaders.GetValues(CorsConstants.AccessControlAllowOrigin).ToArray());
Assert.Equal(
new[] { "true" },
responseHeaders.GetValues(CorsConstants.AccessControlAllowCredentials).ToArray());
Assert.Equal(
new[] { "header1", "header2" },
responseHeaders.GetValues(CorsConstants.AccessControlAllowHeaders).ToArray());
Assert.Equal(
new[] { "PUT" },
responseHeaders.GetValues(CorsConstants.AccessControlAllowMethods).ToArray());
var content = await response.Content.ReadAsStringAsync();
Assert.Empty(content);
}
[Fact]
public async Task PolicyFailed_Allows_ActualRequest_WithMissingResponseHeaders()
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Adding a custom header makes it a non simple request.
var requestBuilder = server
.CreateRequest("http://localhost/Cors/GetUserComments")
.AddHeader(CorsConstants.Origin, "http://example2.com");
// Act
var response = await requestBuilder.SendAsync("PUT");
// Assert
// MVC applied the policy and since that did not pass, there were no access control headers.
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Empty(response.Headers);
// It still have executed the action.
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("[\"usercomment1\",\"usercomment2\",\"usercomment3\"]", content);
}
[Theory]
[InlineData("GET")]
[InlineData("HEAD")]
[InlineData("POST")]
public async Task DisableCors_ActionsCanOverride_ControllerLevel(string method)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Exclusive content is not available on other sites.
var requestBuilder = server
.CreateRequest("http://localhost/Cors/GetExclusiveContent")
.AddHeader(CorsConstants.Origin, "http://example.com");
// Act
var response = await requestBuilder.SendAsync(method);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
// Since there are no response headers, the client should step in to block the content.
Assert.Empty(response.Headers);
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("exclusive", content);
}
[Theory]
[InlineData("GET")]
[InlineData("HEAD")]
[InlineData("POST")]
public async Task DisableCors_PreFlight_ActionsCanOverride_ControllerLevel(string method)
{
// Arrange
var server = TestHelper.CreateServer(_app, SiteName);
var client = server.CreateClient();
// Exclusive content is not available on other sites.
var requestBuilder = server
.CreateRequest("http://localhost/Cors/GetExclusiveContent")
.AddHeader(CorsConstants.Origin, "http://example.com")
.AddHeader(CorsConstants.AccessControlRequestMethod, method)
.AddHeader(CorsConstants.AccessControlRequestHeaders, "Custom");
// Act
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
// Assert
// Since there are no response headers, the client should step in to block the content.
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Empty(response.Headers);
// Nothing gets executed for a pre-flight request.
var content = await response.Content.ReadAsStringAsync();
Assert.Empty(content);
}
}
}

View File

@ -20,6 +20,8 @@
"ContentNegotiationWebSite": "1.0.0",
"ControllerDiscoveryConventionsWebSite": "1.0.0",
"ControllersFromServicesWebSite": "1.0.0",
"CorsWebSite": "1.0.0",
"CorsMiddlewareWebSite": "1.0.0",
"CustomRouteWebSite": "1.0.0",
"ErrorPageMiddlewareWebSite": "1.0.0",
"FilesWebSite": "1.0.0",

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace CorsMiddlewareWebSite
{
[Route("CorsMiddleWare/[action]")]
public class BlogController : Controller
{
public string GetExclusiveContent()
{
return "exclusive";
}
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>94ba134d-04b3-48aa-ba55-5a4db8640f2d</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>41642</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Cors;
using Microsoft.AspNet.Mvc;
using Microsoft.Framework.DependencyInjection;
namespace CorsMiddlewareWebSite
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var configuration = app.GetTestConfiguration();
app.UseServices(services =>
{
services.AddMvc();
});
app.UseCors(policy => policy.WithOrigins("http://example.com"));
app.UseMvc();
}
}
}

View File

@ -0,0 +1,22 @@
{
"commands": {
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
},
"dependencies": {
"Kestrel": "1.0.0-*",
"Microsoft.AspNet.Cors": "1.0.0-*",
"Microsoft.AspNet.Cors.Core": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.Xml": "6.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
},
"frameworks": {
"dnx451": { },
"dnx50": { }
},
"webroot": "wwwroot"
}

View File

@ -0,0 +1,4 @@
CorsMiddlewareWebSite
===
This web site illustrates how to use CorsMiddleware to apply a policy for entire application.

View File

@ -0,0 +1 @@
HelloWorld

View File

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNet.Cors.Core;
using Microsoft.AspNet.Mvc;
namespace CorsWebSite
{
[Route("Cors/[action]")]
[EnableCors("AllowAnySimpleRequest")]
public class BlogController : Controller
{
public IEnumerable<string> GetBlogComments(int id)
{
return new[] { "comment1", "comment2", "comment3" };
}
[EnableCors("AllowSpecificOrigin")]
public IEnumerable<string> GetUserComments(int id)
{
return new[] { "usercomment1", "usercomment2", "usercomment3" };
}
[DisableCors]
[AcceptVerbs("HEAD", "GET", "POST")]
public string GetExclusiveContent()
{
return "exclusive";
}
[EnableCors("WithCredentialsAnyOrigin")]
public string EditUserComment(int id, string userComment)
{
return userComment;
}
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>94ba134d-04b3-48aa-ba55-5a4db8640f2d</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>41642</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,60 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc;
using Microsoft.Framework.DependencyInjection;
namespace CorsWebSite
{
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var configuration = app.GetTestConfiguration();
app.UseServices(services =>
{
services.AddMvc();
services.ConfigureCors(options =>
{
options.AddPolicy(
"AllowAnySimpleRequest",
builder =>
{
builder.AllowAnyOrigin()
.WithMethods("GET", "POST", "HEAD");
});
options.AddPolicy(
"AllowSpecificOrigin",
builder =>
{
builder.WithOrigins("http://example.com");
});
options.AddPolicy(
"WithCredentials",
builder =>
{
builder.AllowCredentials()
.WithOrigins("http://example.com");
});
options.AddPolicy(
"WithCredentialsAnyOrigin",
builder =>
{
builder.AllowCredentials()
.AllowAnyOrigin()
.AllowAnyHeader()
.WithMethods("PUT", "POST")
.WithExposedHeaders("exposed1", "exposed2");
});
});
});
app.UseMvc();
}
}
}

View File

@ -0,0 +1,22 @@
{
"commands": {
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
},
"dependencies": {
"Kestrel": "1.0.0-*",
"Microsoft.AspNet.Cors": "1.0.0-*",
"Microsoft.AspNet.Cors.Core": "1.0.0-*",
"Microsoft.AspNet.Mvc": "6.0.0-*",
"Microsoft.AspNet.Mvc.Xml": "6.0.0-*",
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
},
"frameworks": {
"dnx451": { },
"dnx50": { }
},
"webroot": "wwwroot"
}

View File

@ -0,0 +1,4 @@
CorsWebSite
===
This web site illustrates how to configure actions to allow/disallow cross origin requests.

View File

@ -0,0 +1 @@
HelloWorld