一. 整体说明

1. 说在前面的话  

  早在前面的章节中,就详细介绍了.Net FrameWork版本下MVC和WebApi的跨域解决方案,详见:https://www.cnblogs.com/yaopengfei/p/10340434.html,由于在Core版本中,MVC和WebApi已经合并,所以在该章节中介绍Asp.Net Core中的跨域解决方案。

2. 背景

  浏览器出于安全性考虑,禁止在网页上发出请求到不同的域的web页面进行请求,此限制称为同域策略。 同域策略可阻止恶意站点读取另一个站点中的敏感数据, 但有时候,你可能想要允许其他站点对您的应用程序进行跨域请求,所以有两套解决方案,分别是:CORS(跨域资源共享) 、 jsonp。

PS:有时候为了方便测试,可以直接配置一下Chrome浏览器,使其支持跨域请求。

3. 跨域资源共享(CORS)

  它是一项 W3C 标准,主流浏览器都支持,原理是让服务器放宽同域策略,它很灵活,可以显式的控制允许哪些地址、哪种请求方式来进行跨域访问。

4. JSONP

  简单的来说,它就是在json数据外面包了一层,它有一个很大的局限性,就是仅支持Get请求,如下JSON和JSONP的区别:

(1) json格式:
   {
  "id":123,
  "name":"ypf"
   }
(2) jsonp格式:在json外面包了一层
callback({
  "id":123,
  "name":"ypf"
  })
  其中callback取决于url传到后台是什么,它就叫什么。

5. 浏览器配置

 有时候为了测试方便,我们可以直接配置一下Chrome浏览器,使其支持跨域。

二. 跨域资源共享

1. 说明

  使用的程序集是【Microsoft.AspNetCore.Cors】,在Core Mvc中,默认已经包含了,无须再引入;他有两种作用形式,可以在Configure管道中UseMvc前进行全局拦截,也可以在以特性的形式作用于控制器 或 Action。

2. Cors策略详解

 A. 设置允许的来源:WithOrigins 或 AllowAnyOrigin        如:WithOrigins("http://localhost:29492","http://localhost:5000")

 B. 设置允许的方法:WithMethods 或 AllowAnyMethod   如:WithMethods("GET","PUT")

 C. 设置允许的请求标头:WithHeaders 或 AllowAnyHeader

 D. 设置公开响应标头:WithExposedHeaders

 E. 允许请求中的凭据:AllowCredentials

PS:AddCors注册服务,AddDefaultPolicy注册默认策略、AddPolicy注册命名策略

3. 使用流程

 流程一: 在ConfigureService先注册策略(默认策略或命名策略),然后可以在Configure管道中进行全局拦截 或者 以特性的形式作用于Controller或action。

 流程二: 直接在Configure中配置相应的策略进行全局拦截,不需要在ConfigureService中注册任何代码。

4. 默认策略测试

(1). 前提

  在Configure中通过AddDefultPolicy注册默认策略,策略内容:允许所有来源、所有方法、所有请求标头、允许请求凭据。代码如下:

  public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
}); //注册跨域请求服务
services.AddCors(options =>
{
//注册默认策略
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

(2). 测试1-全局拦截

   在ConfigureService中(app.UseMvc前)通过app.UseCors();注册默认策略,然后在前端访问Test1方法,正常访问。

PS:Test1方法要注释掉cors特性后再测试。

      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseStaticFiles();
app.UseCookiePolicy(); //全局配置跨域(一定要配置在 app.UseMvc前)
//1. 默认策略
app.UseCors(); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
         /// <summary>
/// 默认策略
/// </summary>
/// <returns></returns>
[HttpPost]
public string Test1()
{
return "ypf001";
}
           //1. 测试cors默认策略
$('#j_btn1').click(function () {
$.post("http://localhost:29492/Home/Test1", {}, function (data) {
alert(data);
});
});

(3). 测试2-特性作用

  在Test1方法上增加特性[EnableCors],然后进行测试,正常访问。

PS:ConfigureService中全局拦截要注释掉再测试

         /// <summary>
/// 默认策略
/// </summary>
/// <returns></returns>
[EnableCors]
[HttpPost]
public string Test1()
{
return "ypf001";
}

5. 命名策略测试

(1). ConfigureService中策略的注册代码

       public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
}); //注册跨域请求服务
services.AddCors(options =>
{
//注册一个名字为“AnotherPolicy”新策略
options.AddPolicy("AnotherPolicy", builder =>
{
builder.WithOrigins("http://localhost:29553", "http://localhost:5001") //多个地址通过"逗号"隔开
.WithMethods("GET","PUT")
.WithHeaders(Microsoft.Net.Http.Headers.HeaderNames.ContentType, "Authorization")
.AllowCredentials(); }); });
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

