在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 的网站项目来支撑.注意,这里是 ...
随机推荐
- [模板] BSGS/扩展BSGS
简介 前置知识: 快速幂&&O(1)快速乘 [模板] 数学基础:快速幂/乘/逆元/exGCD/(ex)CRT/(ex)Lucas定理
- jQuery之事件和批量操作、事件委托示例
一.常用事件 click(function(){...}) // 点击时触发 focus(function(){...}) // 获得焦点触发 blur(function(){...}) // 失去焦 ...
- error:Microsoft Visual C++ 14.0 is required.
1,下载14.0安装包,解压,再次执行pip install XX命令 2,安装包已经在下载文件中 3,最不行找这个网站 https://www.microsoft.com/zh-cn/downloa ...
- docker的安装,升级,与删除(最新版)
docker安装在ubuntu上 以前叫做 Docker engine安装现在叫做docker-ce的 第一种安装办法: root下执行,sudo su - root apt-get update - ...
- 洛谷P2634 聪明可可
还是点分治 树上问题真有趣ovo,这道题统计模3为0的距离,可以把重心的子树分开统计,也可以一次性统计,然后容斥原理减掉重复的.. 其他的过程就是点分治的板子啦. #include <bits/ ...
- Codeforces 629D Babaei and Birthday Cakes DP+线段树
题目:http://codeforces.com/contest/629/problem/D 题意:有n个蛋糕要叠起来,能叠起来的条件是蛋糕的下标比前面的大并且体积也比前面的大,问能叠成的最大体积 思 ...
- 【JVM】JVM内存结构 VS Java内存模型 VS Java对象模型
原文:JVM内存结构 VS Java内存模型 VS Java对象模型 Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清 ...
- javascript: Element.getBoundingClientRect() 获取元素在网页上的坐标位置
来自:https://blog.csdn.net/weixin_42895400/article/details/81811095?utm_source=blogxgwz1 Element.getBo ...
- echarts地图详解
$(function() { // 路径配置 require.config({ paths : { // echarts: 'http://echarts.baidu.com/build/dist' ...
- utf8的大小写敏感性测试及其修改方法
utf8的大小写敏感性测试及其修改方法 # 测试utf8的大小写敏感性及其修改方法 -- 以下是utf8不区分大小写 # 修改数据库: ALTER DATABASE database_name CHA ...