一劳永逸:域名支持通配符,ASP.NET Core中配置CORS

ASP.NET Core 内置了对 CORS 的支持,使用很简单,只需先在 Startup 的 ConfigureServices() 中添加 CORS 策略:

复制代码

复制代码

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options => options.AddPolicy(
        "AllowSameDomain",
        builder => builder.WithOrigins(
            "http://www.cnblogs.com",
            "https://q.cnblogs.com",
            "https://zzk.cnblogs.com",
            "https://i.cnblogs.com",
            "https://news.cnblogs.com",
            "https://job.cnblogs.com")));
}

复制代码

复制代码

然后在想启用 CORS 的控制器 Action 上应用这个策略:

[EnableCors("AllowSameDomain")]
public IActionResult Markdown()
{
    return View();
}

但是,当看到上面一堆网址时,当想到每增加一个二级域名都需要修改上面的代码时,一种不舒服的感觉油然而生,一种想偷懒的冲动涌上心头。

难道没有一劳永逸的方法吗?DNS解析中支持在域名中使用通配符(*.cnblogs.com),CA证书中也支持,如果这里的 CORS 策略也支持使用通配符,不就可以一劳永逸了吗?配置 CORS 策略的代码就可以简化为下面的样子:

复制代码

复制代码

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options => options.AddPolicy(
        "AllowSameDomain",
        builder => builder.WithOrigins("*.cnblogs.com")));
}

复制代码

复制代码

不仅一劳永逸,而且代码更加简洁漂亮。

可是负责 ASP.NET CORS 的开发者没这么贴心,只能自己动手了。

从 github 签出 ASP.NET CORS 的源代码,找到其中根据域名进行判断的实现代码:

复制代码

复制代码

public class CorsService : ICorsService
{
    public virtual void EvaluateRequest(HttpContext context, CorsPolicy policy, CorsResult result)
    {
        var origin = context.Request.Headers[CorsConstants.Origin];
        if (StringValues.IsNullOrEmpty(origin) || !policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
        {
            return;
        }

        AddOriginToResult(origin, policy, result);
        result.SupportsCredentials = policy.SupportsCredentials;
        AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders);
    }

    public virtual void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result)
    {
        var origin = context.Request.Headers[CorsConstants.Origin];
        if (StringValues.IsNullOrEmpty(origin) || !policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
        {
            return;
        }

        //...
    }
}

复制代码

复制代码

(这里竟然有重复代码,又增添了一份不舒服的感觉,暂且不管)

原来是通过 !policy.Origins.Contains(origin) 判断的,只要修改这个地方的判断代码,就能实现一劳永逸的偷懒目的。但是,这部分代码不是随意可以修改的,要走代码贡献流程,而且不一定被接受,目前还是先想办法扩展它吧。

英明的 ASP.NET CORS 开发者早就考虑了这个地方的扩展性,将 EvaluateRequest() 与 EvaluatePreflightRequest 定义为虚拟方法,我们只需定义一个子类继承自 CorsService ,覆盖这两个方法即可。

接下来就是解决如何覆盖的问题。把父类中的实现代码复制过来修改不可取,以后 ASP.NET CORS 升级了,这部分代码改了,就会带来问题。我们需要想办法在不改变现有处理逻辑的前提下,影响处理结果。

我们继续看 !policy.Origins.Contains(origin) ,policy.Origins 的类型是 IList<string> ,它存储的就是我们在定义 CORS 策略时添加的网址,所以,如果我们想要影响这里的判断结果,唯有改变 policy.Origins 的值。

根据当前的情况,我们可以把问题简化为下面的代码:

复制代码

复制代码

public static void Main(string[] args)
{
    IList<string> origins = new List<string>() { "*.cnblogs.com" };            
    var origin = "http://www.cnblogs.com";
    //在origins中添加"http://www.cnblogs.com"
    Assert.True(origins.Contains(origin));
}

复制代码

复制代码

接下来只需做一件事——集中精力把上面域名出售平台注释变成代码。

。。。

我们将注释变成了下面的代码:

复制代码

复制代码

