在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 的网站项目来支撑.注意,这里是 ...
随机推荐
- iOS 利用高德地图WMS服务
Demo: https://github.com/xushiyou23/AMapTesting 转: 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net ...
- python之序列化模块、双下方法(dict call new del len eq hash)和单例模式
摘要:__new__ __del__ __call__ __len__ __eq__ __hash__ import json 序列化模块 import pickle 序列化模块 补充: 现在我们都应 ...
- mpvue——引入echarts打包vendor过大
前言 有一个项目需要引入图表,当时有两种选择一种是mpvue-echarts,一种是F2,而我经过踩坑之后依然决然的选择了mpvue-echarts,简单快捷容易上手,主要之前用过比较熟悉. 问题 | ...
- 【XSY2843】「地底蔷薇」 NTT什么的 扩展拉格朗日反演
题目大意 给定集合\(S\),请你求出\(n\)个点的"所有极大点双连通分量的大小都在\(S\)内"的不同简单无向连通图的个数对\(998244353\)取模的结果. \(n\le ...
- mongoDB 数据库操作
mongoDB 数据库操作 数据库命名规则 . 使用 utf8 字符,默认所有字符为 utf8 . 不能含有空格 . / \ "\0" 字符 (c++ 中会将 "\0&q ...
- H5下拉刷新和上拉加载实现原理浅析
前言 在移动端H5网页中,下拉刷新和上拉加载更多数据的交互方式出现频率很高,开源社区也有很多类似的解决方案,如iscroll,pulltorefresh.js库等.下面是对这两种常见交互基本实现原理的 ...
- 题解 UVA1567 【A simple stone game】
题目大意 一堆石子有n个,首先第一个人开始可以去1~
- [SCOI2015]小凸想跑步
题目描述 小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏. 操场是个凸 n 边形, nn 个顶点按照逆时针从 0 ∼n−1 编号.现在小凸随机站在操场中的某个位置,标记为p点.将 p ...
- C# 数独算法——LINQ+委托
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Sing ...
- Hadoop记录-JMX参数
Yarn metrics参数说明 获取Yarn jmx信息:curl -i http://xxx:8088/jmx Hadoop:service=ResourceManager,name=FSOpDu ...