本文3.0版本文章

 
 

更新反馈

1、博友@落幕残情童鞋说到了,Nginx反向代理实现跨域,因为我目前还没有使用到,给忽略了,这次记录下,为下次补充。此坑已填

2、提示:跨域的姊妹篇——《三十三║ ⅖ 种方法实现完美跨域

代码已上传Github+Gitee,文末有地址

  今天忙着给小伙伴们提出的问题解答,时间上没把握好,都快下班了,赶紧发布:书说上文《从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十一 || AOP自定义筛选,Redis入门 11.1》,昨天咱们说到了分布式缓存键值数据库,主要讲解了如何安装,使用,最后遗留了一个问题,同步+Redis缓存还是比较简单,如何使用异步泛型存取Redis,还是一直我的心结,希望大家有会的,可以不吝赐教,本系列教程已经基本到了尾声,今天就说两个小的知识点,既然本系列是讲解前后端分离的,那一定会遇到跨域的问题,没错,今天将说下跨域!然后顺便说一下DTOs(数据传输对象),这些东西大家都用过,比如,在MVC中定义一个ViewModel,是基于Model实体类的,然后做了相应的变化,以适应前端需求,没错,就是这个,如果大型的实体类,一个个复杂的话会稍显费力,今天就是用一个自动映射工具——AutoMapper。

零、今天完成左下角的深紫色部分

一、为什么会出现跨域的问题

跨域问题由来已久,主要是来源于浏览器的”同源策略”。 
​ 何为同源?只有当协议、端口、和域名都相同的页面,则两个页面具有相同的源。只要网站的 协议名protocol、 主机host、 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用,会受到同源策略的限制。 ​ 同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用(通常指使用XMLHttpRequest请求)。

所以说我们在web中,我们无法去获取跨域的请求,常见的就是无法通过js获取接口(这里要说下我的以前使用的经验:在同源系统下,前端js去调用后端接口,然后后端C#去调取跨域接口,这是我以前采用的办法,但是前后端分离,这个办法肯定就是不行了,因为那时候已经没有了前后端之分,是两个项目),所以我们只要合理使用同源策略,就可以达到跨域访问的目的。

二、三种跨域方式 之JsonP

我自己建立了一个静态页面,用来模拟前端访问,具体如下步骤:

1、模拟前端访问页面

新建一个Html页面,使用Jquery来发送请求(文件在项目的WWW文件夹下,大家可以自己下载,或者Copy下边代码)。

一共三种跨域方法:

<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": ,
"bsubmitter": "",
"btitle": "",
"bcategory": "",
"bcontent": "",
"btraffic": ,
"bcommentNum": ,
"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的前端页面请求写法,要求很严谨

2、请求页面部署

1、其实只需要当前Blog.Core 项目配置了静态文件中间件,直接访问就可以

比如我的在线地址:查看右侧公告栏

2、单独部署:将这个页面部署到自己的IIS中(拷贝到文件里,直接在iis添加该文件,访问刚刚的Html文件目录就行)

3、设计后台接口

在我们的项目 LoginController 中,设计Jsonp接口,Core调用的接口我们已经有了,就是之前获取Token的接口GetJWTStr

      [HttpGet]
[Route("jsonp")]
public void Getjsonp(string callBack, long id = , string sub = "Admin", int expiresSliding = , int expiresAbsoulute = )
{
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的接口写法,要求很严谨

4、点击”通过JsonP实现跨域请求“按钮,发现已经有数据了,证明Jsonp跨域已经成功,你可以换成自己的域名试一试,但是Cors的还不行

三、三种跨域方式 之添加请求头实现跨域

这里我没有写到代码里,是在一般处理程序里之前用到的

1、后端

        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);
}

2、前端

       function ashxRequest() {
$.post("http://localhost:5551/ashxRequest.ashx", { name: "halo" }, function (data) {
for (var i in data) {
alert(data[i]);
}
}, "json")
}

大家感兴趣可以自己实验下。有问题请留言

四、三种跨域方式 之 高效CORS

1、前端ajax调用

前端的代码在jsonp的时候已经写好,请往上看第二节,后端接口也是Token接口

剩下的就是配置跨域了,很简单!

2、配置 CORS 跨域

在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();
});
}); // 这是第二种注入跨域服务的方法,这里有歧义,部分读者可能没看懂,请看下边解释
//services.AddCors();
#endregion

歧义解释:

可能有些读者会说,你这里写错了,应该是 app.UseCors() ,我肯定是知道的,那为啥还要这么写呢,是因为这里我提供了两套 Cors 跨域写法:

