Skip to content

Commit 6dc28d8

Browse files
authored
Update CORS middleware to use endpoint metadata (#4460)
1 parent 6187e62 commit 6dc28d8

16 files changed

+705
-49
lines changed

src/Middleware/CORS/samples/SampleDestination/Program.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.IO;
56
using Microsoft.AspNetCore.Hosting;
67
using Microsoft.Extensions.Logging;
@@ -16,10 +17,31 @@ public static void Main(string[] args)
1617
.UseUrls("http://+:9000")
1718
.UseContentRoot(Directory.GetCurrentDirectory())
1819
.ConfigureLogging(factory => factory.AddConsole())
19-
.UseStartup<Startup>()
20+
.UseStartup(GetStartupType())
2021
.Build();
2122

2223
host.Run();
2324
}
25+
26+
private static Type GetStartupType()
27+
{
28+
var startup = Environment.GetEnvironmentVariable("CORS_STARTUP");
29+
if (startup == null)
30+
{
31+
return typeof(Startup);
32+
}
33+
else
34+
{
35+
switch (startup)
36+
{
37+
case "Startup":
38+
return typeof(Startup);
39+
case "StartupWithoutEndpointRouting":
40+
return typeof(StartupWithoutEndpointRouting);
41+
}
42+
}
43+
44+
throw new InvalidOperationException("Could not resolve the startup type. Unexpected CORS_STARTUP environment variable.");
45+
}
2446
}
2547
}
Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System;
54
using System.Net;
65
using System.Text;
76
using System.Threading.Tasks;
@@ -26,66 +25,70 @@ public Startup(ILoggerFactory loggerFactory)
2625

