在 ASP.NET Core 中启用跨域请求(CORS)

本文介绍如何在 ASP.NET Core 的应用程序中启用 CORS。
浏览器安全性可防止网页向不处理网页的域发送请求。 此限制称为同域策略。 同域策略可防止恶意站点从另一站点读取敏感数据。 有时,你可能想要允许其他站点对你的应用进行跨域请求。 有关详细信息,请参阅MOZILLA CORS 一文。
跨源资源共享(CORS):
是一种 W3C 标准,可让服务器放宽相同的源策略。
不是一项安全功能,CORS 放宽 security。 API 不能通过允许 CORS 来更安全。 有关详细信息,请参阅CORS 的工作原理。
允许服务器明确允许一些跨源请求,同时拒绝其他请求。
比早期的技术(如JSONP)更安全且更灵活。
查看或下载示例代码(如何下载)
同一原点
如果两个 Url 具有相同的方案、主机和端口(RFC 6454),则它们具有相同的源。
这两个 Url 具有相同的源:
https://example.com/foo.html
https://example.com/bar.html
这些 Url 的起源不同于前两个 Url:
https://example.net:不同的域
https://www.example.com/foo.html:不同的子域
http://example.com/foo.html:不同方案
https://example.com:9000/foo.html:不同的端口
启用 CORS
有三种方法可启用 CORS:
使用命名策略或默认策略的中间件。
使用终结点路由。
带有[EnableCors]属性的。
通过命名策略使用[EnableCors]属性,可在限制支持 CORS 的终结点时提供最佳控制。
以下各节详细介绍了每种方法。

具有命名策略和中间件的 CORS
CORS 中间件处理跨域请求。 以下代码将 CORS 策略应用到具有指定来源的所有应用的终结点:
C#

复制
public class Startup
{
readonly string MyAllowSpecificOrigins = “_myAllowSpecificOrigins”;

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(name: MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com");
                          });
    });

    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseCors(MyAllowSpecificOrigins);

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

}
前面的代码:
将策略名称设置为 _myAllowSpecificOrigins 。 策略名称为任意名称。
调用 UseCors 扩展方法并指定 _myAllowSpecificOrigins CORS 策略。 UseCors添加 CORS 中间件。 必须将对的调用 UseCors 置于之后 UseRouting 但在之前 UseAuthorization 。 有关详细信息,请参阅中间件顺序。
AddCors使用lambda 表达式调用。 Lambda 采用 CorsPolicyBuilder 对象。 本文稍后将介绍配置选项,如 WithOrigins 。
启用 _myAllowSpecificOrigins 所有控制器终结点的 CORS 策略。 请参阅终结点路由,将 CORS 策略应用到特定终结点。
通过终结点路由,CORS 中间件必须配置为在对和的调用之间执行 UseRouting UseEndpoints 。
有关测试代码的说明,请参阅测试 CORS ,如以上代码所示。
AddCors方法调用将 CORS 服务添加到应用的服务容器:
C#

复制
public class Startup
{
readonly string MyAllowSpecificOrigins = “_myAllowSpecificOrigins”;

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(name: MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com");
                          });
    });

    services.AddControllers();
}

有关详细信息,请参阅本文档中的CORS 策略选项。
这些 CorsPolicyBuilder 方法可以链接在一起,如以下代码所示:
C#

复制
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins(“http://example.com”,
“http://www.contoso.com”)
.AllowAnyHeader()
.AllowAnyMethod();
});
});

services.AddControllers();

}
注意:指定的 URL不能包含尾随斜杠( / )。 如果 URL 以结尾 / ,则比较返回, false 不返回任何标头。

具有默认策略和中间件的 CORS
以下突出显示的代码将启用默认 CORS 策略:
C#

复制
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.WithOrigins(“http://example.com”,
“http://www.contoso.com”);
});
});

    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

}
前面的代码将默认的 CORS 策略应用到所有控制器终结点。

通过终结点路由启用 Cors
目前使用每个终结点启用 CORS 不 RequireCors 支持自动预检请求。 not 有关详细信息,请参阅此 GitHub 问题和测试与终结点路由和 [HTTPOPTIONS] 的 CORS。
使用终结点路由,可以使用一组扩展方法在每个终结点上启用 CORS RequireCors :
C#