1、配置在 configureServices 中,然后再在管道中开启中间件,就是上边的写法;

2、还有一个是,只在 configureServices 中,开启服务,然后在中间件中,具体的配置:

 //跨域第一种版本,请要ConfigureService中配置服务 services.AddCors();
// app.UseCors(options => options.WithOrigins("http://localhost:8021").AllowAnyHeader()
//.AllowAnyMethod());

基本注释都有,大家都能看的懂,就这么简单!

注意:在定义策略 LimitRequests 的时候,源域名应该是客户端请求的端口域名,不是当前API的域名端口。

感谢博友 @学弱 提醒:CORS的配置一定要放在AutoFac前面,否则builder.Populate(services);后,你再进行配置会没有效果。

3、启动中间件

在启动文件 的Configure 配置方法里,添加启用Cors中间件服务

感谢博友@kiritio_ooo的提醒,Git已更新

注意:如果你使用了 app.UserMvc() 或者 app.UseHttpsRedirection()这类的中间件,一定要把 app.UseCors() 写在它们的上边,先进行跨域,再进行 Http 请求,否则会提示跨域失败。

因为这两个都是涉及到 Http请求的,如果你不跨域就直接转发或者mvc,那肯定报错。

4、运行调试,一切正常

至此,跨域的问题已经完成辣

重要:如果你想查看效果,我的最新的Github上代码已经给大家写好了,大家clone以后,只需要执行 http://localhost:8081/corspost.html ,就能看到各种效果了。当然如果懒得下载,可以看我的在线效果:查看右侧公告栏


注意:这里要说下,如果遇到了跨域失败的提示,比如这样:

这个并不一定是没有配置好导致的跨域失败,还有可能是接口有错误,比如 500,或者是 404 了,导致的接口异常,所以就提示访问有错误。

五、其他跨域方法补充

请参考我的文章:

三十三║ ⅖ 种方法实现完美跨域

  nginx是一个高性能的web服务器,常用作反向代理服务器。nginx作为反向代理服务器,就是把http请求转发到另一个或者一些服务器上。

通过把本地一个url前缀映射到要跨域访问的web服务器上,就可以实现跨域访问。

对于浏览器来说,访问的就是同源服务器上的一个url。而nginx通过检测url前缀,把http请求转发到后面真实的物理服务器。并通过rewrite命令把前缀再去掉。这样真实的服务器就可以正确处理请求,并且并不知道这个请求是来自代理服务器的。

简单说,nginx服务器欺骗了浏览器,让它认为这是同源调用,从而解决了浏览器的跨域问题。又通过重写url,欺骗了真实的服务器,让它以为这个http请求是直接来自与用户浏览器的。

这样,为了解决跨域问题,只需要动一下nginx配置文件即可。

六、结语

三种办法其实都能达到目的,但是优缺点也很明显

1、手动创建JSONP跨域

优点:无浏览器要求,可以在任何浏览器中使用此方式

缺点:格式要求很严格,只支持get请求方式,请求的后端出错不会有提示,造成不能处理异常

2、添加请求头实现跨域

优点:支持任意请求方式,并且后端出错会像非跨域那样有报错,可以对异常进行处理

缺点:兼容性不是很好,IE的话 <IE10 都不支持此方式

虽然CORS的方法有点儿类似请求头,但是封装,兼容性,灵活性都要好的很多,强烈推荐。

七、初探DTOs

请看以下实体类

//数据库实体类
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,明天再见吧~

八、CODE

https://github.com/anjoy8/Blog.Core

https://gitee.com/laozhangIsPhi/Blog.Core