2726
public void ConfigureServices(IServiceCollection services)
2827
{
29-
services.AddCors();
30-
}
31-
32-
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
33-
{
34-
app.Map("/allow-origin", innerBuilder =>
28+
services.AddCors(options =>
3529
{
36-
innerBuilder.UseCors(policy => policy
30+
options.AddPolicy("AllowOrigin", policy => policy
3731
.WithOrigins(DefaultAllowedOrigin)
3832
.AllowAnyMethod()
3933
.AllowAnyHeader());
4034

41-
innerBuilder.UseMiddleware<SampleMiddleware>();
42-
});
43-
44-
app.Map("/allow-header-method", innerBuilder =>
45-
{
46-
innerBuilder.UseCors(policy => policy
35+
options.AddPolicy("AllowHeaderMethod", policy => policy
4736
.WithOrigins(DefaultAllowedOrigin)
4837
.WithHeaders("X-Test", "Content-Type")
4938
.WithMethods("PUT"));
5039

51-
innerBuilder.UseMiddleware<SampleMiddleware>();
52-
});
53-
54-
app.Map("/allow-credentials", innerBuilder =>
55-
{
56-
innerBuilder.UseCors(policy => policy
40+
options.AddPolicy("AllowCredentials", policy => policy
5741
.WithOrigins(DefaultAllowedOrigin)
5842
.AllowAnyHeader()
5943
.WithMethods("GET", "PUT")
6044
.AllowCredentials());
6145

62-
innerBuilder.UseMiddleware<SampleMiddleware>();
63-
});
64-
65-
app.Map("/exposed-header", innerBuilder =>
66-
{
67-
innerBuilder.UseCors(policy => policy
46+
options.AddPolicy("ExposedHeader", policy => policy
6847
.WithOrigins(DefaultAllowedOrigin)
6948
.WithExposedHeaders("X-AllowedHeader", "Content-Length"));
7049

71-
innerBuilder.UseMiddleware<SampleMiddleware>();
72-
});
73-
74-
app.Map("/allow-all", innerBuilder =>
75-
{
76-
innerBuilder.UseCors(policy => policy
50+
options.AddPolicy("AllowAll", policy => policy
7751
.AllowAnyOrigin()
7852
.AllowAnyMethod()
7953
.AllowAnyHeader()
8054
.AllowCredentials());
55+
});
56+
services.AddRouting();
57+
}
8158

82-
innerBuilder.UseMiddleware<SampleMiddleware>();
59+
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
60+
{
61+
app.UseRouting(routing =>
62+
{
63+
routing.Map("/allow-origin", HandleRequest).WithCorsPolicy("AllowOrigin");
64+
routing.Map("/allow-header-method", HandleRequest).WithCorsPolicy("AllowHeaderMethod");
65+
routing.Map("/allow-credentials", HandleRequest).WithCorsPolicy("AllowCredentials");
66+
routing.Map("/exposed-header", HandleRequest).WithCorsPolicy("ExposedHeader");
67+
routing.Map("/allow-all", HandleRequest).WithCorsPolicy("AllowAll");
8368
});
8469

70+
app.UseCors();
71+
72+
app.UseEndpoint();
73+
8574
app.Run(async (context) =>
8675
{
8776
await context.Response.WriteAsync("Hello World!");
8877
});
8978
}
79+
80+
private Task HandleRequest(HttpContext context)
81+
{
82+
var content = Encoding.UTF8.GetBytes("Hello world");
83+
84+
context.Response.Headers["X-AllowedHeader"] = "Test-Value";
85+
context.Response.Headers["X-DisallowedHeader"] = "Test-Value";
86+
87+
context.Response.ContentType = "text/plain; charset=utf-8";
88+
context.Response.ContentLength = content.Length;
89+
context.Response.Body.Write(content, 0, content.Length);
90+
91+
return Task.CompletedTask;
92+
}
9093
}
9194
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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.Net;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Hosting;
7+
using Microsoft.AspNetCore.Http;
8+
using Microsoft.Extensions.DependencyInjection;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace SampleDestination
12+
{
13+
public class StartupWithoutEndpointRouting
14+
{
15+
private static readonly string DefaultAllowedOrigin = $"http://{Dns.GetHostName()}:9001";
16+
private readonly ILogger<StartupWithoutEndpointRouting> _logger;
17+
18+
public StartupWithoutEndpointRouting(ILoggerFactory loggerFactory)
19+
{
20+
_logger = loggerFactory.CreateLogger<StartupWithoutEndpointRouting>();
21+
_logger.LogInformation($"Setting up CORS middleware to allow clients on {DefaultAllowedOrigin}");
22+
}
23+
24+
public void ConfigureServices(IServiceCollection services)
25+
{
26+
services.AddCors();
27+
}
28+
29+
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
30+
{
31+
app.Map("/allow-origin", innerBuilder =>
32+
{
33+
innerBuilder.UseCors(policy => policy
34+
.WithOrigins(DefaultAllowedOrigin)
35+
.AllowAnyMethod()
36+
.AllowAnyHeader());
37+
38+
innerBuilder.UseMiddleware<SampleMiddleware>();
39+
});
40+
41+
app.Map("/allow-header-method", innerBuilder =>
42+
{
43+
innerBuilder.UseCors(policy => policy
44+
.WithOrigins(DefaultAllowedOrigin)
45+
.WithHeaders("X-Test", "Content-Type")
46+
.WithMethods("PUT"));
47+
48+
innerBuilder.UseMiddleware<SampleMiddleware>();
49+
});
50+
51+
app.Map("/allow-credentials", innerBuilder =>
52+
{
53+
innerBuilder.UseCors(policy => policy
54+
.WithOrigins(DefaultAllowedOrigin)
55+
.AllowAnyHeader()
56+
.WithMethods("GET", "PUT")
57+
.AllowCredentials());
58+
59+
innerBuilder.UseMiddleware<SampleMiddleware>();
60+
});
61+
62+
app.Map("/exposed-header", innerBuilder =>
63+
{
64+
innerBuilder.UseCors(policy => policy
65+
.WithOrigins(DefaultAllowedOrigin)
66+
.WithExposedHeaders("X-AllowedHeader", "Content-Length"));
67+
68+
innerBuilder.UseMiddleware<SampleMiddleware>();
69+
});
70+
71+
app.Map("/allow-all", innerBuilder =>
72+
{
73+
innerBuilder.UseCors(policy => policy
74+
.AllowAnyOrigin()
75+
.AllowAnyMethod()
76+
.AllowAnyHeader()
77+
.AllowCredentials());
78+
79+
innerBuilder.UseMiddleware<SampleMiddleware>();
80+
});
81+
82+
app.Run(async (context) =>
83+
{
84+
await context.Response.WriteAsync("Hello World!");
85+
});
86+
}
87+
}
88+
}

src/Middleware/CORS/samples/SampleOrigin/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System.IO;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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 Microsoft.AspNetCore.Cors.Infrastructure;
5+
6+
namespace Microsoft.AspNetCore.Cors
7+
{
8+
/// <summary>
9+
/// Metadata that provides a CORS policy.
10+
/// </summary>
11+
public class CorsPolicyMetadata : ICorsPolicyMetadata
12+
{
13+
public CorsPolicyMetadata(CorsPolicy policy)
14+
{
15+
Policy = policy;
16+
}
17+
18+
/// <summary>
19+
/// The policy which needs to be applied.
20+
/// </summary>
21+
public CorsPolicy Policy { get; }
22+
}
23+
}
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 Microsoft.AspNetCore.Cors;
6+
using Microsoft.AspNetCore.Cors.Infrastructure;
7+
using Microsoft.AspNetCore.Routing;
8+
9+
namespace Microsoft.AspNetCore.Builder
10+
{
11+
/// <summary>
12+
/// CORS extension methods for <see cref="IEndpointConventionBuilder"/>.
13+
/// </summary>
14+
public static class CorsEndpointConventionBuilderExtensions
15+
{
16+
/// <summary>
17+
/// Adds a CORS policy with the specified name to the endpoint(s).
18+
/// </summary>
19+
/// <param name="builder">The endpoint convention builder.</param>
20+
/// <param name="policyName">The CORS policy name.</param>
21+
/// <returns>The original convention builder parameter.</returns>
22+
public static IEndpointConventionBuilder WithCorsPolicy(this IEndpointConventionBuilder builder, string policyName)
23+
{
24+
if (builder == null)
25+
{
26+
throw new ArgumentNullException(nameof(builder));
27+
}
28+
29+
builder.Apply(endpointBuilder =>
30+
{
31+
endpointBuilder.Metadata.Add(new EnableCorsAttribute(policyName));
32+
});
33+
return builder;
34+
}
35+
36+
/// <summary>
37+
/// Adds the specified CORS policy to the endpoint(s).
38+
/// </summary>
39+
/// <param name="builder">The endpoint convention builder.</param>
40+
/// <param name="configurePolicy">A delegate which can use a policy builder to build a policy.</param>
41+
/// <returns>The original convention builder parameter.</returns>
42+
public static IEndpointConventionBuilder WithCorsPolicy(this IEndpointConventionBuilder builder, Action<CorsPolicyBuilder> configurePolicy)
43+
{
44+
if (builder == null)
45+
{
46+
throw new ArgumentNullException(nameof(builder));
47+
}
48+
49+
if (configurePolicy == null)
50+
{
51+
throw new ArgumentNullException(nameof(configurePolicy));
52+
}
53+
54+
var policyBuilder = new CorsPolicyBuilder();
55+
configurePolicy(policyBuilder);
56+
var policy = policyBuilder.Build();
57+
58+
builder.Apply(endpointBuilder =>
59+
{
60+
endpointBuilder.Metadata.Add(new CorsPolicyMetadata(policy));
61+
});
62+
return builder;
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)