复制
public class Startup
{
readonly string MyAllowSpecificOrigins = “_myAllowSpecificOrigins”;

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(name: MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com");
                          });
    });

    services.AddControllers();
    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/echo",
            context => context.Response.WriteAsync("echo"))
            .RequireCors(MyAllowSpecificOrigins);

        endpoints.MapControllers()
                 .RequireCors(MyAllowSpecificOrigins);

        endpoints.MapGet("/echo2",
            context => context.Response.WriteAsync("echo2"));

        endpoints.MapRazorPages();
    });
}

}
在上述代码中:
app.UseCors启用 CORS 中间件。 由于尚未配置默认策略,因此 app.UseCors() 单独不启用 CORS。
/echo和控制器端点允许使用指定策略的跨域请求。
/echo2和 Razor Pages 终结点不允许跨源请求,因为未指定默认策略。
[DisableCors]特性不会禁用通过终结点路由启用的 CORS RequireCors 。
请参阅测试与终结点路由和 [HttpOptions] 的 CORS ,获取与前面类似的代码测试说明。

使用属性启用 CORS
使用[EnableCors]属性启用 CORS,并将命名策略应用到只有那些需要 CORS 的终结点提供了精细的控制。
[EnableCors]属性提供了一种用于全局应用 CORS 的替代方法。 [EnableCors]特性启用所选终结点的 CORS,而不是所有终结点:
[EnableCors]指定默认策略。
[EnableCors("{Policy String}")]指定命名策略。
[EnableCors]特性可应用于:
Razor分页PageModel
控制器
控制器操作方法
可以将不同的策略应用到具有属性的控制器、页面模型或操作方法 [EnableCors] 。 如果将 [EnableCors] 属性应用于控制器、页面模型或操作方法,并在中间件中启用了 CORS,则会应用这两种策略。 建议不要结合策略。使用 [EnableCors] 特性或中间件,而不是在同一应用中。
下面的代码将不同的策略应用于每个方法:
C#

复制
[Route(“api/[controller]”)]
[ApiController]
public class WidgetController : ControllerBase
{
// GET api/values
[EnableCors(“AnotherPolicy”)]
[HttpGet]
public ActionResult<IEnumerable> Get()
{
return new string[] { “green widget”, “red widget” };
}

// GET api/values/5
[EnableCors("Policy1")]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
    return id switch
    {
        1 => "green widget",
        2 => "red widget",
        _ => NotFound(),
    };
}

}
下面的代码创建两个 CORS 策略:
C#

复制
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy("Policy1",
            builder =>
            {
                builder.WithOrigins("http://example.com",
                                    "http://www.contoso.com");
            });

        options.AddPolicy("AnotherPolicy",
            builder =>
            {
                builder.WithOrigins("http://www.contoso.com")
                                    .AllowAnyHeader()
                                    .AllowAnyMethod();
            });
    });

    services.AddControllers();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

}
对于限制 CORS 请求的最佳控制:
[EnableCors(“MyPolicy”)]与命名策略一起使用。
不要定义默认策略。
请勿使用终结点路由。
下一节中的代码满足前面的列表。
有关测试代码的说明,请参阅测试 CORS ,如以上代码所示。

禁用 CORS
[DisableCors] 特性不会禁用已通过终结点路由启用的 CORS。
以下代码定义 CORS 策略 “MyPolicy” :
C#

复制
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: “MyPolicy”,
builder =>
{
builder.WithOrigins(“http://example.com”,
“http://www.contoso.com”)
.WithMethods(“PUT”, “DELETE”, “GET”);
});
});

    services.AddControllers();
    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapRazorPages();
    });
}

}
以下代码禁用操作的 CORS GetValues2 :
C#

复制
[EnableCors(“MyPolicy”)]
[Route(“api/[controller]”)]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();

// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);

// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);


// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
    ControllerContext.MyDisplayRouteInfo();

}
前面的代码:
不会使用终结点路由启用 CORS。
不定义默认的 CORS 策略。
使用[EnableCors (“MyPolicy”)]启用控制器的 “MyPolicy” CORS 策略。
为方法禁用 CORS GetValues2 。
有关测试上述代码的说明,请参阅测试 CORS 。

CORS 策略选项
本部分介绍可在 CORS 策略中设置的各种选项:
设置允许的来源
设置允许的 HTTP 方法
设置允许的请求标头
设置公开的响应标头
跨域请求中的凭据
设置预检过期时间
AddPolicy在中调用 Startup.ConfigureServices 。 对于某些选项,最好先阅读CORS 如何工作部分。
设置允许的来源
AllowAnyOrigin:允许所有来源的 CORS 请求和任何方案( http 或 https )。 AllowAnyOrigin是不安全的,因为任何网站都可以向应用程序发出跨域请求。
备注

