网络基础

知识回顾

  • Web服务器:网络中的一台电脑,用于存储Web资源。
  • 客户端软件:浏览器

那么我们是怎样访问到Web服务器中的资源的呢?如果想弄清楚这件事,还需要掌握如下知识点

URL地址

地址:
生活:北京市 顺义区 京顺路99号 =============== 校区
网络:http://www.xxx.com/sss/sss.png =============== 网络中的资源

就像生活中的地址一样,URL用于定位网络当中的资源,每个资源都对应着一个独一无二的URL地址。

协议

什么是HTTP

超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准,
HTTP协议默认端口号是 80

什么是HTTPS

HTTPS 是身披 SSL外壳的 HTTP。HTTPS是一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。
HTTPS协议默认端口号是 443

主机地址

IP地址

IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
比如生活中的地址,表示地理上的一个位置,通过这个地址可以找到这个地理位置。
同样的,互联网中的每一台主机电脑,都有一个地址。通过这个地址可以找到这台主机电脑。
本机IP:127.0.0.1

域名

形如 www.xxx.com 这样的就叫做域名,域名需要单独购买。购买后,需要绑定IP地址进行使用。绑定IP地址后,就可以通过域名找到主机电脑了。
本机域名:localhost

DNS服务器

DNS服务器用于记录 IP地址和域名的对应关系。
前面说,域名和IP地址是绑定关系,那么某个域名绑定的IP地址是什么呢?这个对应关系就记录在DNS服务器当中。
通过DNS服务器可以解析出 一个域名对应的IP地址。
www.itcbc.com ————————- 123.57.103.104
www.baidu.com ————————- 23.89.134.189

端口号

所谓的端口,就好像是门牌号一样,客户端可以通过ip地址找到对应的服务器端
但是服务器端是有很多应用程序的,每个应用程序对应一个端口号,通过端口号,客户端才能真正的访问到该服务器上的应用程序。
为了对端口进行区分,将每个端口进行了编号,这就是端口号。
端口号的范围是 0 ~ 65535
如果一个应用程序使用了3006端口号,其他的应用程序就不能再使用3006端口号。

客户端请求服务器的步骤

当我们在浏览器地址栏输入 “http://www.xxx.com/api/getbooks”时,客户端是如何找到服务器并发送请求的?

  1. 先找到服务器
    1. 检测浏览器缓存有没有缓存该域名对应的IP地址。有则通过IP地址去找服务器
    2. 检测本地的hosts文件,是否有记录该域名对应的IP地址,有则通过IP地址去找服务器
    3. 通过DNS服务器解析,找到该域名对应的IP地址,然后通过IP地址去找服务器
  2. 进行三次握手(建立连接,确认一下双方是否有发送数据和接收数据的能力)
    1. 第一次握手是在建立连接,客户端发送连接请求报文段,把标有SYN的数据包发给服务器端即为接收端。
    2. 第二次握手是服务器端即接收端收到客户端的SYN的报文段,同时发送标有SYN/ACK的数据包。
    3. 第三次握手是客户端收到服务器端的SYN/ACK的数据包后,向服务器端发送标有ACK的数据包。
  3. 发送请求,传输数据。。。
  4. 服务器处理,并做出响应
  5. 浏览器接收响应结果。
  6. 四次挥手(作用是断开连接)

    写接口步骤梳理

    image.png
    image.png
    image.png
    image.png
    image.png

    Express

    express 介绍

  • Express 是一个第三方模块,用于快速搭建服务器(替代http模块)
  • Express 是一个基于 Node.js 平台,快速、开放、极简的 web 开发框架。同类型产品 Koa
  • Express保留了http模块的基本API,使用express的时候,也能使用http的API
    • 使用express的时候,仍然可以使用http模块的方法,比如 res.end()、req.url
  • express还额外封装了一些新方法,能让我们更方便的搭建服务器
  • express提供了中间件功能,其他很多强大的第三方模块都是基于express开发的
  • Express 官网
  • Express 中文文档(非官方)
  • Express GitHub仓库
  • 菜鸟教程
  • 腾讯云开发者手册
  • 百度自行搜索

    安装 express

    项目文件夹中,执行 npm i express。即可下载安装express。

    注意:express不能安装在express文件夹中。否则安装失败。