从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十二 || 三种跨域方式比较,DTOs(数据传输对象)初探的更多相关文章

  1. 前后端分离 导致的 静态页面 加载 <script type="module" > 报CORS 跨域错误,提示 blocked by CORS policy

    1.前言 静态页面 加载 <script type="module" > 报CORS 跨域错误,提示Access to script at ftp:///xxx.js ...

  2. 从壹开始前后端分离 [.netCore 填坑 ] 三十三║ ⅖ 种方法实现完美跨域

    缘起 哈喽大家周四好,趁着大家在团建的时候花一个下午学点儿东西,也是督促大家学习哟,希望大家看到老张的文章,可以有一丢丢的学习动力.不过话说过来,该吃的团建还是要去的,不能学我呀 [ /(ㄒoㄒ)/~ ...

  3. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十三 || DTOs 对象映射使用,项目部署Windows+Linux完整版

    更新 很多小伙伴在用 IIS 发布的时候,总是会有一些问题,文章下边 #autoid-6-0-0 我也简单的动图展示了,如何 publish 到 IIS 的过程,如果你能看懂,却发现自己的项目有问题的 ...

  4. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十七 ║Vue基础:使用Vue.js 来画博客首页+指令(一)

    缘起 书说前两篇文章<十五 ║ Vue前篇:JS对象&字面量&this>和 <十六 ║ Vue前篇:ES6初体验 & 模块化编程>,已经通过对js面向对 ...

  5. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十九║Vue基础: 样式动态绑定+生命周期

    回顾 哈喽大家好,前后端分离系列文章又开始了,今天周一,还是感谢大家花时间来观看我写的博客,周末呢,没有写文章,但是也没有闲着,主要是研究了下遗留问题,看过之前文章的应该知道,之前的在AOP使用Red ...

  6. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之七 || API项目整体搭建 6.2 轻量级ORM

    更新 1.在使用的时候,特别是更新数据的时候,如果不知道哪里有问题,可以查看数据库 和 实体类 的字段,是否大小写一致,比如 name 和 Name 2.在使用Sqlsugar 的 CodeFirst ...

  7. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

  8. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十一 || AOP自定义筛选,Redis入门 11.1

    代码已上传Github+Gitee,文末有地址 书说上文<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之十 || AOP面向切面编程浅 ...

  9. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十五 ║Vue基础:JS面向对象&字面量& this字

    缘起 书接上文<从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十四 ║ VUE 计划书 & 我的前后端开发简史>,昨天咱们说到了以我的经历说明的web开发经历的 ...

随机推荐

  1. BZOJ_1076_[SCOI2008]奖励关_状压DP

    BZOJ_1076_[SCOI2008]奖励关_状压DP 题意: 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物, 每次你都可以选择吃或者不吃(必须在抛 ...

  2. 【Canal源码分析】Sink及Store工作过程

    一.序列图 二.源码分析 2.1 Sink Sink阶段所做的事情,就是根据一定的规则,对binlog数据进行一定的过滤.我们之前跟踪过parser过程的代码,发现在parser完成后,会把数据放到一 ...

  3. Java基础-方法重载和方法重写的区别

    什么是java方法重载 (1) 方法重载是让类以统一的方式处理不同类型数据的一种手段.多个同名函数同时存在,具有不同的参数个数/类型. 重载Overloading是一个类中多态性的一种表现. (2)  ...

  4. JDBC知识详解

    一.相关概念 1.什么是JDBC JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它 ...

  5. r.js合并实践 --项目中用到require.js做生产时模块开发 r.js build.js配置详解

    本文所用源代码已上传,需要的朋友自行下载:点我下载 第一步: 全局安装  npm install -g requirejs 第二步: 1.以下例子主要实现功能, 1)引用jq库获取dom中元素文本, ...

  6. Python:游戏:300行代码实现俄罗斯方块

    本文代码基于 python3.6 和 pygame1.9.4. 俄罗斯方块是儿时最经典的游戏之一,刚开始接触 pygame 的时候就想写一个俄罗斯方块.但是想到旋转,停靠,消除等操作,感觉好像很难啊, ...

  7. 详解synchronized与Lock的区别与使用

    知识点 1.线程与进程 在开始之前先把进程与线程进行区分一下,一个程序最少需要一个进程,而一个进程最少需要一个线程.关系是线程–>进程–>程序的大致组成结构.所以线程是程序执行流的最小单位 ...

  8. c#在pictureBox控件上绘制多个矩形框及删除绘制的矩形框

    在pictureBox上每次只绘制一个矩形框,绘制下一个矩形框时上次绘制的矩形框取消,代码如链接:https://www.cnblogs.com/luxiao/p/5625196.html 在绘制矩形 ...

  9. 学习python的第三天

    4.28日总结 一.关于python 1.交互式 说一句解释一句 2.命令行式 1.编写文件并且保存 2.打开python解释器,在pyrhon中打开文本,读入内存(python打开的时候,翻译不是瞬 ...

  10. 如何通过免费开源ERP Odoo建立你的团队, 销售过程和目标

    这种快速的一步一步的指南将引导您完成Odoo CRM, 帮助您轻松处理您的销售渠道, 时刻从线索到客户管理您的销售渠道. 配置 从 Odoo初始化后,生成你的数据库, 选择CRM 作为第一个app安装 ...