指定 AllowAnyOrigin 和 AllowCredentials 是不安全的配置,并可能导致跨站点请求伪造。 使用这两种方法配置应用时,CORS 服务将返回无效的 CORS 响应。
AllowAnyOrigin影响预检请求和 Access-Control-Allow-Origin 标头。 有关详细信息,请参阅预检请求部分。
SetIsOriginAllowedToAllowWildcardSubdomains:将 IsOriginAllowed 策略的属性设置为一个函数,当计算是否允许源时,此函数允许源匹配已配置的通配符域。
C#

复制
options.AddPolicy(“MyAllowSubdomainPolicy”,
builder =>
{
builder.WithOrigins(“https://*.example.com”)
.SetIsOriginAllowedToAllowWildcardSubdomains();
});
设置允许的 HTTP 方法
AllowAnyMethod:
允许任何 HTTP 方法:
影响预检请求和 Access-Control-Allow-Methods 标头。 有关详细信息,请参阅预检请求部分。
设置允许的请求标头
若要允许在 CORS 请求中发送特定标头(称为作者请求标头),请调用 WithHeaders 并指定允许的标头:
C#

复制
options.AddPolicy(“MyAllowHeadersPolicy”,
builder =>
{
// requires using Microsoft.Net.Http.Headers;
builder.WithOrigins(“http://example.com”)
.WithHeaders(HeaderNames.ContentType, “x-custom-header”);
});
若要允许所有作者请求标头,请调用 AllowAnyHeader :
C#

复制
options.AddPolicy(“MyAllowAllHeadersPolicy”,
builder =>
{
builder.WithOrigins(“https://*.example.com”)
.AllowAnyHeader();
});
AllowAnyHeader影响预检请求和访问控制请求标头。 有关详细信息,请参阅预检请求部分。
WithHeaders仅当发送的标头 Access-Control-Request-Headers 与中所述的标头完全匹配时,才可以使用 CORS 中间件策略匹配指定的特定标头 WithHeaders 。
例如,考虑按如下方式配置的应用:
C#

复制
app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));
CORS 中间件使用以下请求标头拒绝预检请求,因为 Content-Language (HeaderNames. ContentLanguage)未在中列出 WithHeaders :

复制
Access-Control-Request-Headers: Cache-Control, Content-Language
应用返回200 OK响应,但不会向后发送 CORS 标头。 因此,浏览器不会尝试跨域请求。
设置公开的响应标头
默认情况下,浏览器不会向应用程序公开所有的响应标头。 有关详细信息,请参阅W3C 跨域资源共享(术语):简单的响应标头。
默认情况下可用的响应标头包括:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
CORS 规范将这些标头称为简单的响应标头。 若要使其他标头可用于应用程序,请调用 WithExposedHeaders :
C#

复制
options.AddPolicy(“MyExposeResponseHeadersPolicy”,
builder =>
{
builder.WithOrigins(“https://*.example.com”)
.WithExposedHeaders(“x-custom-header”);
});
跨域请求中的凭据
凭据需要在 CORS 请求中进行特殊处理。 默认情况下,浏览器不会使用跨域请求发送凭据。 凭据包括 cookie 和 HTTP 身份验证方案。 若要使用跨域请求发送凭据,客户端必须设置 XMLHttpRequest.withCredentials 为 true 。
XMLHttpRequest直接使用:
JavaScript

复制
var xhr = new XMLHttpRequest();
xhr.open(‘get’, ‘https://www.example.com/api/test’);
xhr.withCredentials = true;
使用 jQuery:
JavaScript

复制
$.ajax({
type: ‘get’,
url: ‘https://www.example.com/api/test’,
xhrFields: {
withCredentials: true
}
});
使用提取 API:
JavaScript

复制
fetch(‘https://www.example.com/api/test’, {
credentials: ‘include’
});
服务器必须允许凭据。 若要允许跨域凭据,请调用 AllowCredentials :
C#

复制
options.AddPolicy(“MyMyAllowCredentialsPolicy”,
builder =>
{
builder.WithOrigins(“http://example.com”)
.AllowCredentials();
});
HTTP 响应包含一个 Access-Control-Allow-Credentials 标头,通知浏览器服务器允许跨源请求的凭据。
如果浏览器发送凭据,但响应不包含有效的 Access-Control-Allow-Credentials 标头,则浏览器不会向应用程序公开响应,而且跨源请求会失败。
允许跨域凭据会带来安全风险。 另一个域中的网站可以代表用户将登录用户的凭据发送给该应用程序,而无需用户的知识。
CORS 规范还指出, “*” 如果标头存在,则将 “源” 设置为 “(所有源)” 无效 Access-Control-Allow-Credentials 。

预检请求
对于某些 CORS 请求,浏览器会在发出实际请求之前发送额外的OPTIONS请求。 此请求称为预检请求。 如果满足以下所有条件,浏览器可以跳过预检请求:
请求方法为 GET、HEAD 或 POST。
应用不会设置、、、或以外的请求标头 Accept Accept-Language Content-Language Content-Type Last-Event-ID 。
Content-Type标头(如果已设置)具有以下值之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
为客户端请求设置的请求标头上的规则适用于应用通过在对象上调用来设置的标头 setRequestHeader XMLHttpRequest 。 CORS 规范调用这些标头作者请求标头。 此规则不适用于浏览器可以设置的标头,如 User-Agent 、 Host 或 Content-Length 。
下面是一个示例响应,它类似于在本文档的 "测试 CORS " 部分中通过 " Put test " 按钮发出的预检请求。

复制
General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8…8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
预检请求使用HTTP OPTIONS方法。 它可能包含以下标头:
访问控制-请求-方法:将用于实际请求的 HTTP 方法。
访问控制-请求标头:应用在实际请求上设置的请求标头的列表。 如前文所述,这不包含浏览器设置的标头,如 User-Agent 。
访问控制-允许-方法
如果预检请求被拒绝,应用将返回响应, 200 OK 但不会设置 CORS 标头。 因此,浏览器不会尝试跨域请求。 有关拒绝的预检请求的示例,请参阅本文档的测试 CORS部分。
使用 F12 工具时,控制台应用会显示类似于以下内容之一的错误,具体取决于浏览器:
Firefox:跨源请求被阻止:相同的源策略不允许读取上的远程资源 https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 。 (原因: CORS 请求未成功)。 了解详细信息
基于 Chromium:派生自源 “” 的 “” 访问权限已被 https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net CORS 策略阻止:响应预检请求未通过访问控制检查:请求的资源上没有 “访问控制-允许-源” 标头。 如果非跳转响应可满足需求,请将请求的模式设置为“no-cors”,以便在禁用 CORS 的情况下提取资源。
若要允许特定标头,请调用 WithHeaders :
C#

复制
options.AddPolicy(“MyAllowHeadersPolicy”,
builder =>
{
// requires using Microsoft.Net.Http.Headers;
builder.WithOrigins(“http://example.com”)
.WithHeaders(HeaderNames.ContentType, “x-custom-header”);
});
若要允许所有作者请求标头,请调用 AllowAnyHeader :
C#

复制
options.AddPolicy(“MyAllowAllHeadersPolicy”,
builder =>
{
builder.WithOrigins(“https://.example.com")
.AllowAnyHeader();
});
浏览器的设置方式并不一致 Access-Control-Request-Headers 。 如果:
标头设置为以外的任何内容"

AllowAnyHeader调用:至少包含 Accept 、 Content-Type 和 Origin ,以及要支持的任何自定义标头。

自动预检请求代码
应用 CORS 策略的时间:
通过 app.UseCors 在中调用 Startup.Configure 。
使用 [EnableCors] 特性。
ASP.NET Core 对 “预检选项” 请求做出响应。
目前使用每个终结点启用 CORS 不 RequireCors 支持自动not预检请求。
本文档的 "测试 CORS " 部分说明了此行为。

用于预检请求的 [HttpOptions] 属性
如果为 CORS 启用了适当的策略,ASP.NET Core 通常会自动响应 CORS 预检请求。 在某些情况下,可能不会出现这种情况。 例如,将CORS 用于终结点路由。
下面的代码使用[HttpOptions]特性为 OPTIONS 请求创建终结点:
C#

复制
[Route(“api/[controller]”)]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}

// OPTIONS: api/TodoItems2 
[HttpOptions]
public IActionResult PreflightRoute()
{
    return NoContent();
}

[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
    if (id < 1)
    {
        return BadRequest();
    }

    return ControllerContext.MyDisplayRouteInfo(id);
}

有关测试上述代码的说明,请参阅通过终结点路由测试 CORS 和 [HttpOptions] 。
设置预检过期时间
Access-Control-Max-Age标头指定可缓存对预检请求的响应的时间长度。 若要设置此标头,请调用 SetPreflightMaxAge :
C#

复制
options.AddPolicy(“MySetPreflightExpirationPolicy”,
builder =>
{
builder.WithOrigins(“http://example.com”)
.SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
});

CORS 如何工作
本部分介绍 HTTP 消息级别的CORS请求中发生的情况。
CORS不是一种安全功能。 CORS 是一种 W3C 标准,可让服务器放宽相同的源策略。
例如,恶意执行组件可能对站点使用跨站点脚本(XSS) ,并对启用了 CORS 的站点执行跨站点请求来窃取信息。
API 不能通过允许 CORS 来更安全。
它由客户端(浏览器)来强制执行 CORS。 服务器执行请求并返回响应,这是返回错误并阻止响应的客户端。 例如,以下任何工具都将显示服务器响应:
Fiddler
Postman
.NET HttpClient
Web 浏览器,方法是在地址栏中输入 URL。
这是一种方法,使服务器能够允许浏览器执行跨源XHR或获取 API请求,否则将被禁止。
没有 CORS 的浏览器不能执行跨域请求。 在 CORS 之前,使用JSONP来绕过此限制。 JSONP 不使用 XHR,而是使用

复制
Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK
响应标头

复制
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f…;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET
请求标头

复制
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 …
在 OPTIONS 请求中,服务器设置响应中的响应标头 Access-Control-Allow-Origin: {allowed origin} 标头。 例如,已部署的示例 Delete [EnableCors] button OPTIONS 请求包含以下标头:
常规标头

复制
Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content
响应标头

复制
Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f…;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET
请求标头

复制
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0
在前面的响应标头中,服务器设置响应中的访问控制允许源标头。 https://cors1.azurewebsites.net此标头的值与 Origin 请求中的标头相匹配。
如果 AllowAnyOrigin 调用了,则将 Access-Control-Allow-Origin: * 返回通配符值。 AllowAnyOrigin允许任何源。
如果响应不包含 Access-Control-Allow-Origin 标头,则跨域请求会失败。 具体而言,浏览器不允许该请求。 即使服务器返回成功的响应,浏览器也不会将响应提供给客户端应用程序。

显示选项请求
默认情况下,Chrome 和 Edge 浏览器不会在 F12 工具的 “网络” 选项卡上显示 “请求” 选项。 若要在这些浏览器中显示选项请求:
chrome://flags/#out-of-blink-cors 或 edge://flags/#out-of-blink-cors
禁用标志。
重新启动.
默认情况下,Firefox 显示 “选项请求”。
IIS 中的 CORS
部署到 IIS 时,如果未将服务器配置为允许匿名访问,则必须在 Windows 身份验证之前运行 CORS。 若要支持此方案,需要为应用安装和配置IIS CORS 模块。

测试 CORS
示例下载包含测试 CORS 的代码。 请参阅如何下载。 该示例是一个 API 项目,其中 Razor 添加了页面:
C#

复制
public class StartupTest2
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: “MyPolicy”,
builder =>
{
builder.WithOrigins(“http://example.com”,
“http://www.contoso.com”,
“https://cors1.azurewebsites.net”,
“https://cors3.azurewebsites.net”,
“https://localhost:44398”,
“https://localhost:5001”)
.WithMethods(“PUT”, “DELETE”, “GET”);
});
});

    services.AddControllers();
    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app)
{
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapRazorPages();
    });
}

}
警告