使用Express构造Web服务器

使用Express构建Web服务器步骤:

  1. 1) 加载 express 模块
  2. 2) 创建 express 服务器
  3. 3) 开启服务器
  4. 4) 监听浏览器请求并进行处理
  1. // 使用express 搭建web服务器
  2. // 1) 加载 express 模块
  3. const express = require('express');
  4. // 2) 创建 express 服务器
  5. const app = express();
  6. // 3) 开启服务器
  7. app.listen(3000, () => console.log('express服务器开始工作了'));
  8. // 4) 监听浏览器请求并进行处理
  9. app.get('GET请求的地址', 处理函数);
  10. app.post('POST请求的地址', 处理函数);

express封装的方法

express之所以能够实现web服务器的搭建,是因为其内部对核心模块http进行了封装。
封装之后,express提供了非常方便好用的方法。
比如前面用到的 app.get() 和 app.post() 就是express封装的新方法。
下面再介绍一个 res.send() 方法

  • 该方法可以代替原生http模块中的 res.end 方法,而且比 res.end 方法更好用
  • res.send() 用于做出响应
  • 响应的内容同样不能为数字
  • 如果响应的是JS对象,那么方法内部会自动将对象转成JSON格式。
  • 而且会自动加Content-Type响应头
  • 如果已经做出响应了,就不要再次做出响应了。
    1. const express = require('express');
    2. const app = express();
    3. app.listen(3006, () => console.log('启动了'));
    4. // 写接口
    5. app.get('/api/test', (req, res) => {
    6. // res.end('hello world,哈哈哈'); // 响应中文会乱码,必须自己加响应头
    7. // res.end(JSON.stringify({ status: 0, message: '注册成功' })); // 只能响应字符串或者buffer类型
    8. // express提供的send方法,可以解决上面的两个问题
    9. res.send({ status: 0, message: '注册成功' }); // send方法会自动设置响应头;并且会自动把对象转成JSON字符串
    10. });

    使用 nodemon 命令运行上述代码后,即可使用 http://localhost:3000/api/test 请求接口了。

全局模块 nodemon

当服务器代码修改后,就要重新启动服务,非常麻烦。
nodemon 是一个全局模块,安装后,可以使用 nodemon 代替 node 运行js文件。
好处是,当代码保存后,nodemon 会检测文件代码是否改变了,如果改变了,就会自动重启服务器。
全局安装nodemon

  1. npm i nodemon -g
  2. # mac系统,安装全局模块,比如加 sudo ,例如 sudo npm i nodemon -g

使用

  1. nodemon xxx.js

使用nodemon 命令代替node命令,我们就可以专心开发代码了,而不用但是服务有没有重启的问题了。 建议不要滥用nodemon,建议只在启动服务时使用nodemon。其他情况继续使用node。

接口测试工具

上述使用浏览器测试接口,只能测试GET方式的接口。如果是POST方式的接口,则无法使用浏览器测试。
所以就需要一个专业的测试接口的工具。
常用的的接口测试工具:

  • Postman (默认英文版本,github上有中文汉化包)
  • ApiPost(默认中文)