(2). Configure全局拦截代码  或者  作用于Action的代码 (二者选其一即可)

全局拦截:

      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseCookiePolicy(); //全局配置跨域(一定要配置在 app.UseMvc前)
//2. 命名策略
app.UseCors("AnotherPolicy"); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}

作用于action

         /// <summary>
/// 命名策略
/// </summary>
/// <returns></returns>
[EnableCors("AnotherPolicy")]
[HttpPost]
public string Test2()
{
return "ypf002";
}

(3). 前端测试代码

           //2. 测试cors命名策略
$('#j_btn2').click(function () {
$.ajax({
url: 'http://localhost:29492/Home/Test2',
type: "Post",
xhrFields: {
//发送跨域请求凭据
withCredentials: true
},
cache: false,
data: {},
beforeSend: function (request) {
//在请求报文头中加入Authorization 目的是让请求为非简单请求
request.setRequestHeader("Authorization", "Bearer 071899A00D4D4C5B1C41A6B0211B9399");
},
success: function (data) {
alert(data);
}
});
});

6. 直接Configure全局配置测试

  直接在Configure管道中配置策略,默认允许所有,注释掉ConfigureService中的所有策略和Test1、Test2方法上的特性, 仍然可以正常访问,测试通过。

       public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
} app.UseStaticFiles();
app.UseCookiePolicy(); //全局配置跨域(一定要配置在 app.UseMvc前)
//3. 直接配置策略,不需在Configure中注册任何代码了
app.UseCors(options =>
{
options.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
}); app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}

特别注意: 

  在core 2.x版本,AllowAnyOrigin()和AllowCredentials()可以共存,在3.x版本,不能共存。

三. JSONP

1. 原始的jsonp模式

  在Asp.Net Core中支持,在.Net版本的webapi中是不支持,即在方法中声明一个接受参数与前端JSONP位置传递过来的进行对应,然后将数据进行包裹返回,$"{myCallBack}({xjs})"。以JQuery的Ajax请求为例,可以指定JSONP的名称,如下面代码指定为:myCallBack,如果省略,JSONP的名称则为:callback。

特别注意:凡是JSONP请求,GET请求地址的第一个参数则为JSONP位置配置的名称,如:http://localhost:29492/Home/GetInfor2?myCallBack=jQuery341001790888637311383_1564124488832&userName=ypf&_=1564124488833

服务器端代码:

        /// <summary>
/// 原始的JSONP模式,支持
/// </summary>
/// <param name="callBack"></param>
/// <param name="userName"></param>
/// <returns></returns>
[HttpGet] public dynamic GetInfor1(string myCallBack, string userName)
{
var data = new
{
id = userName + "",
userName = userName
};
string xjs = JsonConvert.SerializeObject(data);
return $"{myCallBack}({xjs})";
}

前端代码:

         $('#j_jsonp1').click(function () {
$.ajax({
url: 'http://localhost:29492/Home/GetInfor1',
type: "get",
dataType: "jsonp",
//需要和服务端回掉方法中的参数名相对应
//注释掉这句话默认传的名称叫callback
jsonp: "myCallBack",
cache: false,
data: { userName: "ypf" },
success: function (data) {
console.log(data);
alert(data.id + "," + data.userName);
}
});
});

2. 利用过滤器判断请求来决定是否跨域

(1) 背景

  想实现服务器端方法正常写,该返回什么就返什么,如果是JSONP请求,则返回JSONP格式,如果是普通请求,则返回不同格式。

(2) 原理

  如果是jsonp请求的,如同下面这个地址:http://localhost:29492/Home/GetInfor2?myCallBack=jQuery341001790888637311383_1564124488832&userName=ypf&_=1564124488833, 第一个参数为myCallBack,如果有这个参数则证明是jsonp请求,需要拿到它的值;反之如果没有这个参数,则是普通请求,需要配置跨域策略了,才能拿到值。

  在过滤器中判断,如果是jsonp请求,则将数据进行包裹进行返回,如果是普通请求,则直接返回,然后把过滤器以特性[IsJsonpCallback]形式作用在GetInfor2方法上。

