一、博友@落幕残情童鞋说到了,Nginx反向代理实现跨域,由于我目前尚未使用到,给忽略了,此次记录下,为下次补充。此坑已填css
二、提示:跨域的姊妹篇——《三十三║ ⅖ 种方法实现完美跨域》html
今天忙着给小伙伴们提出的问题解答,时间上没把握好,都快下班了,赶忙发布:书说上文《从壹开始先后端分离【 .NET Core2.0 +Vue2.0 】框架之十一 || AOP自定义筛选,Redis入门 11.1》,昨天我们说到了分布式缓存键值数据库,主要讲解了如何安装,使用,最后遗留了一个问题,同步+Redis缓存仍是比较简单,如何使用异步泛型存取Redis,仍是一直个人心结,但愿你们有会的,能够不吝赐教,本系列教程已经基本到了尾声,今天就说两个小的知识点,既然本系列是讲解先后端分离的,那必定会遇到跨域的问题,没错,今天将说下跨域!而后顺便说一下DTOs(数据传输对象),这些东西你们都用过,好比,在MVC中定义一个ViewModel,是基于Model实体类的,而后作了相应的变化,以适应前端需求,没错,就是这个,若是大型的实体类,一个个复杂的话会稍显费力,今天就是用一个自动映射工具——AutoMapper。前端
跨域问题由来已久,主要是来源于浏览器的”同源策略”。
何为同源?只有当协议、端口、和域名都相同的页面,则两个页面具备相同的源。只要网站的 协议名protocol、 主机host、 端口号port 这三个中的任意一个不一样,网站间的数据请求与传输便构成了跨域调用,会受到同源策略的限制。 同源策略限制从一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不一样域的服务进行跨站调用(一般指使用XMLHttpRequest请求)。jquery
因此说咱们在web中,咱们没法去获取跨域的请求,常见的就是没法经过js获取接口(这里要说下个人之前使用的经验:在同源系统下,前端js去调用后端接口,而后后端C#去调取跨域接口,这是我之前采用的办法,可是先后端分离,这个办法确定就是不行了,由于那时候已经没有了先后端之分,是两个项目),因此咱们只要合理使用同源策略,就能够达到跨域访问的目的。nginx
我本身创建了一个静态页面,用来模拟前端访问,具体以下步骤:git
新建一个Html页面,使用Jquery来发送请求(文件在项目的WWW文件夹下,你们能够本身下载,或者Copy下边代码)。github
一共三种跨域方法:web
<html> <head> <meta charset="utf-8"> <title>Blog.Core</title> <script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script> <style> div { margin: 10px; word-wrap: break-word; } </style> <script> $(document).ready(function () { $("#jsonp").click(function () { $.getJSON("/api/Login/jsonp?callBack=?", function (data) { $("#data-jsonp").html("数据: " + data.value); }); }); $("#cors").click(function () { $.get("/api/Login/Token", function (data, status) { console.log(data); $("#status-cors").html("状态: " + status); $("#data-cors").html("数据: " + data? data.token:"失败"); }); }); $("#cors-post").click(function () { let postdata = { "bID": 10, "bsubmitter": "222", "btitle": "33333", "bcategory": "4444", "bcontent": "5555", "btraffic": 0, "bcommentNum": 0, "bUpdateTime": "2018-11-08T02:36:26.557Z", "bCreateTime": "2018-11-08T02:36:26.557Z", "bRemark": "string" }; $.ajax({ type: 'post', url: '/api/Values', contentType: 'application/json', data: JSON.stringify(postdata), success: function (data, status) { console.log(data); $("#status-cors-post").html("状态: " + status); $("#data-cors-post").html("数据: " + JSON.stringify(data)); } }); //$.ajax({ // type: "POST", // url: "/api/Values", // success: function (data, status) { // console.log(data); // $("#status-cors-post").html("状态: " + status); // $("#data-cors-post").html("数据: " + data); // } //}); }); }); </script> </head> <body> <h3>经过JsonP实现跨域请求</h3> <button id="jsonp">发送一个 GET </button> <div id="status-jsonp"></div> <div id="data-jsonp"></div> <hr /> <h3>添加请求头实现跨域</h3> 无 <hr /> <h3>经过CORS实现跨域请求,另须要在服务器端配置CORE</h3> <button id="cors">发送一个 GET </button> <div id="status-cors"></div> <div id="data-cors"></div> <hr /> <button id="cors-post">发送一个 POST </button> <div id="status-cors-post"></div> <div id="data-cors-post"></div> <hr /> </body> </html>
注意:这里必定要注意jsonp的前端页面请求写法,要求很严谨ajax
一、其实只须要当前Blog.Core 项目配置了静态文件中间件,直接访问就能够
数据库
好比个人在线地址:查看右侧公告栏
二、单独部署:将这个页面部署到本身的IIS中(拷贝到文件里,直接在iis添加该文件,访问刚刚的Html文件目录就行)
在咱们的项目 LoginController 中,设计Jsonp接口,Core调用的接口咱们已经有了,就是以前获取Token的接口GetJWTStr
[HttpGet] [Route("jsonp")] public void Getjsonp(string callBack, long id = 1, string sub = "Admin", int expiresSliding = 30, int expiresAbsoulute = 30) { TokenModel tokenModel = new TokenModel(); tokenModel.Uid = id; tokenModel.Sub = sub; DateTime d1 = DateTime.Now; DateTime d2 = d1.AddMinutes(expiresSliding); DateTime d3 = d1.AddDays(expiresAbsoulute); TimeSpan sliding = d2 - d1; TimeSpan absoulute = d3 - d1; string jwtStr = BlogCoreToken.IssueJWT(tokenModel, sliding, absoulute);
//重要,必定要这么写 string response = string.Format("\"value\":\"{0}\"", jwtStr); string call = callBack + "({"+response+"})"; Response.WriteAsync(call); }
注意:这里必定要注意jsonp的接口写法,要求很严谨
四、点击”经过JsonP实现跨域请求“按钮,发现已经有数据了,证实Jsonp跨域已经成功,你能够换成本身的域名试一试,可是Cors的还不行
这里我没有写到代码里,是在通常处理程序里以前用到的
public void ProcessRequest(HttpContext context) { //接收参数 string uName = context.Request["name"]; string data = "{\"name\":\"" + uName + "\",\"age\":\"18\"}"; //只需在服务端添加如下两句 context.Response.AddHeader("Access-Control-Allow-Origin", "*"); //跨域能够请求的方式 context.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET"); context.Response.Write(data); }
function ashxRequest() { $.post("http://localhost:5551/ashxRequest.ashx", { name: "halo" }, function (data) { for (var i in data) { alert(data[i]); } }, "json") }
你们感兴趣能够本身实验下。有问题请留言
前端的代码在jsonp的时候已经写好,请往上看第二节,后端接口也是Token接口
剩下的就是配置跨域了,很简单!
在ConfigureServices中添加
#region CORS //跨域第二种方法,声明策略,记得下边app中配置 services.AddCors(c => { //↓↓↓↓↓↓↓注意正式环境不要使用这种全开放的处理↓↓↓↓↓↓↓↓↓↓ c.AddPolicy("AllRequests", policy => { policy .AllowAnyOrigin()//容许任何源 .AllowAnyMethod()//容许任何方式 .AllowAnyHeader()//容许任何头 .AllowCredentials();//容许cookie }); //↑↑↑↑↑↑↑注意正式环境不要使用这种全开放的处理↑↑↑↑↑↑↑↑↑↑ //通常采用这种方法 c.AddPolicy("LimitRequests", policy => { policy .WithOrigins("http://127.0.0.1:1818", "http://localhost:8080", "http://localhost:8021", "http://localhost:8081", "http://localhost:1818")//支持多个域名端口,注意端口号后不要带/斜杆:好比localhost:8000/,是错的 .AllowAnyHeader()//Ensures that the policy allows any header. .AllowAnyMethod(); }); }); //注意还须要在下边 Configure方法 中进行中间件配置 这里有歧义,部分读者可能没看懂,请看下边解释 //services.AddCors(); #endregion
歧义解释:
可能有些读者会说,你这里写错了,应该是 app.UseCors() ,我确定是知道的,那为啥还要这么写呢,是由于这里我提供了两套 Cors 跨域写法:
一、配置在 configureServices 中,而后再在管道中开启中间件,就是上边的写法;
二、还有一个是,只在 configureServices 中,开启服务,而后在中间件中,具体的配置:
//跨域第一种版本,请要ConfigureService中配置服务 services.AddCors(); // app.UseCors(options => options.WithOrigins("http://localhost:8021").AllowAnyHeader() //.AllowAnyMethod());
基本注释都有,你们都能看的懂,就这么简单!
注意:在定义策略 LimitRequests 的时候,源域名应该是客户端请求的端口域名,不是当前API的域名端口。
感谢博友 @学弱 提醒:CORS的配置必定要放在AutoFac前面,不然builder.Populate(services);后,你再进行配置会没有效果。
在启动文件 的Configure 配置方法里,添加启用Cors中间件服务
感谢博友@kiritio_ooo的提醒,Git已更新
注意:若是你使用了 app.UserMvc() 或者 app.UseHttpsRedirection()这类的中间件,必定要把 app.UseCors() 写在它们的上边,先进行跨域,再进行 Http 请求,不然会提示跨域失败。
由于这两个都是涉及到 Http请求的,若是你不跨域就直接转发或者mvc,那确定报错。
至此,跨域的问题已经完成辣
重要:若是你想查看效果,个人最新的Github上代码已经给你们写好了,你们clone之后,只须要执行 http://localhost:8081/corspost.html ,就能看到各类效果了。固然若是懒得下载,能够看个人在线效果:查看右侧公告栏
注意:这里要说下,若是遇到了跨域失败的提示,好比这样:
这个并不必定是没有配置好致使的跨域失败,还有多是接口有错误,好比 500了,致使的接口异常,因此就提示访问有错误。
请参考个人文章:
nginx是一个高性能的web服务器,经常使用做反向代理服务器。nginx做为反向代理服务器,就是把http请求转发到另外一个或者一些服务器上。
经过把本地一个url前缀映射到要跨域访问的web服务器上,就能够实现跨域访问。
对于浏览器来讲,访问的就是同源服务器上的一个url。而nginx经过检测url前缀,把http请求转发到后面真实的物理服务器。并经过rewrite命令把前缀再去掉。这样真实的服务器就能够正确处理请求,而且并不知道这个请求是来自代理服务器的。
简单说,nginx服务器欺骗了浏览器,让它认为这是同源调用,从而解决了浏览器的跨域问题。又经过重写url,欺骗了真实的服务器,让它觉得这个http请求是直接来自与用户浏览器的。
这样,为了解决跨域问题,只须要动一下nginx配置文件便可。
三种办法其实都能达到目的,可是优缺点也很明显
一、手动建立JSONP跨域
优势:无浏览器要求,能够在任何浏览器中使用此方式
缺点:格式要求很严格,只支持get请求方式,请求的后端出错不会有提示,形成不能处理异常
二、添加请求头实现跨域
优势:支持任意请求方式,而且后端出错会像非跨域那样有报错,能够对异常进行处理
缺点:兼容性不是很好,IE的话 <IE10 都不支持此方式
虽然CORS的方法有点儿相似请求头,可是封装,兼容性,灵活性都要好的不少,强烈推荐。
请看如下实体类
//数据库实体类 public class Author { public string Name { get; set; } } public class Book { public string Title { get; set; } public Author Author { get; set; } } //页面实体类 public class BookViewModel { public string Title { get; set; } public string Author { get; set; } } //api调用 BookViewModel model = new BookViewModel { Title = book.Title, Author = book.Author.Name }
上面的例子至关的直观了,咱们平时也是这么用的基本,可是问题也随之而来了,咱们能够看到在上面的代码中,若是一旦在Book对象里添加了一个额外的字段,然后想在前台页面输出这个字段,那么就须要去在项目里找到每一处有这样BookViewModel转换字段的地方,这是很是繁琐的。另外,BookViewModel.Author是一个string类型的字段,可是Book.Author属性倒是Author对象类型的,咱们用的解决方法是经过Book.Auther对象来取得Author的Name属性值,而后再赋值给BookViewModel的Author属性,这样看起行的通,可是想想,若是打算在之后的开发中把Name拆分红两个-FisrtName和LastName,个人天呐!咱们得去把原来的ViewModel对象也拆分红对应的两个字段,而后在项目中找到全部的转换,而后替换。
那么有什么办法或者工具来帮助咱们可以避免这样的状况发生呢?AutoMapper正是符合要求的一款插件。只需一键操做,就能一劳永逸,解决全部问题,而后经过依赖注入,快速使用:
//AutoMapper自动映射 //Mapper.Initialize(cfg => cfg.CreateMap<BlogArticle, BlogViewModels>()); //BlogViewModels models = Mapper.Map<BlogArticle, BlogViewModels>(blogArticle); BlogViewModels models = IMapper.Map<BlogViewModels>(blogArticle);//就这一句话彻底搞定全部转换
今天由于时间的关系,没有说到Automapper,明天再见吧~