两个软件用法类似,且都比较简单。下面拿ApiPost说明。

  • 首先,登录ApiPost,没有账号的需要先注册
  • 点击侧边栏的“项目”,创建一个新的项目
  • 在“项目”页,选项进入该项目接口
    04-写接口(砍掉) - 图6

    发送GET请求

    Postman

    04-写接口(砍掉) - 图7

    ApiPost

    04-写接口(砍掉) - 图8

    发送POST请求

    Postman

    04-写接口(砍掉) - 图9

    ApiPost

    04-写接口(砍掉) - 图10

    写接口

    写接口的目的是体会后端的接口是怎么写的。所以写最简单的接口即可。

  • 创建后端接口项目文件夹,比如 code

  • 下载安装所需模块 express

    创建index.js

    创建index.js,作用是编写后端代码,启动服务。
    使用的模块为 express。
    初始代码,可以去 https://www.expressjs.com.cn/starter/hello-world.html 复制并修改

    1. // 用于启动服务,并且写接口
    2. const express = require('express')
    3. const app = express()
    4. // 写接口
    5. // app.请求方式('接口地址', '带有req、res参数的处理函数');
    6. // app.get('/api/student', (req, res) => {});
    7. // app.post();
    8. // app.delete();
    9. // app.put();
    10. app.listen(3000, () => console.log('启动了'));

    只需这几行,就可以使用 nodemon index.js 启动服务了。

    封装操作JSON的模块

  • 准备好存储数据的 json文件(books.json)。

  • 准备好处理JSON文件的自定义模块。

    准备JSON文件

    真实开发中,数据一般都放在数据库中存储,作为前端程序员,大部分都不了解数据库。
    所以,我们可以使用JSON文件存储数据。(使用JSON文件代替数据库,以便完成后续的开发)
    创建 db.json ,文件内容如下:

    1. [
    2. {
    3. "id": 1,
    4. "bookname": "西游记",
    5. "author": "吴承恩",
    6. "publisher": "北京出版社"
    7. },
    8. {
    9. "id": 2,
    10. "bookname": "红楼梦",
    11. "author": "曹雪芹",
    12. "publisher": "上海出版社"
    13. },
    14. {
    15. "id": 5,
    16. "bookname": "水浒传",
    17. "author": "施耐庵",
    18. "publisher": "清华出版社"
    19. },
    20. {
    21. "id": 12,
    22. "bookname": "三国演义",
    23. "author": "罗贯中",
    24. "publisher": "商务出版社"
    25. }
    26. ]

    封装操作JSON文件的模块

    为了方便对JSON文件中的数据进行查询,新增,修改,删除等操作。我们自己封装一个自定义模块

  • 自己创建 json-parser.js 文件

  • 我们可以使用 then-fs 进行封装。
  • 约定,每一个方法都返回Promise对象
  • 后期,就可以通过 .then 或者 async和await 获取结果了。
  • 这样,就可以知道Promise如何使用了。

代码如下:

  1. // 这里封装 query、add、del、update方法,分别查询JSON数据、添加、修改、删除数据
  2. const { join } = require('path')
  3. const { readFile, writeFile } = require('then-fs')
  4. // 拼接books.json的绝对路径
  5. const filename = join(__dirname, 'books.json')
  6. async function query() {
  7. try {
  8. // let 结果 = await Promise对象
  9. let res = await readFile(filename, 'utf-8')
  10. let arr = JSON.parse(res)
  11. return arr // 相当于返回了 new Promise((resolve, reject) => { resolve(arr) })
  12. } catch (err) {
  13. // return err // 相当于返回了 new Promise((resolve, reject) => { resolve(err) })
  14. return Promise.reject(err) // 相当于 reject(err)
  15. }
  16. }
  17. async function add(obj) {
  18. try {
  19. // 1. 先获取全部的图书,得到数组
  20. let arr = await query() // query()返回Promise对象
  21. // console.log(arr)
  22. // 2. 把新书加进去
  23. obj.id = Number(arr[arr.length - 1].id) + 1
  24. arr.push(obj)
  25. // 3. 把全部的图书重新存起来
  26. return writeFile(filename, JSON.stringify(arr))
  27. } catch (err) {
  28. return Promise.reject(err)
  29. }
  30. }
  31. async function del(id) {
  32. try {
  33. // 1. 先获取全部的图书
  34. let arr = await query()
  35. // 2. 根据id筛选,把一本书删除掉
  36. let result = arr.filter(item => item.id != id)
  37. // 3. 把剩余的图书存起来
  38. return writeFile(filename, JSON.stringify(result))
  39. } catch (err) {
  40. return Promise.reject(err)
  41. }
  42. }
  43. async function update(obj) {
  44. try {
  45. // 1. 获取全部图书
  46. let arr = await query()
  47. // 2. 修改一本书
  48. let index = arr.findIndex(item => item.id == obj.id)
  49. arr.splice(index, 1, obj)
  50. // 3. 把全部图书重新存起来
  51. return writeFile(filename, JSON.stringify(arr))
  52. } catch (err) {
  53. return Promise.reject(err)
  54. }
  55. }
  56. module.exports = { query, add, del, update }

