在最近的項目中,後端使用ABP,前端採用React,先後端徹底分離。其中大部分接口都經過WebApi層調用,項目中未使用Session。但最後在添加一個網站的驗證碼驗證留言功能時,使用了Session驗證的方式,因此將驗證碼請求與校驗功能放在了Web層。因爲測試階段先後端不一樣域,涉及到跨域請求的問題。跨域問題能夠經過代理等手段解決,可是也能夠在後端作些簡單的修改來進行實現。WebApi的跨域處理比較簡單,有官方給出的解決方案
Microsoft.AspNet.WebApi.Cors
。可是Web層通常不涉及跨域,因此本身進行了探索實現。 ##1、常見方案html
<system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Methods" value="OPTIONS,POST,GET"/> <add name="Access-Control-Allow-Headers" value="x-requested-with"/> <add name="Access-Control-Allow-Origin" value="*" /> </customHeaders> </httpProtocol> </system.webServer>
[AllowCrossSiteJson("localhost:3000")]
的Attribute。 AllowCrossSiteJsonAttribute
類代碼以下:public class AllowCrossSiteJsonAttribute : ActionFilterAttribute { private string[] _domains; public AllowCrossSiteJsonAttribute(string domain) { _domains = new string[] { domain }; } public AllowCrossSiteJsonAttribute(string[] domains) { _domains = domains; } public override void OnActionExecuting(ActionExecutingContext filterContext) { var context = filterContext.RequestContext.HttpContext; var host = context.Request.Headers.Get("Origin"); if (host != null&& _domains.Contains(host)) { //域 filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", host); //Http方法 filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Methods", "*"); } base.OnActionExecuting(filterContext); } }
##2、常見方案問題分析前端
localhost:3000
。Content-Type
字段的類型通常是application/json
時,就是複雜請求。##3、增長對複雜請求的預檢(Preflight,即Options請求)處理支持 asp.net的web層,Options請求是在哪裏進行處理?到達控制器中的action時,已是正式請求了,最終發現應該能夠在Global.asax中,經過Application_BeginRequest
方法進行處理。web
protected override void Application_BeginRequest(object sender, EventArgs e) { if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")//攔截處理Options請求 { string domain = Request.Headers.Get("Origin"); // //這裏能夠對domain進行校驗,即維護一個可跨域訪問的列表,進行比對,校驗經過後才執行下面的操做。本文中不作處理。 // Response.Headers.Add("Access-Control-Allow-Origin", domain); Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type,authorization");//authorization是我項目中要用到的,讀者能夠忽略 Response.Flush(); Response.End(); } base.Application_BeginRequest(sender, e); }
這樣,咱們對Options跨域請求進行了「可支持跨域」的應答。以後的正式請求到達控制器中的Action,又有相應的跨域訪問處理。那麼對於整個的複雜請求跨域就完成實現了。 可是,上文中咱們提到,要實現的是驗證碼Session驗證功能,那麼就還涉及到Cookie跨域攜帶的問題,咱們來作進一步的改造。ajax
##4、攜帶Cookie跨域json
Application_BeginRequest
方法,增長代碼<font style="color: #AD5D0F;">Response.Headers.Add("Access-Control-Allow-Credentials", "true");</font>protected override void Application_BeginRequest(object sender, EventArgs e) { if (Request.Headers.AllKeys.Contains("Origin") && Request.HttpMethod == "OPTIONS")//攔截處理Options請求 { string domain = Request.Headers.Get("Origin"); // //這裏能夠對domain進行校驗,即維護一個可跨域訪問的列表,進行比對,校驗經過後才執行下面的操做。本文中不作處理。 // Response.Headers.Add("Access-Control-Allow-Origin", domain); Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type,authorization");//authorization是我項目中要用到的,讀者能夠忽略 Response.Headers.Add("Access-Control-Allow-Credentials", "true");//可攜帶Cookie Response.Flush(); Response.End(); } base.Application_BeginRequest(sender, e); }
OnActionExecuting
方法, 增長代碼<font style="color: #AD5D0F;">filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Credentials", "true");</font>,另外須要注意<font style="color: #AD5D0F;">filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", host);</font>,代碼中host不能用*來代替,必須使用具體的host名稱,這是跨域攜帶cookie的要求。public override void OnActionExecuting(ActionExecutingContext filterContext) { var context = filterContext.RequestContext.HttpContext; var host = context.Request.Headers.Get("Origin"); if (host != null&& _domains.Contains(host)) { //域,帶cookie請求必須明確指定host,不能使用*代替 filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", host); //Http方法 filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Methods", "*"); //可攜帶cookie filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Credentials", "true"); } base.OnActionExecuting(filterContext); }
至此,咱們完成了在asp.net MVC中複雜請求攜帶cookie跨域的處理。下面給出前端的調用代碼以供參考。 Ajax版本後端
$.ajax({ url: 'http://192.168.100.66:3006/OnlineMessage', type: 'post', xhrFields: { withCredentials: true }, dataType: 'application/json; charset=utf-8', data: { "author": "1", "qq": "2", "phone": "3", "email": "4", "content": "留言", "checkCode": "一二三四" }, complete: function (data) { alert(JSON.stringify(data)); } });
Xhr版本跨域
function loadXMLDoc() { var xhr = new XMLHttpRequest(); xhr.open("POST", "http://192.168.100.66:3006/OnlineMessage"); xhr.setRequestHeader("Content-type", "application/json"); xhr.withCredentials = true; xhr.send('{"author": "1","qq": "2","phone": "3","email": "4","content": "留言","checkCode": "一二三四"}'); }