一劳永逸:域名支持通配符,ASP.NET Core中配置CORS
ASP.NET Core 內(nèi)置了對(duì) CORS 的支持,使用很簡(jiǎn)單,只需先在 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 上應(yīng)用這個(gè)策略:
[EnableCors("AllowSameDomain")] public IActionResult Markdown() {return View(); }但是,當(dāng)看到上面一堆網(wǎng)址時(shí),當(dāng)想到每增加一個(gè)二級(jí)域名都需要修改上面的代碼時(shí),一種不舒服的感覺(jué)油然而生,一種想偷懶的沖動(dòng)涌上心頭。
難道沒(méi)有一勞永逸的方法嗎?DNS解析中支持在域名中使用通配符(*.cnblogs.com),CA證書(shū)中也支持,如果這里的 CORS 策略也支持使用通配符,不就可以一勞永逸了嗎?配置?CORS 策略的代碼就可以簡(jiǎn)化為下面的樣子:
public void ConfigureServices(IServiceCollection services) {services.AddCors(options => options.AddPolicy("AllowSameDomain",builder => builder.WithOrigins("*.cnblogs.com"))); }不僅一勞永逸,而且代碼更加簡(jiǎn)潔漂亮。
可是負(fù)責(zé)?ASP.NET?CORS?的開(kāi)發(fā)者沒(méi)這么貼心,只能自己動(dòng)手了。
從 github 簽出 ASP.NET CORS 的源代碼,找到其中根據(jù)域名進(jìn)行判斷的實(shí)現(xiàn)代碼:
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;}//...} }(這里竟然有重復(fù)代碼,又增添了一份不舒服的感覺(jué),暫且不管)
原來(lái)是通過(guò)?!policy.Origins.Contains(origin)?判斷的,只要修改這個(gè)地方的判斷代碼,就能實(shí)現(xiàn)一勞永逸的偷懶目的。但是,這部分代碼不是隨意可以修改的,要走代碼貢獻(xiàn)流程,而且不一定被接受,目前還是先想辦法擴(kuò)展它吧。
英明的 ASP.NET CORS 開(kāi)發(fā)者早就考慮了這個(gè)地方的擴(kuò)展性,將?EvaluateRequest() 與?EvaluatePreflightRequest 定義為虛擬方法,我們只需定義一個(gè)子類(lèi)繼承自?CorsService ,覆蓋這兩個(gè)方法即可。
接下來(lái)就是解決如何覆蓋的問(wèn)題。把父類(lèi)中的實(shí)現(xiàn)代碼復(fù)制過(guò)來(lái)修改不可取,以后?ASP.NET CORS 升級(jí)了,這部分代碼改了,就會(huì)帶來(lái)問(wèn)題。我們需要想辦法在不改變現(xiàn)有處理邏輯的前提下,影響處理結(jié)果。
我們繼續(xù)看?!policy.Origins.Contains(origin)?,policy.Origins 的類(lèi)型是?IList<string> ,它存儲(chǔ)的就是我們?cè)诙x CORS 策略時(shí)添加的網(wǎng)址,所以,如果我們想要影響這里的判斷結(jié)果,唯有改變?policy.Origins 的值。
根據(jù)當(dāng)前的情況,我們可以把問(wèn)題簡(jiǎn)化為下面的代碼:
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)); }接下來(lái)只需做一件事——集中精力把上面域名出售平臺(tái)注釋變成代碼。
。。。
我們將注釋變成了下面的代碼:
private void EvaluateOriginForWildcard(IList<string> origins, string origin) {//只在沒(méi)有匹配的origin的情況下進(jìn)行操作if (!origins.Contains(origin)){//查詢(xún)所有以星號(hào)開(kāi)頭的originvar wildcardDomains = origins.Where(o => o.StartsWith("*"));if (wildcardDomains.Any()){//遍歷以星號(hào)開(kāi)頭的originforeach (var wildcardDomain in wildcardDomains){//如果以.cnblogs.com結(jié)尾if (origin.EndsWith(wildcardDomain.Substring(1))//或者以//cnblogs.com結(jié)尾,針對(duì)http://cnblogs.com|| origin.EndsWith("//" + wildcardDomain.Substring(2))){//將http://www.cnblogs.com添加至originsorigins.Add(origin);break;}}}} }然后基于上面的代碼實(shí)現(xiàn) CorsService 的子類(lèi)?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 ,因?yàn)樵?service.AddMvc 中已經(jīng)把 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"))); }一勞永逸的目標(biāo)就此達(dá)成,不舒服的感覺(jué)煙消云散。
總結(jié)
以上是生活随笔為你收集整理的一劳永逸:域名支持通配符,ASP.NET Core中配置CORS的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 用canvas整个打飞机游戏
- 下一篇: 回顾游戏中的设计模式:策略模式vs抽象工