获取图书接口

  • 接口名称:/api/getbooks
  • 请求方式:GET
  • 请求参数:无
  • 响应数据:{ status: 0, message: '获取图书成功', data: [ { 一本书 }, { 一本书 } ] }

基本代码如下:

  1. const { query, add, del, update } = require('./json-parser'); // 导入 处理JSON文件的模块
  2. app.get('/api/getbooks', async (req, res) => {
  3. let data = await query();
  4. res.send({ status: 0, message: '获取图书成功', data });
  5. })

接下来,可以使用客户端发送请求检测了。
04-写接口(砍掉) - 图11

添加图书接口

  • 接口名称:/api/addbook
  • 请求方式:POST
  • 请求体:bookname (书名)、author(作者)、publisher(出版社)
  • Content-Type: application/x-www-form-urlencoded
  • 响应数据:{ status: 201, message: '添加图书成功' }

基本代码如下:

  1. // https://www.expressjs.com.cn/4x/api.html#req.body
  2. app.use(express.json()) // 解析JSON格式的请求体
  3. app.use(express.urlencoded({ extended: true })) // 解析查询字符串格式的请求体
  4. // 上述两行代码,作用是接收客户端提交的请求体。并且把接收到的请求体赋值给了 req.body
  5. app.post('/api/addbook', async (req, res) => {
  6. let row = req.body;
  7. let r = await add(row);
  8. if (r === undefined) {
  9. res.send({ status: 0, message: '添加成功' })
  10. }
  11. })

接下来,可以使用客户端发送请求检测了。
04-写接口(砍掉) - 图12

修改图书接口

  • 接口名称:/api/updatebook
  • 请求方式:PUT
  • 请求体:id(ID)、bookname (书名)、author(作者)、publisher(出版社)
  • Content-Type: application/x-www-form-urlencoded
  • 响应数据:{ status: 0, message: '修改图书成功' }

所以:

  1. app.put('/api/updatebook', async (req, res) => {
  2. let row = req.body;
  3. let r = await update(row);
  4. if (r === undefined) {
  5. res.send({ status: 0, message: '修改成功' })
  6. }
  7. })

接下来,可以使用客户端发送请求检测了。
04-写接口(砍掉) - 图13

删除图书接口

  • 接口名称:/api/delbook
  • 请求方式:DELETE
  • 请求参数:id(ID)
  • 响应数据:{ status: 0, message: '删除图书成功' }

所以:

  1. app.delete('/api/delbook', async (req, res) => {
  2. let id = req.query.id;
  3. await del(id)
  4. res.send({ status: 0, message: '删除图书成功' })
  5. });

接下来,可以使用客户端发送请求检测了。
04-写接口(砍掉) - 图14

修改一下json-parser中的代码

  • 添加,计算id的时候

obj.id = arr.length === 0 ? 1 : Number(arr[arr.length - 1].id) + 1

  • 修改和删除的时候,和id比较的时候,不要用全等,因为客户端提交的数据可能是字符串的id

    使用前端代码测试接口

    找到以前做过的图书管理案例,修改请求的根路径为 “http://localhost:3000”,表示使用我们自己的接口了。
    前端发送Ajax请求,请求接口。会有跨域问题。
    这里可以使用CORS方案解决跨域。

    后端

  • 安装 cors 模块。npm i cors

  • 代码中,在所有接口之前,加载cors,并注册为中间件。
    let cors = require('cors')
    app.use(cors())

    前端

    正常发送请求即可。
    1. <script src="./axios.js"></script>
    2. <script>
    3. axios.get('http://localhost:3000/api/getbooks').then(result => {
    4. console.log(result);
    5. });
    6. </script>