过滤器代码:

  public class IsJsonpCallbackAttribute: ActionFilterAttribute
{
private const string CallbackQueryParameter = "myCallBack";
public override void OnActionExecuted(ActionExecutedContext context)
{ string text = context.HttpContext.Request.QueryString.Value;
string[] arrys = text.Split('&');
if (arrys[].Contains(CallbackQueryParameter))
{
var myCallBackValue = arrys[].Split('=')[];
var result = $"{myCallBackValue}({((ObjectResult)context.Result).Value})";
context.HttpContext.Response.WriteAsync(result);
} base.OnActionExecuted(context);
}
}

服务器端代码:

      /// <summary>
/// 改造后的Jsonp(请求是Jsonp格式,则返回jsonp格式,反之普通格式)
/// </summary>
/// <param name="callBack"></param>
/// <param name="userName"></param>
/// <returns></returns>
[HttpGet]
[IsJsonpCallback] public dynamic GetInfor2(string userName)
{
var data = new
{
id = userName + "",
userName = userName
};
string xjs = JsonConvert.SerializeObject(data);
return $"{xjs}";
}

前端代码:

   //2. 利用过滤器改造-jsonp请求
$('#j_jsonp2').click(function () {
$.ajax({
url: 'http://localhost:29492/Home/GetInfor2',
type: "get",
dataType: "jsonp",
//需要和服务端回掉方法中的参数名相对应
//注释掉这句话默认传的名称叫callback
jsonp: "myCallBack",
cache: false,
data: { userName: "ypf" },
success: function (data) {
console.log(data);
alert(data.id + "," + data.userName);
}
});
});
//3. 利用过滤器改造-普通请求
$('#j_jsonp21').click(function () {
$.ajax({
url: 'http://localhost:29492/Home/GetInfor2',
type: "get",
dataType: "json",
cache: false,
data: { userName: "ypf" },
success: function (data) {
console.log(data);
alert(data.id + "," + data.userName);
}
});
});

3. 利用程序集【WebApiContrib.Core.Formatter.Jsonp】改造

  GitHub地址:https://github.com/WebApiContrib/WebAPIContrib.Core/tree/master/src/WebApiContrib.Core.Formatter.Jsonp

  通过Nuget引入上面程序集,在ConfigureServices中的AddMvc中,书写代码 option => option.AddJsonpOutputFormatter(),即可实现JSONP请求返回JSONP格式,普通请求返回不同格式, 默认情况下回调参数为“callback”,也可以手动配置:option =>option.AddJsonpOutputFormatter(mvcOption.OutputFormatters.OfType<JsonOutputFormatter>().FirstOrDefault(), "myCallBack")

默认回调参数配置的代码:

   services.AddMvc(
//不写参数的话,走的是默认回调参数 callback
option => option.AddJsonpOutputFormatter()
).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

命名回调参数配置的代码:

  MvcOptions mvcOption = new MvcOptions();
services.AddMvc(
//配置JSONP的方案二,回调参数配置为myCallBack
option =>option.AddJsonpOutputFormatter(mvcOption.OutputFormatters.OfType<JsonOutputFormatter>().FirstOrDefault(), "myCallBack")
).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

通过上面的代码配置,即可实现JSONP请求返回JSONP格式,普通请求返回不同格式。(原理去github上看代码)

四. Chrome浏览器配置

1. 前提

  注释掉所有策略、特性,通过下面的步骤设置Chrome浏览器的属性,使其支持跨域。

2. 配置步骤

 (1).在电脑上新建一个目录,例如:C:\MyChromeDevUserData

 (2).在Chrome根目录中找到exe程序,在其属性页面中的目标输入框里加上 --disable-web-security --user-data-dir=C:\MyChromeDevUserData,其中--user-data-dir的值就是刚才新建的目录。

如下图:

配置内容为:"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir=C:\MyChromeDevUserData,--user-data-dir

 (3).点击应用和确定后关闭属性页面,并打开chrome浏览器。再次打开chrome,发现有“--disable-web-security”相关的提示,如下图:,说明chrome可以正常跨域工作了。

3. 测试

(1). 服务器端方法 和 前端请求代码 如下:

         /// <summary>
/// 不进行任何配置,通过配置浏览器进行跨域
/// </summary>
/// <returns></returns>
public string TestNoConfig()
{
return "ypfTestNoConfig";
}
            //1. 配置浏览器进行跨域
$('#j_brower').click(function () {
$.post("http://localhost:29492/Home/TestNoConfig", {}, function (data) {
alert(data);
});
});

(2). 先用普通浏览器进行请求,请求不通,如下图:

(3). 再用配置好跨域的chrome进行请求,请求通了,如下图:

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