WithOrigins(“https://localhost:”);应仅用于测试示例应用程序,类似于下载示例代码。
下面 ValuesController 提供用于测试的终结点:
C#

复制
[EnableCors(“MyPolicy”)]
[Route(“api/[controller]”)]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();

// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);

// PUT api/values/5
[HttpPut("{id}")]
public IActionResult Put(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);


// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
    ControllerContext.MyDisplayRouteInfo();

}
MyDisplayRouteInfo由RouteInfo NuGet 包提供,并显示路由信息。
使用以下方法之一测试前面的示例代码:
使用部署的示例应用 https://cors3.azurewebsites.net/ 。 无需下载示例。
dotnet run使用的默认 URL 运行示例 https://localhost:5001 。
从 Visual Studio 运行示例,并将端口设置为44398,查找的 URL https://localhost:44398 。
使用带有 F12 工具的浏览器:
选择 “值” 按钮,然后查看 “网络” 选项卡中的标头。
选择 “放置测试” 按钮。 请参阅显示选项请求,以获取有关显示选项请求的说明。 PUT 测试创建两个请求:一个选项预检请求和 PUT 请求。
选择此 GetValues2 [DisableCors] 按钮可触发失败的 CORS 请求。 如文档中所述,响应返回200成功,但不进行 CORS 请求。 选择 “控制台” 选项卡以查看 CORS 错误。 根据浏览器,将显示类似于以下内容的错误:
'https://cors1.azurewebsites.net/api/values/GetValues2’CORS 策略已阻止从原始位置获取的访问权限 ‘https://cors3.azurewebsites.net’ :请求的资源上没有 “访问控制-允许” 标头。 如果非跳转响应可满足需求,请将请求的模式设置为“no-cors”,以便在禁用 CORS 的情况下提取资源。
可以使用Fiddler或Postman等工具来测试启用了CORS 的终结点。 使用工具时,标头指定的请求源 Origin 必须与接收请求的主机不同。 如果请求不是基于标头值跨域的,则 Origin :
不需要 CORS 中间件来处理请求。
不会在响应中返回 CORS 标头。
以下命令使用 curl 发出带有以下信息的选项请求:
Bash