private void EvaluateOriginForWildcard(IList<string> origins, string origin)
{
    //只在没有匹配的origin的情况下进行操作
    if (!origins.Contains(origin))
    {
        //查询所有以星号开头的origin
        var wildcardDomains = origins.Where(o => o.StartsWith("*"));
        if (wildcardDomains.Any())
        {
            //遍历以星号开头的origin
            foreach (var wildcardDomain in wildcardDomains)
            {
                //如果以.cnblogs.com结尾
                if (origin.EndsWith(wildcardDomain.Substring(1))
                    //或者以//cnblogs.com结尾,针对http://cnblogs.com
                    || origin.EndsWith("//" + wildcardDomain.Substring(2)))
                {
                    //将http://www.cnblogs.com添加至origins
                    origins.Add(origin);
                    break;
                }
            }
        }
    }
}

复制代码

复制代码

然后基于上面的代码实现 CorsService 的子类 WildcardCorsService :

复制代码

复制代码

public class WildcardCorsService : CorsService
{
    public WildcardCorsService(IOptions<CorsOptions> options)
        : base(options)
    {
    }

    public override void EvaluateRequest(HttpContext context, CorsPolicy policy, CorsResult result)
    {
        var origin = context.Request.Headers[CorsConstants.Origin];
        EvaluateOriginForWildcard(policy.Origins, origin);
        base.EvaluateRequest(context, policy, result);
    }

    public override void EvaluatePreflightRequest(HttpContext context, CorsPolicy policy, CorsResult result)
    {
        var origin = context.Request.Headers[CorsConstants.Origin];
        EvaluateOriginForWildcard(policy.Origins, origin);
        base.EvaluatePreflightRequest(context, policy, result);
    }

    private void EvaluateOriginForWildcard(IList<string> origins, string origin)
    {
        //...
    }
}

复制代码

复制代码

然后将其注入:

services.Add(ServiceDescriptor.Transient<ICorsService, WildcardCorsService>());

(注:这里要用 Add ,不要用 TryAdd ,因为在 service.AddMvc 中已经把 CorsService 注入了,用 Add 才能覆盖 CorsService 的注入。)

最终 ConfigureServices() 中的代码变成了这样:

复制代码

复制代码

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Add(ServiceDescriptor.Transient<ICorsService, WildcardCorsService>());
    services.Configure<CorsOptions>(options => options.AddPolicy(
        "AllowSameDomain",
        builder => builder.WithOrigins("*.cnblogs.com")));
}

复制代码

复制代码

一劳永逸的目标就此达成,不舒服的感觉烟消云散。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在.NET Core 3.1,当你在前端发起一个跨域请求时,如果没有在后端接口的响应头设置'Access-Control-Allow-Origin',那么就会出现报错信息"has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource." 。 这是因为浏览器实施了同源策略,限制了跨域请求。跨域请求是指前端代码在一个域名下发起请求,但是请求的目标地址与当前域名不一致。而为了保护用户的安全,浏览器默认不允许这种跨域请求,除非在服务器端设置了相应的响应头,即'Access-Control-Allow-Origin'。这个响应头指定了允许跨域请求的源,可以是具体的域名或者通配符"*"表示允许所有源。 解决这个问题的方法有几种: 1. 在后端代码设置响应头,添加'Access-Control-Allow-Origin',并指定允许跨域请求的源。例如,在.NET Core可以通过使用CORS间件来实现这个设置。在Startup.cs文件的ConfigureServices方法,添加以下代码: services.AddCors(options => { options.AddPolicy("AllowAll", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); 然后在Configure方法,使用UseCors方法将该策略应用到所有请求上: app.UseCors("AllowAll"); 2. 如果只允许特定的域名发起跨域请求,可以在设置响应头时指定具体的域名,而不是使用通配符"*"。 3. 如果你使用的是第三方的库或框架,例如jQuery,在发起请求时,可以通过设置xhrFields属性来添加'Access-Control-Allow-Origin'的请求头,例如: $.ajax({ url: 'http://localhost:52156/api/Person/1', type: 'GET', crossDomain: true, xhrFields: { withCredentials: true }, success: function (data) { console.log(data); } }); 通过以上的方法,你可以解决.NET Core 3.1报错"has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."的问题。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [解决前后端跨域报错:has been blocked by CORS policy: No ‘Access-Control-Allow-Origin](https://blog.csdn.net/wsaicyj/article/details/127814374)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [asp.net core 3.1 解决跨域的问题](https://blog.csdn.net/qq_25086397/article/details/104412767)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值