c# - 为什么跨域资源共享 (CORS) 不会在服务器端阻塞?
问题描述
我创建了 2 个项目:Client和Server。
客户端是一个 Razor Web 应用程序,在索引页面上包含用于调用 api 的 javascript。它托管在http://localhost:8000下。
索引.cshtml
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<div class="container">
<div class="row">
<div class="col-6">
<button id="sender-get">GET</button>
<div id="content-get"></div>
</div>
<div class="col-6">
<button id="sender-post">POST</button>
<div id="content-post"></div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.0/js/bootstrap.min.js" integrity="sha256-oKpAiD7qu3bXrWRVxnXLV1h7FlNV+p5YJBIr8LOCFYw=" crossorigin="anonymous"></script>
<script>
$(document).ready(() => {
$('#sender-get').click(() => {
$.get("http://localhost:9000/weatherforecast")
.done(() => {
$('#content-get').text('done');
})
.fail(() => {
$('#content-get').text('fail');
});
});
$('#sender-post').click(() => {
$.post("http://localhost:9000/weatherforecast")
.done(() => {
$('#content-post').text('done');
})
.fail(() => {
$('#content-post').text('fail');
});
});
});
</script>
服务器是一个带有天气预报模板的 ASPNET Core (3.1) Web Api。它托管在http://localhost:9000下。它使用 CORS 中间件并配置为接受来自http://localhost:5000的请求。
WeatherForecastController.cs
namespace Server.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
[HttpPost]
public IEnumerable<WeatherForecast> Post()
{
return null;
}
}
}
启动.cs
namespace Server
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("http://localhost:5000");
});
});
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
当我点击POST时,请求失败,这是一个正确的响应:
但是如果你在 api 控制器上设置一个断点,它仍然会命中 Post() 方法:
解决方案
被击中的控制器端点是预期的行为。
CORS 限制在浏览器端强制执行,而不是在服务器端强制执行。您提到的 ASP.NET Core CORS 中间件也是如此。如果可能,中间件的职责仅仅是指示浏览器首先不要发送不允许的请求。然而,一些“简单”的请求(比如一些非常简单的 GET 和 POST)总是会到达服务器——这就是你观察到的。
CORS 一开始可能听起来很棘手,但就像生活中的所有事情一样,一旦你看看幕后,这一切都是有道理的。
跨域资源共享 (CORS) 是一种机制,它使用额外的 HTTP 标头来告诉浏览器让在一个来源上运行的 Web 应用程序可以访问来自不同来源的选定资源。当 Web 应用程序请求的资源与其自己的来源(域、协议或端口)不同时,它会执行跨域 HTTP 请求。
对于简单的请求(像大多数HEAD/GET
但也有一些简单的请求POST
,请参阅https://developer.mozilla.org/docs/Web/HTTP/CORS#Simple_requests了解详细信息),浏览器只执行请求并检查响应中的 CORS 标头( like Access-Control-Allow-Origin
) 来确定是否允许请求或是否应丢弃结果。
对于预先发送的请求(例如PUT/PATCH/DELETE
但也GET/POST
带有非标准标头或内容类型,请参阅https://developer.mozilla.org/docs/Web/HTTP/CORS#Preflighted_requests了解详细信息),浏览器发出所谓的 pre-带有OPTION
http 动词的飞行请求,以确定端点是否允许跨源请求。
浏览器对可能完全由于您观察到的行为而改变服务器上的数据的任何请求执行预先飞行前请求。如果它不使用无害的OPTION
前期,服务器将在收到请求时删除相应的请求,DELETE
尽管该请求本应被服务器的 CORS 策略拒绝。
但是,浏览器不会像大多数那样对简单请求进行飞行前请求,GET
因为这些请求是无害的。这就是为什么您的断点仍然被命中但响应随后被浏览器丢弃的原因。这也是您永远不应该更改GET
请求中的数据而使用专用动词的原因之一PUT/PATCH/DELETE
:)
推荐阅读
- javascript - 在javascript中设置对象属性
- python - 有人可以帮助解释为什么不能在包装器中使用 klass 吗?关于 python __closure__
- java - HttpMessageNotReadableException:读取输入消息时出现 I/O 错误;嵌套异常是 java.net.SocketTimeoutException
- node.js - 在创建反应应用程序时,进程停止并显示 1 个漏洞(运行 npm 审计)
- ios - macCatalyst 上的通用链接不起作用
- javascript - 从元素列表中获取输入数据
- django - 如何在 django 过滤器中实现 2 个布尔字段条件 OR?
- django - 从 React 前端向 Django 发送 POST 时出错,错误状态 401
- ruby-on-rails - 上传到 ActionText 文本区域的图像未正确链接到 S3
- r - 如何更改 facet_nested 标签之一的位置?