HTTP协议

curl-v

  • $ curl -s -v 网址
    • -s 是silent,用于隐藏进度条
    • -v 是verbose,用于打印全部header
      • 开头的是注释
    • 开头的是HTTP请求

    • < 开头的是HTTP响应
  • example
    • $ curl -s -v http://xiedaimala.com=>得到301和Location,于是重新请求(-L 自动重定向)

image.png

  • $ curl -s -o null -v https://xiedaimala.com
    • -o null是为了隐藏HTML文本,内容太多不方便展示
    • Linux或MAC 将null改为 /dev/null

      请求和响应

      请求

      POST/xxx HTTP/1.1 请求头
      Host:xiedaimala.com 请求行
      User-Agent:curl/7.61.1
      Accept:/
      回车
      {“username”:”gouson”} 请求体/消息体
  • 如果请求体的内容为JSON
  • 那么请求头就要有 Content-Type:application/json
  • MDN 常见MIME类型列表

    响应

    HTTP/1.1 301 Moved Permanently 状态行
    Content-Type:text/html 响应头
    Content-Length:193
    Location:https://xiedaimala.com/
    回车
    <!DOCTYPE html> 响应体/消息体



  • 如果响应体的内容为JSON

  • 那么响应头就要有 Content-Type:application/json

    HTTP的复杂性

  • HTTP复杂就复杂在很多请求头和响应头

  • 每个请求头或响应头各不相同

Web框架

功能

  • 更方便地处理HTTP请求与响应
  • 更方便地连接数据库、Redis
  • 更方便的路由
  • 其他:HTML模板

    理念

  • Web框架的主流思路都是MVC

  • Model处理数据相关逻辑
  • View处理视图相关逻辑,前后分离之后,View不重要
  • Controller负责其他逻辑

image.png

处理HTTP请求和响应

最简单的封装

  • 将请求封装为 [['get','/xxx'],{请求头},'请求体']
  • 将响应封装为 [status,{响应头},'响应体']

    Node.js的封装

  • 封装在http模块中

  • 使用request(IncomingMessage的实例)读取请求
  • 使用response(ServerResponse的实例)设置响应

    Express的封装

  • 封装级别高一点点,只需理解Express的编程模型即可

  • Express

Express-demo-1

初始化

$ yarn add express

  • app.js ```javascript const express = require(‘express’) const app = express() const port = 3000

app.get(‘/‘, (req, res) => { res.send(‘Hello World!’) })

app.listen(port=3000, () => { console.log(Example app listening at http://localhost:${port}) })

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626058971489-9f5b92a1-8e9f-4263-97c8-278e11259b54.png#clientId=uee42a711-37ec-4&from=paste&height=91&id=u2b11d3a0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=64&originWidth=541&originalType=binary&ratio=1&size=7206&status=done&style=none&taskId=u5add1782-bb89-4c03-8943-ca1f31fa694&width=765.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626059086098-3e8edcb1-2f04-496d-bc42-7c847afaa8c7.png#clientId=uee42a711-37ec-4&from=paste&height=209&id=u0e2ae243&margin=%5Bobject%20Object%5D&name=image.png&originHeight=187&originWidth=525&originalType=binary&ratio=1&size=6281&status=done&style=none&taskId=u27c08539-24e5-452f-8677-7ee613b95cc&width=586.5)
  2. <a name="eHgx8"></a>
  3. ## 使用TypeScript
  4. 安装全局工具<br />`$ yarn global add typescript ts-node ts-node-dev`<br />安装类型支持<br />`$ yarn add @types/express`
  5. 创建app2.ts,内容和app.js一样即可
  6. 初始化ts<br />`$ tsc --init`<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626062415969-30cb5060-4473-436e-8bf3-af13125011ce.png#clientId=uee42a711-37ec-4&from=paste&height=60&id=u63e8afa1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=59&originWidth=618&originalType=binary&ratio=1&size=8509&status=done&style=none&taskId=u0d12ba89-bc17-4acb-b7a4-480ddda05d1&width=627)<br />打开tsconfig.json
  7. - es5改成es2016(也就是es6)
  8. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626062532705-db58e105-ff26-4188-9999-12535082f509.png#clientId=uee42a711-37ec-4&from=paste&height=197&id=u6abb0e23&margin=%5Bobject%20Object%5D&name=image.png&originHeight=362&originWidth=751&originalType=binary&ratio=1&size=43287&status=done&style=none&taskId=ud4de88a5-bd04-4813-ad2e-01ad469bcd6&width=408.5)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626062553598-075ca32f-4c87-4383-99da-c97e25b5cfda.png#clientId=uee42a711-37ec-4&from=paste&height=66&id=u4f0966fd&margin=%5Bobject%20Object%5D&name=image.png&originHeight=90&originWidth=560&originalType=binary&ratio=1&size=9769&status=done&style=none&taskId=ub0714a36-dedc-4017-a57a-8a34e07bf14&width=411)
  9. - 没有隐式any打开
  10. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626062634780-786cd010-bbe6-4486-9111-f4361422a826.png#clientId=uee42a711-37ec-4&from=paste&height=76&id=u73747c03&margin=%5Bobject%20Object%5D&name=image.png&originHeight=152&originWidth=1048&originalType=binary&ratio=1&size=34520&status=done&style=none&taskId=uc8c332e1-200b-480d-9e7f-62ce056b7fa&width=524)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626062699732-595c5251-2267-462c-a473-9aac7be570a3.png#clientId=uee42a711-37ec-4&from=paste&height=48&id=ua93a36f7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=91&originWidth=993&originalType=binary&ratio=1&size=19583&status=done&style=none&taskId=u6b66f1c8-a024-4324-8a0a-ffb23403669&width=520.5)
  11. - require改为import
  12. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/734550/1626062994268-2ecec67c-8d4f-4f56-a680-f973d662dc6a.png#clientId=uee42a711-37ec-4&from=paste&height=25&id=u66db5923&margin=%5Bobject%20Object%5D&name=image.png&originHeight=49&originWidth=430&originalType=binary&ratio=1&size=6616&status=done&style=none&taskId=u2ac2f1b1-f4db-4f00-b9cf-4343a47c67d&width=215)
  13. ```typescript
  14. import express from 'express'
  15. const app = express()
  16. const port = 3000
  17. app.get('/', (req, res) => {
  18. console.log(req)
  19. res.send('你好!')
  20. })
  21. app.listen(port, () => {
  22. console.log(`Example app listening at http://localhost:${port}`)
  23. })
  • 运行

