在最近的项目中,后端使用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. B. Vova and Trophies 字符串预处理+思维+贪心

    题意:给出一个字符串 只有G和S  可以交换任意两个位置的字符一次 问 最长的G的长度是多少 思路:预处理字符串 把相同的G粘成一个G 记一下数量  字符串变为 GSSGSGGSGSSG 相邻有一个S ...

  2. oracle表的操作SQL语句

    这篇文章的内容包括:表的増删改查,字段的増删改查,主键.外键.唯一.非空.默认约束的増删改   查看自己用户的所有表: select * from user_tab_comments;    www. ...

  3. Disconf 分布式配置管理平台(安装配置)

    Disconf 分布式配置管理平台(安装配置) 依赖环境 Nginx:处理静态资源请求.动态请求转发到Tomcat Tomcat:处理Nginx的请求 Redis:用户session管理 MySQL: ...

  4. Hack The Box 获取邀请码

    TL DR; 使用curl请求下面的地址 curl -X POST https://www.hackthebox.eu/api/invite/generate {"success" ...

  5. Tomcat 日志文件分割

    新到公司, 拿到了前辈们留下的 程序 “病历书” , 上面记载了项目上的一些 经常会犯的毛病,  还有相应的解决方法. 其中有的是因为后台 代码逻辑上的一些原因 , N手代码通病了吧 (这个还是以后再 ...

  6. Mybatis-generator自动生成器

    目录 前言牢骚 Mybatis-generator是什么? Mybatis-generator怎么使用? 第一步,Maven文件引用 第二步,数据库建立 第三步,写mybatis-generator. ...

  7. Maven 学习总结 (一)

    一.何为Maven 1.Maven是优秀的构建工具 maven的用途之一是用于构建,他是一个强大的构建工具,能够帮助我们自动化构建过程,从清理.编译.测试到生成报告,再到打包和部署. 他抽象了一个完整 ...

  8. CSS Grid基于网格的二维布局系统(详细教程)

    .grid-wrap{ display: inline-flex; padding: 20px; background: #f4f4f4; word-break: initial; } .handle ...

  9. 【转载】c++类的实例化与拷贝

    https://www.cnblogs.com/chris-cp/p/3578976.html c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起: https://blog.csdn.net/qq_2 ...

  10. Java之final关键字详解

    1. 修饰类 当用final去修饰一个类的时候,表示这个类不能被继承. 注意: a. 被final修饰的类,final类中的成员变量可以根据自己的实际需要设计为fianl. b. final类中的成员 ...