复制
curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

通过终结点路由和 [HttpOptions] 测试 CORS
目前使用每个终结点启用 CORS 不 RequireCors 支持自动预检请求。 not 请考虑以下代码,它使用终结点路由启用 CORS:
C#

复制
public class StartupEndPointBugTest
{
readonly string MyPolicy = “_myPolicy”;

// .WithHeaders(HeaderNames.ContentType, "x-custom-header")
// forces browsers to require a preflight request with GET

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(name: MyPolicy,
            builder =>
            {
                builder.WithOrigins("http://example.com",
                                    "http://www.contoso.com",
                                    "https://cors1.azurewebsites.net",
                                    "https://cors3.azurewebsites.net",
                                    "https://localhost:44398",
                                    "https://localhost:5001")
                       .WithHeaders(HeaderNames.ContentType, "x-custom-header")
                       .WithMethods("PUT", "DELETE", "GET", "OPTIONS");
            });
    });

    services.AddControllers();
    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();

    app.UseCors();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers().RequireCors(MyPolicy);
        endpoints.MapRazorPages();
    });
}

}
下面 TodoItems1Controller 提供用于测试的终结点:
C#

复制
[Route(“api/[controller]”)]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
// PUT: api/TodoItems1/5
[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
if (id < 1)
{
return Content($“ID = {id}”);
}

    return ControllerContext.MyDisplayRouteInfo(id);
}

