在最近的项目中,后端使用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": "一二三四"}'); }