$ ts-node-dev app2.ts
image.png

app对象的类型

类型为Express接口
image.png
Express extends Application
image.png
Applicetion extends EventEmitter,Irouter……
image.png
其中Irouter包含了get/post/put等方法
image.png

Express脚手架

express-generator

安装
$ yarn global add express-generator
使用
$ express -h 查看帮助
$ express --view=ejs . 创建文件,点表示当前目录。会覆盖文件,所以重新安装@types/express
$ yarn start
image.png
express-demo-2

改为TypeScript

  • 把app.js复制到app.ts
  • $ yarn add @types/node--dev 这样才能使用
  • 用import代替require
  • 用const代替var
  • 需RequestHandler和ErrorRequestHandler断言

image.png
image.png

  • 将bin/www中的入口改为app.ts

image.png

  • 添加start:ts脚本,将node改为ts-node

image.png

app.use与Express编程模型

express-demo-1代码

app.use()

  • 尝试使用req.url和res.send

image.png
image.png

  • 多次使用会报错

image.png
image.png

  • 改成res.write,就像流一样,记得加上 end

image.png
image.png
image.png

  • 什么时候可以省略next

最后一个use中才可以省略,否则没有next就不会往下一个next走了

express编程模型

image.png

中间件

fn就是中间件,因为它是被插入到启动和结束中间的物件

优点

  • 模块化
    • 这种模型使得每个功能都能通过一个函数实现
    • 然后通过app.use将这个函数整合起来
    • 如果把函数防盗文件或npm里,就实现了模块化
  • 以express-demo-1为例

    • app.use(logger('dev'));
    • logger('dev') 会返回一个函数
    • 这个函数会在每次请求到达的时候,打印出信息

      路由

  • 使用app.use实现路由

    1. app.use((req, res, next) => {
    2. if (req.path === '/xxx' && req.method === 'get') {
    3. res.write('home');
    4. }
    5. next();
    6. });

    image.png

  • 语法糖

    app.use(‘/xxx’, fn)
    app.get(‘/xxx’, fn)
    app.post(‘/xxx’, fn)
    app.route(‘/xxx’).all(f1).get(f2).post(f3)

    1. app.use('/aaa', (request, response, next) => {
    2. response.send('aaa')
    3. next()
    4. })
    5. app.get('xxx', (request, response, next) => {
    6. response.send('get xxx')
    7. next()
    8. })
    9. app.post('yyy', (request, response, next) => {
    10. response.send('post yyy')
    11. next()
    12. })
    13. app.route('/zzz')
    14. .all((request, response, next) => {
    15. response.send('all zzz')
    16. next()
    17. })
    18. .get((request, response, next) => {
    19. response.send('get zzz')
    20. next()
    21. })
    22. .post((request, response, next) => {
    23. response.send('post zzz')
    24. next()
    25. })

    错误处理

    image.png
    image.png

  • next参数(error)会直接进入errorHandler

image.png

多个错误处理

image.png
image.png

next(‘route’)

  1. app.get('/a_route_behind_paywall',
  2. function checkIfPaidSubscriber (req, res, next) {
  3. if (!req.user.hasPaid) {
  4. // continue handling this request
  5. next('route')//这里就会跳过下面的函数
  6. } else {
  7. next()
  8. }
  9. }, function getPaidContent (req, res, next) {
  10. PaidContent.find(function (err, doc) {
  11. if (err) return next(err)
  12. res.json(doc)
  13. })
  14. })

lib/router/route.js
image.png