|
【联调】客户端与服务器如何通信
之前我们写过vue构建一个前端项目,同时基于go-gin实现了一个API后端,这篇文章主要讲述联调的一些实践,包括前后端如何进行消息通信。
计算机网络
站在巨人的肩膀上,我们可以快速登高
当你在看这篇文章的时候,有没有思考过,你是怎么得到这篇文章的内容的?
- 历史及发展: 计算机网络的最初需求来源于共享信息,所以通过电缆连接局域网内的多台计算机,通过电信号来进行数据传输,伴随发展(问题及需求便会促进发展),庞大的局域网需要开始划分,交换机产生,再随着网络入户,路由器也得以发展,至今,形成了世界互联。
- 网络协议: 一个说英语,一个说中文,必然互相是没办法交流的,所以通信的前提是一致的“约定”。从OSI模型到TCP/IP,以及后面web发展起来的HTTP及HTTPS,我们当下的通信协议已经非常成熟且广泛应用。
- IP与端口: 我们把那些配有IP地址的设备叫主机
,通过这个IP地址,我们可以被点对点定位,但是仅凭ip是不能完成通信的,我们还依靠一些辅助技术,如DNS、ARP等等,当定位到我们主机IP的时候,到底是邮件系统处理还是LOL处理这个消息呢,此时我们需要给每个需要通信的应用加上端口,得以最终定位。
这样我们大概可以从宏观上对网络通信有所理解,我们点击文章,发起了一个域名(映射到IP端口)请求,数据从路由器开始带着目标地址出发,通过协议的层层映射,找到目标主机及程序,程序对请求进行处理后,给
邮差一个返回数据,最终这个文章得以展示在我们的界面上。(PS:以上只是简单的具象化的描述,真正的通信过程其实是复杂的,涉及到非常多的知识,建议阅读一下相关的内容。)
应用协议
这个协议不是计算机网络上提到的协议,而是我们应用层面,前端和后端共享的一套通信协议,约定我们的数据格式。
- 字节流: 网络数据的传输都是通过数据流传输,数据格式就是字节数组,但是我们往往传输的数据是一个对象,为了保证数据的完整、有序性,需要采取序列化和反序列化编解对象。(一般我们存储的时候也是序列化后进行存储)
- 序列化: 把对象转化为可传输的字节序列过程称为序列化,反之为反序列化。
- 几种常用序列化方式: XML、JSON、Protobuf。(json 基本取代了xml,protobuf 也正在成为主流)
诸如,基于JSON来定义登录协议:
// 协议格式,这个是两边约定统一的。
type LoginReq struct {
UserID int64 `json:"user_id"`
Password string `json:"password"`
}
// 此时,前端发送一个 {"user_id": 1, "password": "password"} 的json数据格式即可
异步与跨域问题
- 同步和异步:我们函数调用能直接获得返回值的调用方式为同步调用,当这个函数不能快速获得返回值的时候,我们为了不阻塞后续程序运行,可以异步,即先处理后续程序,等该函数返回了再响应,此为异步。一般前后端分离,消息通信都是异步请求,因为请求的响应时间是不稳定的。
- 跨域资源共享(CORS):指一种基于HTTP头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin(域,协议和端口)。所以之前分享的vue单页应用,其实是起了一个端口8088服务来访问单页资源,而go-gin后端是起了一个端口8888的TCP Server,所以如果直接请求是会受到跨域限制的,按照CORS规则,我们可以在服务器和客户端任意一端做处理。
实践
本次实践代码基于之前的Vue前端和go-gin后端实现。
一、前端接口请求:
- axios: 一个基于 promise 的 HTTP 库。封装一个 request.js 实现api的注册与调用:
```js import axios from 'axios';
const service = axios.create({ baseURL: 'http://localhost:8888', // 定义服务器接口前缀 timeout: 5000 }); // reuest 使用封装 service.interceptors.request.use( config => { return config; }, error => { console.log(error); return Promise.reject(); } ); // 响应封装 service.interceptors.response.use( response => { if (response.status === 200) { return response.data; } else { Promise.reject(); } }, error => { console.log(error); return Promise.reject(); } ); export default service; ```
``` import request from 'request'; // 上面创建的 request.js
/* * 登录验证 * @param {username, password} data * @returns / export const loginReq = data => { return request({ url: '/login', method: 'post', params: data }) } ```
- 函数调用,Login.vue script 添加:
js
loginReq(param).then((res) => {
if (res.code == "200") {
// 登录成功
} else {
// 登录失败
}
});
二、后端
- 后端在之前的文章已经实现了接口,这里后端处理了跨域问题,定义Cors函数,在main函数初始化router前调用,添加Header参数。
```go func Cors() gin.HandlerFunc { return func(c gin.Context) { method := c.Request.Method // 可将将 替换为指定的域名 c.Header("Access-Control-Allow-Origin", "*") c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") c.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
} ```
总结
- 项目实践永远只是小部分内容,真正的工程项目中,有非常复杂的处理与设计,积累基础是理解复杂设计的前提。
- 通过基础的应用搭建,我们可以在此基础上实现我们想要实现的功能。看1w行代码也比不上写10行代码,赶快开始写起来吧。
- 微信关注公众号 做个项目 后台回复 管理系统
可以获得前后端项目源码,项目目录front为前端路径,backend为后端路径。
|
|