跨域支持
解决什么问题
前后端分离场景下,浏览器会执行同源策略限制跨域请求。Aegis.Core.Cors 负责在 Aegis 应用中自动注册 CORS 策略和中间件,让前端可以正常调用 API。
如何引入
NuGet 包:Aegis.Core.Cors
注册方式:通过 Component.deps.json 自动注册。这个组件既要注册服务,也要进入中间件链,两处都要配置:
{
"Components": {
"Services": [
"Cors"
],
"Middlewares": [
"Cors"
]
}
}
中间件顺序
Cors 必须放在 Authentication 之前。浏览器发送 OPTIONS 预检请求时不会携带鉴权头,如果认证中间件先执行,预检请求会被拦截,前端看到的是跨域错误而不是认证错误。
{
"Components": {
"Middlewares": [
"Swagger",
"Cors",
"UMeta.Logging",
"UMeta.Audit",
"Authentication",
"Authorization"
]
}
}
配置:在 appsettings.json 中通过 Aegis:Cors 节配置策略:
{
"Aegis": {
"Cors": {
"AllowedOrigins": [
"https://app.example.com",
"https://admin.example.com"
],
"AllowedMethods": ["GET", "POST", "PUT", "DELETE"],
"AllowedHeaders": ["Authorization", "Content-Type"],
"ExposedHeaders": ["X-Pagination"],
"AllowCredentials": true,
"PreflightMaxAgeSeconds": 600
}
}
}
默认行为
不配置 Aegis:Cors 节时,组件使用以下默认值,等同于全部放行:
| 配置项 | 默认值 | 效果 |
|---|---|---|
Enabled | true | CORS 中间件默认生效 |
PolicyName | AegisCorsPolicy | 策略名称,一般不需要改 |
AllowedOrigins | 空 | 允许所有源 |
AllowedMethods | 空 | 允许所有 HTTP 方法 |
AllowedHeaders | 空 | 允许所有请求头 |
ExposedHeaders | ["X-Pagination"] | 暴露分页响应头 |
AllowCredentials | true | 允许携带凭证 |
PreflightMaxAgeSeconds | 600 | 预检缓存 10 分钟 |
EnablePreflightCache | true | 预检缓存默认开启 |
此外 X-Request-Id 会被强制暴露,不需要手动配置。
这意味着只要在 Component.deps.json 中启用了 Cors,不做任何额外配置,前端就可以跨域访问 API。 这个默认适合本地开发和内部系统。对外暴露的系统建议明确写出允许源和允许头。
配置项说明
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Enabled | bool | true | 是否启用 CORS。设为 false 时不返回 CORS 策略,等同于关闭跨域支持 |
PolicyName | string | AegisCorsPolicy | CORS 策略名称,与 UseCors(name) 对应 |
AllowedOrigins | string[] | 空(允许所有) | 允许的源列表。为空或包含 * 时允许所有源 |
AllowedMethods | string[] | 空(允许所有) | 允许的 HTTP 方法。为空或包含 * 时允许所有方法 |
AllowedHeaders | string[] | 空(允许所有) | 允许的请求头。为空或包含 * 时允许所有头 |
ExposedHeaders | string[] | ["X-Pagination"] | 前端可以读取的响应头。X-Request-Id 会自动追加,不需要手动配置 |
AllowCredentials | bool | true | 是否允许携带 Cookie、Authorization 等凭证 |
PreflightMaxAgeSeconds | int | 600(10 分钟) | 浏览器缓存预检结果的时间(秒) |
EnablePreflightCache | bool | true | 是否启用预检缓存。设为 false 时每次跨域请求都会触发预检 |
前端依赖的响应头
框架中有两个响应头前端经常需要读取:
X-Request-Id:请求唯一标识,用于链路追踪和问题排查。组件会强制暴露,不需要手动配置。X-Pagination:分页信息(总条数、总页数等)。默认暴露,不需要手动配置。
如果还有其他自定义响应头需要前端读取,加到 ExposedHeaders 中:
{
"ExposedHeaders": ["X-Pagination", "X-Custom-Header"]
}
AllowCredentials 与通配符源的限制
当 AllowCredentials 为 true 时,AllowedOrigins 不能设为 *。这是 CORS 规范的硬限制——浏览器会拒绝带凭证的通配符源响应。
组件的处理方式是:当 AllowedOrigins 为空或包含 * 时,内部使用 SetIsOriginAllowed(_ => true) 代替 AllowAnyOrigin(),这样既允许所有源,又能携带凭证。
但如果要上生产,建议明确列出允许源:
{
"Aegis": {
"Cors": {
"AllowedOrigins": [
"https://app.example.com"
],
"AllowCredentials": true
}
}
}
边界与限制
- 组件只管 CORS 策略注册和中间件接入。如果上游有反向代理(如 Nginx、IIS)也配了 CORS 头,可能出现重复响应头,需要在一侧关闭。
- 策略缓存基于双重检查锁定,配置变更后会清缓存重建。高并发场景下不会有性能问题。
PolicyName与UseCors(name)必须一致。默认值AegisCorsPolicy由组件内部管理,一般不需要修改。
常见问题
前端报跨域,但后端看起来没问题
最常见原因是 Cors 没有进入 Middlewares,或者中间件顺序放错了。检查:
Services里是否包含CorsMiddlewares里是否包含CorsCors是否位于Authentication之前
前端拿不到 X-Request-Id
X-Request-Id 会被强制暴露。如果仍然拿不到,检查:
- 实际响应里有没有这个头(由
ActionContext中间件产生) - 请求是否真的经过了
Cors中间件 - 前端是否在跨域场景下读取该头(同源请求不需要 CORS 头也能读到)
改了配置没生效
确认 appsettings.json 文件已保存,且没有环境特定配置(如 appsettings.Production.json)覆盖了你的修改。组件使用 IOptionsMonitor 监听变更,正常情况下保存即生效。