// Delete: api/TodoItems1/5
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);

// GET: api/TodoItems1
[HttpGet]
public IActionResult GetTodoItems() =>
    ControllerContext.MyDisplayRouteInfo();

[EnableCors]
[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
    ControllerContext.MyDisplayRouteInfo();

// Delete: api/TodoItems1/MyDelete2/5
[EnableCors]
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);

}
从已部署示例的测试页测试前面的代码。
Delete [EnableCors] 和GET [EnableCors] 按钮成功,因为终结点具有 [EnableCors] 和响应预检请求。 其他终结点失败。 “获取” 按钮失败,因为JavaScript发送:
JavaScript

复制
headers: {
“Content-Type”: “x-custom-header”
},
下面 TodoItems2Controller 提供了类似的终结点,但包含响应选项请求的显式代码:
C#

复制
[Route(“api/[controller]”)]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
// OPTIONS: api/TodoItems2/5
[HttpOptions("{id}")]
public IActionResult PreflightRoute(int id)
{
return NoContent();
}

// OPTIONS: api/TodoItems2 
[HttpOptions]
public IActionResult PreflightRoute()
{
    return NoContent();
}

[HttpPut("{id}")]
public IActionResult PutTodoItem(int id)
{
    if (id < 1)
    {
        return BadRequest();
    }

    return ControllerContext.MyDisplayRouteInfo(id);
}

// [EnableCors] // Not needed as OPTIONS path provided
[HttpDelete("{id}")]
public IActionResult MyDelete(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);

[EnableCors]  // Rquired for this path
[HttpGet]
public IActionResult GetTodoItems() =>
    ControllerContext.MyDisplayRouteInfo();

[HttpGet("{action}")]
public IActionResult GetTodoItems2() =>
    ControllerContext.MyDisplayRouteInfo();

[EnableCors]  // Rquired for this path
[HttpDelete("{action}/{id}")]
public IActionResult MyDelete2(int id) =>
    ControllerContext.MyDisplayRouteInfo(id);

}
从已部署示例的测试页测试前面的代码。 在 “控制器” 下拉列表中,选择 “预检”,然后设置 “控制器”。 对终结点的所有 CORS 调用都将 TodoItems2Controller 成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值