和服务端通信
本章讲解和服务端系统的通信,示例使用的服务端系统是 我们开发的 bysms药品销管系统
,也是我们后面项目实战1要实现的系统
HTTP 协议
web网站 前端浏览器 和 后端系统 之间就是通过HTTP协议进行通信的,所以要学习前端的开发,必须先了解HTTP协议的基础知识。
请看完链接教程的 前3章 。
里面有涉及到 Python编码的部分 可以忽略,重点是:
-
HTTP协议的概念
-
请求消息,响应消息的格式
XHR/AJAX
用户访问网页,包括网页里面内嵌的 CSS/js/图片 等等资源,通常是浏览器直接获取的,这种获取请求的HTTP被称之为 同步请求
而浏览器执行网页包含的js代码的过程,有时js代码还要到服务端 提交、获取数据, 这样的请求被称之为 异步请求
异步请求,典型的出现在前后端分离的web系统中
系统架构大体如下图所示
浏览器的js环境内置了一个 XMLHttpRequest 类型,简称 XHR
可以这样创建一个 XHR 对象
XHR 对象 有属性方法,支持 浏览器页面代码 和 服务端之间的通信。
这种使用 XMLHttpRequest 对象 和 服务端之间的 通信技术,是一种异步的通信方式。
而且开始的那几年,这种方式传输数据,使用的序列化方法主要是 XML, 所以又被称之为 AJAX (Asynchronous JavaScript And XML)
方式。
使用 浏览器原生的 XMLHttpRequest 对象收发消息,可以点击这里了解。
缺点是:写起来比较麻烦,所以目前并不推荐使用。
现在比较多的用法是: 使用 jQuery库 ,或者 ES6引入的 fetch
API
这里介绍 使用 jQuery库 和服务端通信
前后端API 接口
当我们的js代码要服务端异步通信时,HTTP请求访问的服务端要获取的 不是像 HTML/CSS/JS 这样的静态文件资源
往往是要求服务端程序进行数据库操作后,再进行代码处理,返回的数据。
这样的异步请求,往往被称为 API请求
提供这种服务的后端,被称为 API 服务端
这种服务接口,被称为 API 接口
。 该接口由系统设计者设计定义,通常会提供接口说明文档,比如我们项目实战1 的 API接口文档
针对该接口测试服务端实现、客户端实现,就是 大家经常听说的 API接口测试
。
后端实现对 静态文件的访问服务 和 动态API请求服务,往往不是一个程序。
比如,静态文件是 nginx 提供的服务,API则是单独开发的业务服务程序
那问题来了:
浏览器发送的 API XHR请求 和 获取静态文件 请求的URL都是同样的 协议、IP、端口,
比如
对接收到 HTTP请求的 后端程序 来说,并不存在同步异步的概念(同步异步HTTP请求的区别是针对前端浏览器的)。
后端是怎么知道哪个请求该由哪个程序处理呢?
解决方法通常是这样:
系统设计者在设计 API 接口消息时, 规定的 API 消息的 URL 路径会有明显的特征,
比如都是以 /api
开头
这样,后端可以配置哪些url访问请求是针对API接口的,转发为API服务
前面介绍过,开发环境下,web服务可以使用 VSCode 的扩展 live Server,
live Server有代理配置项,可以设置转发规则
如下配置,
"liveServer.settings.proxy": {
"enable": true,
"baseUri": "/api",
"proxyUri": "http://127.0.0.1:80/api"
}
其中 baseUri 就是配置什么样的请求是要转发给API服务的
这里 /api
, 就是把 /api
开头的 http请求 都转给 bysms系统处理,
因为 bysms系统服务的IP和端口 是 127.0.0.1:80
,
所以 proxyUri
的值为 http://127.0.0.1:80/api
,也可以写成 http://127.0.0.1/api
注意: http://127.0.0.1:80/api
后面的部分 /api
不能少,
否则如果是 http://127.0.0.1:80
, 发送的url /api/customer
,到了 服务后端 就变成了 /customer
jQuery 构建请求消息
我们可以使用jQuery库里面的 ajax()方法 和服务端进行HTTP消息的收放
请求方法
典型的ajax()方法,接收一个settings参数,该参数是一个PlainObject类型, 可以把它当作 Object 来用
可以在里面设定 XHR(AJAX)请求的数据 , 包括 请求方法、url、消息头和消息体,比如,要发送 get
请求,可以使用这样的代码
这里settings参数包含了两个键值对数据:
- type 是 HTTP方法
比如 GET,POST,PUT,DELETE,HEAD 等等
- url 是 请求的url地址
比如这里就是 /api/mgr/signin
,
注意:这里url没有前面的 http://IP:端口
这部分,表示前面的这部分和当前网页前面的这部分相同
下面是是POST请求的一个代码示例
这里 data 是请求消息体,后面有讲解
如果请求的方法是get、post,还有简便写法,如下
$.get('/api/mgr/signin')
$.post('/api/mgr/signin',
{
username:'byhy',
password:'abc'
}
)
// 或者
$.get({
url : '/api/mgr/signin',
})
$.post({
url : '/api/mgr/signin',
data: {
username:'byhy',
password:'abc',
}
})
可以把上述代码放到HTML中,比如名为 test.html,如下
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
</head>
<body>
测试消息,请按F12打开DevTools的网络标签页观看
<script>
$.get('/api/mgr/signin')
</script>
</body>
</html>
然后启动 live server (注意按照前面说的,配置好vscode扩展 live server 的 proxy 到 bysms)
然后,打开浏览器按F12打开DevTools的网络标签页,观看网络请求
然后,访问这个test.html
就可以看到 js 代码发出了请求,并且被转发到bysms系统,bysms系统返回了响应数据
url 和 url参数
前面讲过,jQuery 发送http请求, 可以使用下面的写法指定 url
讲HTTP协议的教程里面提到,url参数 格式都是 urlencode 格式
比如:
问号后面的部分 wd=iphone&rsv_spt=1
就是 url 参数,
每个参数之间是用 &
隔开的。
上面的例子中 有两个参数 wd 和 rsv_spt, 他们的值分别为 iphone 和 1
自己写代码拼接出 urlencode 格式的字符串有时比较麻烦,
可以使用浏览器内置的 URLSearchParams 类型。
URLSearchParams对象的方法 toString
,可以将对象序列化为 urlencoded 格式
比如:
var queryStr = new URLSearchParams({code:'6000001', time:'2022-02-23' }).toString()
$.get(`http://localhost/api/stock?${queryStr}`)
也可以使用 jQuery 的 param 方法
var queryStr = $.param({code:'6000001', time:'2022-02-23' })
$.get(`http://localhost/api/stock?${queryStr}`)
消息头
jQuery 发送http请求要定制一些消息头,可以通过ajax方法的settings参数里面的 headers
属性设置,如下
contentType
ajax方法的参数对象 contentType 设置消息头 contentType 的值
缺省为: application/x-www-form-urlencoded; charset=UTF-8
可以改为其它的,比如
$.ajax({
url: '/api/mgr/signin',
type: 'POST',
contentType : 'application/json',
data: JSON.stringify({ username: 'byhy', password:'abc'}),
})
消息体
jQuery的请求消息体都是参数对象 settings的 data
属性中设置的
该属性的值可以是一个 Object 或者 字符串
urlencode 格式
消息体也可以是 urlencode 格式,同样可以使用 URLSearchParams 或者 jQuery的 param 方法。
其实,如果使用jQuery发送请求, data参数如果是 Object,缺省行为就是转化为 urlencode格式
所以可以直接传对象,如下
$.ajax({
// 提交的网址
url: 'http://localhost/api/mgr/signin',
type: 'POST',
data: { username: 'byhy', password:'88888888' }
})
JSON 格式
现在的API接口消息体 很多是JSON格式的字符串。
可以使用浏览器js内置对象 JSON
JSON 的方法 stringify
可以序列化 js对象 为 JSON格式的字符串
$.ajax({
url: '/api/mgr/signin',
type: 'POST',
contentType : 'application/json',
data: JSON.stringify({ username: 'byhy', password:'88888888'}),
})
jQuery 解析响应消息
解析响应消息的前提是能正确获取到响应消息
如果使用的是jQuery, settings的 success属性函数定义了成功接收到HTTP响应消息的回调函数
可以在回调函数的第3个参数 jqXHR 对象获取响应消息的信息
这个jqXHR 类型 是 XMLHTTPRequest 的扩展类型,封装了一些便捷方法
消息头
$.ajax({
url: '/api/mgr/signin',
type: 'POST',
data: 'username=byhy&password=88888888',
// 正确返回
success: function(data, textStatus, xhr) {
// 获取状态码
console.log(textStatus);
// 获取所有消息头
console.log(xhr.getAllResponseHeaders());
// 获取某个消息头
console.log(xhr.getResponseHeader("content-length"));
},
// 错误
error:function (xhr, textStatus, errorThrown ){
console.error(`${xhr.status} \n${textStatus} \n${errorThrown }`)
}
})
- success 请求响应成功后的回调函数
所谓成功就是返回响应消息,
回调函数被传入3个参数:
-
data 从服务端返回的数据
-
textStatus 返回的状态文本描述
-
xhr 这是 XMLHttpRequest的扩展类型jqXHR的对象
-
error 请求响应失败后的回调函数
回调函数被传入3个参数:
-
xhr 这是 XMLHttpRequest的扩展类型jqXHR的对象
-
textStatus 返回的错误状态文本描述
比如:"timeout", "error", "abort", 等等 "parsererror"
-
errorThrown 异常对象文本
比如:"Not Found" 或者 "Internal Server Error."
消息体解析
如果使用的是jQuery,ajax 参数 settings的 success属性函数定义了成功接收到HTTP响应消息的回调函数
回调函数的第1个参数 data 包含了响应的消息体数据。
jQuery 会根据响应消息信息(比如 content-type 消息头)猜测对应的数据类型,从而进行相应的处理。
比如下面的示例
$.ajax({
url: '/api/mgr/signin',
type: 'POST',
data: 'username=byhy&password=88888888',
// 正确返回
success: function(data, textStatus, xhr) {
console.log(data)
}
})
登录成功后, 服务端返回的响应的 消息头 content-type : application/json
指明了 响应的消息体是json格式,
jQuery 会自动反序列化为 js中对应的数据对象, 传递给 data参数。
如果明确知道响应消息体数据格式, 应该设置 ajax 参数 settings 的 dataType
属性说明这个data的类型
比如 设置为 json,
$.ajax({
url: '/api/mgr/signin',
type: 'POST',
data: 'username=byhy&password=88888888',
// 响应消息格式 预设为 json,
dataType: 'json',
// 正确返回
success: function(data, textStatus, xhr) {
console.log(data)
}
})
dataType
属性值还可以是
- xml
返回 XML 对象
- html
返回 HTML 字符串
- text
返回消息体字符串
session 机制
session机制在我们的API接口教程中有讲解,点击这里学习
学完大家就会知道, 采用session机制的系统设计,通常session号是认证成功后, 服务端是通过 HTTP的响应头 Set-Cookie
把产生的 sessionid 告诉客户端的。
Set-Cookie
是设定服务端要存放在客户端的Cooked数据,这些数据,浏览器会自动保存。
后续访问该网站的其它请求,浏览器会自动在HTTP的请求头 Cookie
中携带保存的所有cookie数据。
所以,只要已经调用登录接口,获取了sessionid,后续的请求代码,不需要我们自己的代码里面设置。
比如
$.ajax({
url: '/api/mgr/customers?action=list_customer&pagesize=5&pagenum=1',
type: 'GET',
// 响应消息格式 预设为 json,
dataType: 'json',
// 正确返回
success: function(data, textStatus, xhr) {
console.log(data)
}
})
如果我们没有登录成功,就执行上面的代码,服务端就会要求认证了。