第十四节:Asp.Net Core 中的跨域解决方案(Cors、jsonp改造、chrome配置)的更多相关文章

  1. asp.net core webapi之跨域(Cors)访问

    这里说的跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被当作 ...

  2. ASP.NET Core-Docs:在 ASP.NET Core 中启用跨域请求(CORS)

    ylbtech-ASP.NET Core-Docs:在 ASP.NET Core 中启用跨域请求(CORS) 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 1. ...

  3. 在 ASP.NET Core 中启用跨域请求(CORS)

    本文介绍如何在 ASP.NET Core 的应用程序中启用 CORS. 浏览器安全可以防止网页向其他域发送请求,而不是为网页提供服务. 此限制称为相同源策略. 同一源策略可防止恶意站点读取另一个站点中 ...

  4. ASP.NET Core Web API 跨域(CORS) Cookie问题

    身为一个Web API,处理来自跨域不同源的请求,是一件十分合理的事情. 先上已有的文章,快速复制粘贴,启用CORS: Microsoft:启用 ASP.NET Core 中的跨域请求 (CORS) ...

  5. js基础 js自执行函数、调用递归函数、圆括号运算符、函数声明的提升 js 布尔值 ASP.NET MVC中设置跨域

    js基础 目录 javascript基础 ESMAScript数据类型 DOM JS常用方法 回到顶部 javascript基础 常说的js包括三个部分:dom(文档document).bom(浏览器 ...

  6. ASP.NET MVC中设置跨域

    ASP.NET MVC中设置跨域 1.什么是跨域请求 js禁止向不是当前域名的网站发起一次ajax请求,即使成功respone了数据,但是你的js仍然会报错.这是JS的同源策略限制,JS控制的并不是我 ...

  7. ASP.NET Core中防跨站点请求伪造

    CSRF(Cross-site request forgery)利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的. 例子 在某个 ...

  8. 学习ASP.NET Core(11)-解决跨域问题与程序部署

    上一篇我们介绍了系统日志与测试相关的内容并添加了相关的功能:本章我们将介绍跨域与程序部署相关的内容 一.跨域 1.跨域的概念 1.什么是跨域? 一个请求的URL由协议,域名,端口号组成,以百度的htt ...

  9. Asp.Net MVC 中实现跨域访问

    在ASP.Net webapi中可以使用  Microsoft.AspNet.WebApi.Cors  来实现: public static class WebApiConfig { public s ...

随机推荐

  1. centos如何强行踢掉某登录用户

    linux是一个多用户操作系统,用户可以在不同的地方链接上LINUX服务器. 在系统中我们可以用w或者who来查看用户: [root@7273 ~]# who root pts/0 2019-04-1 ...

  2. MUI下拉菜单样式

    <div class="mui-input-row my_select"> <label style="width: 47px;padding-righ ...

  3. WorkFlow四:添加用户决策步骤

    沿用之前的例子,做个用户决策步骤. 1.事物代码SWDD: 进入抬头,点击类的绑定按钮. 2.选择类的绑定,点击继续. 这是类的绑定已经变色了.这时候点击保存,再点击返回到图片逻辑流界面. 3.在发送 ...

  4. springboot入门介绍

    1. SpringBoot学习之@SpringBootApplication注解 下面是我们经常见到SpringBoot启动类代码: @SpringBootApplicationpublic clas ...

  5. 集成学习-Majority Voting

    认识 集成学习(Ensemble Methods), 首先是一种思想, 而非某种模型, 是一种 "群体决策" 的思想, 即对某一特定问题, 用多个模型来进行训练. 像常见的单个模型 ...

  6. yum lockfile is held by another process

    使用yum安装软件报错 yum lockfile is held by another process 解决方法 rm -f /var/run/yum.pid

  7. 【若泽大数据】玩转大数据之Spark零基础到实战

    https://www.bilibili.com/video/av29407581?p=1 若泽大数据官网 http://www.ruozedata.com/ tidb 系列三:有了sparkjdbc ...

  8. 安装elasticsearch+kibana+searchguard

    ---------------------------------安装es的search-guard-------------------------------------------------- ...

  9. axios post方式请求x-ww格式的数据

    //使用axios时,要确定是json格式还是x-www格式的,axios默认是json格式的,如果是x-ww格式需要做如下配置: let url = "/hehe/site/getcomm ...

  10. 字符串的方法slice、substr、substring对比

    三个方法的参数1都代表子串开始位置,参数2在slice和substring中表示结束位置,而在substr中代表的则是子串长度: 对于负数态度,当出现在参数1的位置时,slice和substr从末尾开 ...