和服务端通信

本章讲解和服务端系统的通信,示例使用的服务端系统是 我们开发的 bysms药品销管系统 ,也是我们后面项目实战1要实现的系统

请大家点击这里,按照说明下载、安装、运行 bysms系统

HTTP 协议

web网站 前端浏览器 和 后端系统 之间就是通过HTTP协议进行通信的,所以要学习前端的开发,必须先了解HTTP协议的基础知识。


请大家点击这里去学习白月黑羽的 HTTP协议 教程

请看完链接教程的 前3章

里面有涉及到 Python编码的部分 可以忽略,重点是:

  • HTTP协议的概念

  • 请求消息,响应消息的格式

XHR/AJAX

用户访问网页,包括网页里面内嵌的 CSS/js/图片 等等资源,通常是浏览器直接获取的,这种获取请求的HTTP被称之为 同步请求

而浏览器执行网页包含的js代码的过程,有时js代码还要到服务端 提交、获取数据, 这样的请求被称之为 异步请求

异步请求,典型的出现在前后端分离的web系统中

系统架构大体如下图所示

image


浏览器的js环境内置了一个 XMLHttpRequest 类型,简称 XHR

可以这样创建一个 XHR 对象

var xhr = new XMLHttpRequest();

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://localhost/index.html


// 异步请求:获取api数据
http://localhost/api/getuserdata

对接收到 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 请求,可以使用这样的代码

$.ajax({  
  type: 'GET',        
  url : '/api/mgr/signin',  
})

这里settings参数包含了两个键值对数据:

  • type 是 HTTP方法

    比如 GET,POST,PUT,DELETE,HEAD 等等

  • url 是 请求的url地址

    比如这里就是 /api/mgr/signin

    注意:这里url没有前面的 http://IP:端口 这部分,表示前面的这部分和当前网页前面的这部分相同


下面是是POST请求的一个代码示例

$.ajax({  
  type: 'POST',        
  url : '/api/mgr/signin', 
  data: {
    username:'byhy',
    password:'abc',
  }
})

这里 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

$.ajax({  
  type: 'GET',        
  url: 'http://localhost/api/something',
})

// 或者
$.get('/api/mgr/signin')

讲HTTP协议的教程里面提到,url参数 格式都是 urlencode 格式

比如:

https://www.baidu.com/s?wd=iphone&rsv_spt=1

问号后面的部分 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 属性设置,如下


$.ajax({  
  url: '/api/something',
  type: 'GET', 
  headers: {"X-Test-Header": "test-value"}
})

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

如果我们没有登录成功,就执行上面的代码,服务端就会要求认证了。