一. 整体说明

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. C# 【Http请求返回】性能优化500毫秒到 60 毫秒

    偶然发现 C# 的 HttpRequest 要比 Chrome 请求同一Url 慢好多.C# HttpRequest 要500毫秒 而Chrome 只需要 39ms. 作为有责任感的 码农.这个 必须 ...

  2. vue.js 打包时出现空白页和路径错误

    vue-cli输入命令:npm  run  build 即可打包vue.js的项目 打包出来后项目中就会多了一个文件夹dist,下图为我们打包过后的项目 我们直接运行打包后的文件夹中的index.ht ...

  3. vue 利用路由跨页传参

    第一页,点击进入第二页进行传值: <template> <div id="app"> <div><router-link to=" ...

  4. iOS 报错信息: dyld: Library not loaded: @rpath/XCTest.framework/XCTest Referenced from

    新建项目,引入framework,运行时出现警告:dyld: Library not loaded: @rpath/RLLibrary.framework/RLLibrary  Referenced ...

  5. loadrunner总结

    loadrunner总结 1.性能测试包含了哪些测试(至少举出3种) 负载测试,压力测试,疲劳强度测试,大数据量测试,并发测试. 2.负载测试和压力测试的区别 性能测试: 是通过自动化的测试工具模拟多 ...

  6. Shell 编程 函数

    本篇主要写一些shell脚本函数的使用. 函数调用 #!/bin/bash sum(){ s=`expr 2 + 3` echo $s } sum [root@localhost ~]# vim su ...

  7. 修改GIT已提交的用户名和邮箱

    修改GIT已提交的用户名和邮箱 原文:https://help.github.com/en/github/using-git/changing-author-info 说明 要更改在现有提交中记录的名 ...

  8. NOIP 2002 选数

    洛谷 P1036 选数 洛谷传送门 JDOJ 1297: [NOIP2002]选数 T2 JDOJ传送门 Description ​ 已知 n 个整数 x1,x2,-,xn,以及一个整数 k(k< ...

  9. 第二阶段冲刺(个人)——three

    今天的个人计划:选择功能界面的选择框排版设计.使得一些选择功能当点击鼠标事件后才会出现. 昨天做了什么?测试登录功能并优化. 遇到了什么困难?一些js函数的运用不熟悉,好多借助了百度.

  10. LeetCode 489. Robot Room Cleaner

    原题链接在这里:https://leetcode.com/problems/robot-room-cleaner/ 题目: Given a robot cleaner in a room modele ...