许多进行前后端开发的同学,在学习过程中也总会问我一个问题:前后端到底是怎么沟通的?
这个问题,可以说是许多学习开发的同学的共同的问题,也是阻碍大多数学习开发和团队协作路上的绊脚石。
但事实上,这个问题远比你想象得简单得多。
1,前后端的交互概述
我们在日常生活中,每天都会上网。无论是我们浏览网页,还是使用QQ、微信。
细心的同学已经发现了:无论是网页,还是我们的一些客户端例如QQ、微信等等,都会通过网络和后台服务器联络并获取最新的信息。
比如说每次打开掘金社区首页,就会推送不一样的最新内容,这是网页联络了其对应的服务端获取的数据:
比如说我们每次打开网易云音乐客户端,发现推送的歌单也不一样,也会包含最新内容,这也是客户端通过网络联络到后台服务端获取的数据:
那大家就很好奇了:这些网页、客户端是怎么和服务器联络的呢?服务器这边又做了什么?
其实答案很简单:无论是网页还是客户端等等,都通过网络请求和后台服务器联络。
就像我们平时通过QQ或者短信等给远在千里的朋友发消息聊天一样,事实上,网络请求就很像我们QQ或者是短信给别人发的一条条消息。
只不过,网络请求是网页或者电脑上的一些应用软件(下面我们统称为客户端)给服务器发的“消息”,并且在网络环境和服务端正常的情况下,客户端发的“消息”,服务端收到后是一定会“回复”的。
客户端发出的消息内容,叫做请求体,服务端回复的消息内容,叫做响应体。
我们以掘金首页为例,每次从打开掘金的首页,到我们看到上面推荐的最新内容,大致可以分为以下几步:
- 加载掘金网页,网页就会先给它自己的服务器后台(掘金后台服务器)发送一个网络请求,这个请求会发送给后台那个获取所有推荐文章的网址(接口)上,以获取所有的推荐文章的列表
- 后端服务器就会从数据库中找来这些文章,并把这些文章的信息比如说每个文章的地址、标题、封面图片url等等,组装成JSON形式,放在响应体中返回给网页
- 网页获取到响应,便把信息解析并排版成好看的样子,可见这里,网页将这些文章信息给组装成了一个个小卡片
上图是在谷歌浏览器的开发者工具中查看的网络请求信息,可见很显然这个网络请求是用于获取掘金文章推荐文章的信息的,响应体中也可见服务端返回了这些文章的信息。
这就是我们平时上网的过程,是不是很简单呢?其实没有大家想象的那样复杂。
这张示意图给出了一种常见的前后端交互的例子:前端向后端发送查询信息的请求,以获取最新信息。
对于开发者来说,前端同学,开发网页的居多,在JavaScript中有发送网络请求的方法,可以发送网络请求。后端同学,开发Spring Boot居多,通常会在Spring Boot中开发相应的功能并创建接口,告诉前端同学接口地址、功能和响应数据体格式,这样前端同学就可以对后端发送请求并获得相应的数据,完成整个系统开发。
至此,相信大家明白了,咱们前后端同学到底是做什么的:
- 前端:负责开发呈现给用户看的界面
- 后端:负责开发系统的逻辑(例如说从数据库查出东西、进行处理规整、返回给前端)
相信大家现在知道了这个问题的结论:前后端交互的方式,就是通过网络请求。
可见,通过网络请求和后端进行交互,赋予了我们网页或者客户端更加动态的内容,使得网页不再单调。
2,网址 - URL
无论是我们平时上网,通常会指定浏览一个地址,比如说要求必应搜索引擎,我们就会输入网址:cn.bing.com
同样地,前端给后端发请求,也需要明确后端服务器的地址才能发送。
这个地址,我们通常称作URL。
URL是缩写,其英文全称是:Uniform Resource Locator(统一资源定位符)。
URL的格式规范规定了由哪几部分组成,以及各种符号的作用。下面来看一个网址:
可见一个完整的网址有如下内容:
- 协议:常用的是
http
和https
,了解有这两种协议即可 - 域名:指向这个网站服务器地址的一串字符串
- 路径:以
/
开头,中间每一层也是使用/
,路径就像是一层层的文件夹,不同的路径通常指向这个服务器下不同的网页或者API等等 - 参数:和路径之间以
?
分隔,?
后面就是参数了,多个参数之间使用&
分隔,每个参数是参数名=参数值
的形式,可见上述URL中只有一个参数,参数名为spm_id_from
,参数值为333.999.0.0
一些网址,以及我们平时本地开发调试的网址,可能会带端口号,因为使用了非标准端口:
但是为什么好像很少平时上网会见到有网址带端口号呢?因为大多数网站都是使用的标准端口,用http
协议访问的网址就是80端口,而https
访问的网址就是443端口的,使用80和443端口的服务端,访问它就是不需要带上端口号的。因为浏览器访问http
网址的时候就会自动访问其80端口,而https
的网址浏览器自动访问其443端口,所以我们就不需要带着端口号了!
网址是我们平时非常常见的东西了。不过大家知道网址实际上的作用吗?
其实,我们每一台接入互联网的设备,无论是计算机还是服务器,都会被分配一个IP地址。这个IP地址就像你家的详细地址门牌号一样,就像我知道了你家的地址我才能到你家来一样,需要有IP地址,我们才能够访问到指定的服务器上面去。
不过IP地址并不方便记忆,因此人们设计出来了域名,可以说就是网址,并通过域名解析服务器(DNS,Domain Name System)将域名和IP地址相映射,这样,每次我们访问一个网址,就可以找到对应服务器的IP并访问指定服务器,大家就能浏览到网页了!
所以说,一些网址我们无法访问,可能是DNS找不到这个网址所在的IP或者是受到了DNS污染:
域名对应着一个IP,你访问到了这个服务器,但是这个服务器下面有多个页面,因此使用路径来标明每个网页,就和我们平时使用电脑的文件路径一样。不过为什么大多数时候,我们访问一个域名但是没有写后面的路径部分,就能够访问到一个网站主页呢?因为只访问地址不加路径的时候,默认访问网址根路径,而大多数网站为了方便起见,把主页放在根路径下了。
3,API
API即为接口,就像一个黑盒一样,开发者可以直接访问而无需知道其内部原理。
我们访问一个API的时候,和浏览一个网页一样,也是去访问一个URL。
只不过我们访问网页的URL的时候,服务器会返回一个网页文件,浏览器把这个网页显示出来。而访问API的URL的时候,大多数时候返回的是一些文本数据而非网页。
比如说在浏览器中访问:cn.bing.com
很显然这是一个网页的URL,浏览器显示出了网页:
如果我们在浏览器中访问一个API的地址:api.iyk0.com/7rtq/?city=南京
这是网上一个免费的天气查询接口,在这个接口URL后的city
参数填上你要查询的城市即可查到该城市七日天气。
总的来说,平时访问的网站和这里访问的API,区别如下:
- 我们访问的网站的URL,会返回网页,浏览器会解析并显示网页内容
- 访问API的URL,会返回一些数据,这些数据就通常是后端从数据库查询并组织成JSON格式并返回的
API可能返回的是文本,也可能是图片、文件等等。
相信大家现在知道了,前后端交互是前端将网络请求发给后端的API,后端API返回数据,网页再将其解析为好看的样子。
无论是Spring Boot还是其它后端框架,基本上都可以轻而易举地解析出请求的URL参数、请求体的信息、请求体等,大多数情况下,基本上查询作用的API接口,都是后端根据URL中的请求的参数或者是路径去查询指定的内容,然后返回。
API通常不是给用户访问的,我们平时上网也很少去拿浏览器访问一个API,而是给前后端交互使用的。
在最开始的例子中,掘金网页(前端)向后端某个网址发了请求获取最新文章,后端就返回了最新推荐的文章信息,这个网址就是掘金后端用于获取最新文章的API。
前面也讲述了前后端交互的过程,就是前端向后端对应功能的API发送请求,后端返回数据给前端。那么开发API,也是后端开发者的工作之一。
我们大致了解API是什么即可,实际上开发的时候大家就明白了。
4,网络请求结构和类型
网络请求其实有很多,我们常用的是GET
和POST
请求。
网络请求通常有以下部分构成:
- 请求行:记录一个请求最基本的信息,例如请求的类型、请求的url和协议等等
- 请求头:主要存放请求的标识信息或者元数据之类的,例如浏览器UA、请求内容格式(是表单还是json)等等
- 请求体:存放请求的数据内容(一般是键值对),一个请求可以没有请求体
当然,后端接收到请求后返回的响应,组成结构和请求是一样的,也是三部分:
- 状态行:由HTTP协议版本号,状态码,状态消息三部分组成
- 响应头:响应的基本信息或者元数据,其中包含了响应体的格式
- 响应体:服务端返回给客户端的数据,例如访问网页时响应体就是网页,访问API时访问体就是一些JSON格式的文本数据或者是文件图片等等
这些内容大家事实上都可以在谷歌开发者工具中去查看,非常简单。
我们常见的网络请求有两种类型:GET
请求和POST
请求。
(1) GET
请求
我们平时在浏览器输入网址访问一个网站,以及使用很多查询类API都是发送了GET
请求。
就像我们上述的天气查询API一样,直接通过浏览器就是对其发送了GET
请求。
这个接口说明文档如下:
根据API,比如我要查询南京天气,我们的请求地址如下:
https://api.iyk0.com/7rtq/?city=南京
可见GET请求很简单,并且可以传递参数,上述就传递了city
参数,值为南京
。
GET请求通常是没有请求体的,其参数写在URL中传递。
这个天气API来自免费API网站:api.iyk0.com/ ,大家可以去试试,感受一下API的使用。
(2) POST
请求
上述GET
请求的参数是直接放在url中的,但是POST
请求的参数就是放在请求体中了。请求体我们平时看不到。
我们可以在谷歌浏览器f12打开开发者工具,在网络这一栏查看请求:
这是浏览掘金时看到的一个请求,为一个POST请求,可以看到其请求行、请求头和请求体。
谷歌浏览器的Network(网络)部分可以查看全部网络请求,过滤器设置为
Fetch/XHR
可以查看该网页所有的对API发送的请求数据,一般要先f12打开控制台然后刷新一下才可以捕获到这些请求,大家一定要学会使用这个开发者工具,以及一些研究爬虫的开发者也会常常使用这个Network功能。
其实,网络请求的类型并不只有这两种,还有很多例如PUT
、PATCH
、DELETE
等等,有兴趣的同学可以上网查阅。
虽然POST请求通常用请求体存放数据,但其仍然可以通过URL传参。
(3) GET
请求和POST
请求的区别
网络请求都是为了客户端和服务端的沟通,信息传递。而GET
请求和POST
请求其实从名字上我们就能看出来两者的标准用途是不一样的:
GET
请求通常用于客户端从服务端获取信息,例如掘金首页要获取最新的文章信息POST
请求通常用于客户端发送信息给服务端,例如我们平时用户登录,都需要发送用户名和密码到服务端,都是通过POST
请求
其本质上最大的区别,就是GET
请求的请求内容(键值对)放在URL中,也就是说GET
请求没有请求体,而POST
请求的请求内容就放在请求体里面了。
GET
请求可以比作我给别人寄明信片,地址和内容都写在明信片上面了。POST
请求可以比作我给别人寄一封信,地址写在信封上,内容专门用信封(请求体)装起来寄出去了。
5,JSON数据格式
前面我们一直在说很多响应体是JSON格式的,确实是这样,因为JSON格式比较容易解析且轻量级。那到底什么是JSON呢?
JSON即为JavaScript对象简谱,可以说是使用字符串表示JavaScript对象的一种格式,是现代互联网一种轻量级的数据交换格式,基本上我们后面调用接口传输数据很多都是json格式的,本质上是有一定格式的字符串。
在JavaScript中,可以很容易地把响应体中的JSON数据直接转换为JavaScript的对象。而前端通过POST
请求发送给后端的JSON数据(在请求体中),也很容易被后端解析成对应的Java对象或者其它编程语言的对象。
我们来看一个JSON数据:
{ "name": "美美", "age": 10, "organization": "小小甜心", "race": "兽人族", "position": "中卫", "skills": ["兔兔斩击", "跳跳攻击", "兔兔应援", "兔兔的力量"] }
这是一个游戏角色的属性数据,可见JSON可以像我们面向对象的编程语言中的对象一样很清晰地描述一个对象的信息。
JSON有以下重要数据类型:
- 对象:对象用一个花括号
{}
表示,其中有一些键值对就是对象的属性和值,例如上述对象中有name
属性,值为"美美"
。对象的属性的值也可以是一个对象 - 数组:数组用方括号
[]
包围,和编程语言中的数组一样表示多个元素的集合,上述对象中的skills
属性的值就是个字符串数组
事实上,JSON数据非常好懂,并且在各个编程语言中,有着许多成熟的外部库能够使得JSON和该编程语言的对象互相转换。
毕竟任何编程语言中表示的对象,都是在内存中的数据,这些内存中的数据都是以二进制储存,是无法直接在网上传输的,因此需要进行序列化,把编程语言中表示的对象转换成JSON字符串就是一种序列化的方式,反过来把JSON变成编程语言中的对象就是反序列化的过程。
正由于JSON轻量级、易解析的特点,很多时候我们都选择JSON作为我们序列化的主要格式和方案。
在前面前后端交互的例子中,后端通常就会把取出的数据序列化为JSON的格式返回给前端,前端再把JSON数据转换成JavaScript对象以显示在网页上。