在最近的项目中,后端使用ABP,前端采用React,前后端完全分离。其中大部分接口都通过WebApi层调用,项目中未使用Session。但最后在添加一个网站的验证码验证留言功能时,使用了Session验证的方式,所以将验证码请求与校验功能放在了Web层。由于测试阶段前后端不同域,涉及到跨域请求的问题。跨域问题可以通过代理等手段解决,但是也可以在后端做些简单的修改来进行实现。WebApi的跨域处理比较简单,有官方给出的解决方案Microsoft.AspNet.WebApi.Cors。但是Web层一般不涉及跨域,所以自己进行了探索实现。

一、常见方案

  1. 在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>
  1. 在被访问的控制器上加上[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);
}
}

二、常见方案问题分析

  1. 上面两种常见方案,方案一堪称简单粗暴,方案二则有针对性的开放一些域来进行跨域访问,比如localhost:3000
  2. 上面两种方案,都存在一个致命的问题,仅对简单跨域请求有效,无法处理复杂的跨域请求。
    • 那么何为复杂的跨域请求?可以参考阮一峰的科普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都没有通过,何谈后续的跨域请求?!

三、增加对复杂请求的预检(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跨域

  1. 修改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);
}
  1. 修改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层中实现复杂请求跨域访问的更多相关文章

  1. 在dotnet core web api中支持CORS(跨域访问)

    最近在写的Office add-in开发系列中,其中有一个比较共性的问题就是在add-in的客户端脚本中访问远程服务时,要特别注意跨域访问的问题. 关于CORS的一些基本知识,请参考维基百科的说明:h ...

  2. Web API中使用CORS解决跨域

    Web API中使用Cros解决跨域 如果两个页面的协议,端口和域名都相同,则两个页面具有相同的源,注:IE不考虑端口,同源策略不会阻止浏览器发送请求,但是它会阻止应用程序看到响应.如下图所示 COR ...

  3. Asp.Net Web Api 接口,拥抱支持跨域访问。

    如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问. 由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题 ...

  4. 如何让你的 Asp.Net Web Api 接口,拥抱支持跨域访问。

    由于 web api 项目通常是被做成了一个独立站点,来提供数据,在做web api 项目的时候,不免前端会遇到跨域访问接口的问题. 刚开始没做任何处理,用jsonp的方式调用 web api 接口, ...

  5. 在Vue中如何使用axios跨域访问数据

    最近在项目中需要用到axios,所以就恶补一下这个axios到底是什么东东.越来它是vue-resource的替代品,官网也说了,以后都用axios, vue-resource不在维护.那么这个axi ...

  6. 在Vue中如何使用axios跨域访问数据(转)

    最近在项目中需要用到axios,所以就恶补一下这个axios到底是什么东东.越来它是vue-resource的替代品,官网也说了,以后都用axios, vue-resource不在维护.那么这个axi ...

  7. 在docker容器中为elasticsearch配置跨域访问

    一.在docker容器中进入elasticsearch对应的容器 docker exec -it [容器名] /bin/bash 二.安装vim编辑器 因为我们需要更改配置文件,安装过的朋友就不用安装 ...

  8. Angular2中对ASP.NET MVC跨域访问

    应用场景 项目开发决定使用angular2进行前后端分离开发,由我负责后端服务的开发,起初选择的是web api进行开发.对跨域访问通过API中间件+过滤器对跨域访问进行支持.开发一段后,通知需要移植 ...

  9. 支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示

    随着深入使用ASP.NET Web Api,我们可能会在项目中考虑将前端的业务分得更细.比如前端项目使用Angularjs的框架来做UI,而数据则由另一个Web Api 的网站项目来支撑.注意,这里是 ...

随机推荐

  1. 【C/C++】任意进制转换

    进制转换:R进制->10进制:10进制->R进制. #include<bits/stdc++.h> using namespace std; /*函数:r进制转换成10进制*/ ...

  2. android O 打开设置->声音->“点按时震动问题”

    主要原因是和导航栏和屏幕最下方3个按键的属性配置有关,因为在PhoneWindowManager中调用方法performHapticFeedbackLw(null, HapticFeedbackCon ...

  3. UOJ#348 州区划分

    解:有一个很显然的状压...... 就设f[s]表示选的点集为s的时候所有方案的权值和. 于是有f[s] = f[s \ t] * (sum[t] / sum[s])P. 这枚举子集是3n的. 然后发 ...

  4. usb输入子系统键盘(四)

    目录 usb输入子系统键盘 设计思路 内核的上报代码 完整代码 title: usb输入子系统键盘 tags: linux date: 2018/12/20/ 17:05:08 toc: true - ...

  5. 第六节:WebApi的部署方式(自托管)

    一. 简单说明 开篇就介绍过WebApi和MVC相比,其中优势之一就是WebApi可以不依赖于IIS部署,可以自托管,当然这里指的是 .Net FrameWork 下的 WebApi 和 MVC 相比 ...

  6. 第十五节:HttpContext五大核心对象的使用(Request、Response、Application、Server、Session)

    一. 基本认识 1. 简介:HttpContext用于保持单个用户.单个请求的数据,并且数据只在该请求期间保持: 也可以用于保持需要在不同的HttpModules和HttpHandlers之间传递的值 ...

  7. 爬取qq音乐巅峰榜---内地音乐的榜单

    import requestsimport jsonimport sys for i in range(0,10): url = "https://szc.y.qq.com/v8/fcg-b ...

  8. 2.解决虚拟机中centos联网的问题

    首先:打开虚拟机的编辑菜单,选择“虚拟机网络编辑器” 虚拟机网络编辑器 在虚拟机网络编辑器中选择还原默认设置 接下来开启CentOS7虚拟机 在这里需要注意的是必需以管理员身份来进行设置,所以要用管理 ...

  9. ssh-copy-id 拷贝用户秘钥

    生成秘钥 ssh-keygen -t [rsa|dsa] 将会生成密钥文件和私钥文件 id_rsa,id_rsa.pub或id_dsa,id_dsa.pub 将 .pub 文件复制到B机器的 .ssh ...

  10. 微信小程序-制作简易豆瓣笔记

    demo截图: 图书:      电影:        共工欲善其事,必先利其器: 小程序编辑器下载地址 : https://mp.weixin.qq.com/debug/wxadoc/dev/dev ...