在ABP的Web层中实现复杂请求跨域访问
在最近的项目中,后端使用ABP,前端采用React,前后端完全分离。其中大部分接口都通过WebApi层调用,项目中未使用Session。但最后在添加一个网站的验证码验证留言功能时,使用了Session验证的方式,所以将验证码请求与校验功能放在了Web层。由于测试阶段前后端不同域,涉及到跨域请求的问题。跨域问题可以通过代理等手段解决,但是也可以在后端做些简单的修改来进行实现。WebApi的跨域处理比较简单,有官方给出的解决方案
Microsoft.AspNet.WebApi.Cors。但是Web层一般不涉及跨域,所以自己进行了探索实现。
一、常见方案
- 在web.config中添加配置。
<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);
}
}
二、常见方案问题分析
- 上面两种常见方案,方案一堪称简单粗暴,方案二则有针对性的开放一些域来进行跨域访问,比如
localhost:3000。 - 上面两种方案,都存在一个致命的问题,仅对简单跨域请求有效,无法处理复杂的跨域请求。
- 那么何为复杂的跨域请求?可以参考阮一峰的科普http://www.ruanyifeng.com/blog/2016/04/cors.html。
比如我们常用的Post或Put请求,Content-Type字段的类型一般是application/json时,就是复杂请求。 - 复杂请求会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错,而这次preflight的Http方法就是Options。换句话说,如果你的xhr请求发出前,会先发出一个Options请求,就说明你要执行的请求是复杂请求。
- 对于复杂的跨域请求,如果连preflight都没有通过,何谈后续的跨域请求?!
- 那么何为复杂的跨域请求?可以参考阮一峰的科普http://www.ruanyifeng.com/blog/2016/04/cors.html。
三、增加对复杂请求的预检(Preflight,即Options请求)处理支持
asp.net的web层,Options请求是在哪里进行处理?到达控制器中的action时,已经是正式请求了,最终发现应该可以在Global.asax中,通过Application_BeginRequest方法进行处理。
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跨域携带的问题,我们来做进一步的改造。
四、携带Cookie跨域
- 修改Global.aspx中的
Application_BeginRequest方法,增加代码Response.Headers.Add("Access-Control-Allow-Credentials", "true");
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);
}
- 修改AllowCrossSiteJsonAttribute类的
OnActionExecuting方法,
增加代码filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Credentials", "true");,另外需要注意filterContext.RequestContext.HttpContext.Response.AppendHeader("Access-Control-Allow-Origin", host);,代码中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": "一二三四"}');
}
在ABP的Web层中实现复杂请求跨域访问的更多相关文章
- 在dotnet core web api中支持CORS(跨域访问)
最近在写的Office add-in开发系列中,其中有一个比较共性的问题就是在add-in的客户端脚本中访问远程服务时,要特别注意跨域访问的问题. 关于CORS的一些基本知识,请参考维基百科的说明:h ...
- Web API中使用CORS解决跨域
Web API中使用Cros解决跨域 如果两个页面的协议,端口和域名都相同,则两个页面具有相同的源,注:IE不考虑端口,同源策略不会阻止浏览器发送请求,但是它会阻止应用程序看到响应.如下图所示 COR ...
- Asp.Net Web Api 接口,拥抱支持跨域访问。
如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问. 由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题 ...
- 如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问。
由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题. 刚开始没做任何处理,用jsonp的方式调用 web api 接口, ...
- 在Vue中如何使用axios跨域访问数据
最近在项目中需要用到axios,所以就恶补一下这个axios到底是什么东东.越来它是vue-resource的替代品,官网也说了,以后都用axios, vue-resource不在维护.那么这个axi ...
- 在Vue中如何使用axios跨域访问数据(转)
最近在项目中需要用到axios,所以就恶补一下这个axios到底是什么东东.越来它是vue-resource的替代品,官网也说了,以后都用axios, vue-resource不在维护.那么这个axi ...
- 在docker容器中为elasticsearch配置跨域访问
一.在docker容器中进入elasticsearch对应的容器 docker exec -it [容器名] /bin/bash 二.安装vim编辑器 因为我们需要更改配置文件,安装过的朋友就不用安装 ...
- Angular2中对ASP.NET MVC跨域访问
应用场景 项目开发决定使用angular2进行前后端分离开发,由我负责后端服务的开发,起初选择的是web api进行开发.对跨域访问通过API中间件+过滤器对跨域访问进行支持.开发一段后,通知需要移植 ...
- 支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示
随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Angularjs的框架来做UI,而数据则由另一个Web Api 的网站项目来支撑.注意,这里是 ...
随机推荐
- [OI]Noip 2018总结(普及)
考砸了,还有原谅我代码十分有限的可读性. 一个人的真正伟大之处就在于他能够认识到自己的渺小.——保罗 从一年前初一九月到现在18年10月接触OI已经有一年了.几次模拟赛也自我感觉良好,都过了一等的线, ...
- #!/usr/bin/python3的作用 解决vscode ImportError: No module named xxxx
在 Python 脚本的第一行经常见到这样的注释: #!/usr/bin/env python3 或者 #!/usr/bin/python3 含义 在脚本中, 第一行以 #! 开头的代码, 在计算机行 ...
- luogu P2617 Dynamic Rankings && bzoj 1901 (带修改区间第k大)
链接:https://www.luogu.org/problemnew/show/P2617 思路: 如果直接在主席树上修改的话,每次修改都会对后面所有的树造成影响,一次修改的复杂度就会变成 : n* ...
- Django Cookie,Session
Cookie Cookie的由来 HTTP协议是无状态的,每次请求都是独立的,对服务器来说,每次的请求都是全新的,上一次的访问是数 据是无法保留到下一次的 某些场景需要状态数据或者中间数据等相关对下一 ...
- python 类的私有变量和私有方法
#!/usr/bin/env python # -*- coding:utf-8 -*- # @Time : 2017/11/08 8:46 # @Author : lijunjiang # @Fil ...
- web.xml:<url-pattern>
web.xml 中的 <url-pattern> 是 <servlet-mapping> 或 <filter-mapping> 下的子标签. url :http:/ ...
- <三>企业级开源仓库nexus3实战应用–使用nexus3配置maven私有仓库
一两个星期之前,你如果在我跟前说起私服的事情,我大概会绕着你走,因为我对这个东西真的一窍不通.事实上也正如此,开发同学曾不止一次的跟我说公司的私服版本太旧了,许多新的依赖编译之后不会从远程仓库自动缓存 ...
- 使用 https://git.io 缩短 a GitHub.com URL.
curl -i https://git.io -F 'url=https://develon2015.github.io' -F 'code=develon' 现在点击 http://git.io/d ...
- CSS三种样式
CSS 指层叠样式表 (Cascading Style Sheets): 1 内联样式:无法复用,在元素style内写 ,很少使用: 2 内部样式:在head元素内style属性内写,此样式可以被当前 ...
- Python列表中查找某个元素的索引(多个)
enumerate() 函数用于将一个可遍历的数据对象(如列表.元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中. 以下是 enumerate() 方法的语法: e ...