NodeJS—学习笔记

学习coderwhy的《深入Node.js》课程的一些小总结。

三、http模块

1.开启服务器的方式

  • http.createServer((req,res)=>{})
  • new http.Server((req,res)=>{})

Server通过listen方法来开启服务器

listen的参数

  • 端口port:可以不传,会默认分配
  • 主机host:默认0.0.0.0
    • localhost:是一个域名,会被解析成127.0.0.1
    • 127.0.0.1:回环地址,主机自己发出的包会被自己接受
      • 正常数据包经过 应用层-传输层-网络层-数据链路层-物理层
      • 回环地址在网络层直接就被捕获到了
      • 监听127.0.0.1时,不能够通过本地ip访问到服务器
    • 0.0.0.0:监听ipv4上所有地址,再根据端口找到不同应用,可以通过本地ip访问到服务器

2.request对象

  • req.url:能够获取请求的url 如 /login?username=why&password=123
    • 通过url内置模块(node内置)解析req.url
      • 解析成一个对象,里面包含req.url的信息
      • const { pathname,query } = url.parse(req.url)
    • 通过querystring内置模块(node内置)解析处理得到的query(username=why&password=123)
      • qs.parse(query) —> {username:why,password:123}
  • req.method:获取请求方式 如 GET、POST
    • GET:查询数据
    • POST:新建数据
    • PATCH:更新数据
    • DELETE:删除数据
  • req.headers
    • keep-alive:保持连接
      • http基于tcp,通常在一次请求和响应后会立刻中断
      • http1.0中想要保持连接需要在浏览器请求头和服务器响应头中添加connection:keep-alive
      • http1.1,默认connection:keep-alive,不同web服务器有不同保持时间,node中默认5s
    • accept-encoding:告知服务器,客户端支持的文件压缩格式,比如js文件可以使用gzip编码,对应.gz文件
      • 通过压缩可以提高文件传输的效率,可以通过webpack配置将大文件进行压缩优化项目
    • accept:浏览器可接收的文件类型
    • user-agent:包含客户端信息

如何获取到post请求body中的数据?
  • body中的数据是通过流的方式写入的,通过req.on(“data”,(data)=>{})监听data事件,获取data中的数据流(buffer)
  • 解码方式
    • data.toString() 默认utf-8
    • req.setEncoding(‘utf-8’)
      • 文本 utf-8
      • 照片 音频 视频 binary
  • 解码之后拿到JSON对象,通过JSON.parse(data)转为对象

3.response对象

  • 响应结果 res.end(“xxx”) 本质上 res.write(“xxx”) res.end()
  • 设置响应的header
    • res.setHeader(“Content-Type”, “text/plain;charset=utf8”)
    • res.writeHead(200, {
      “Content-Type”: “text/plain;charset=utf8”
      })

4.文件上传

方法一:用传统的方式,手动获取文件数据
  • 不能直接拿到从客户端发送过来的文件数据,然后进行写入,因为这些数据中不仅仅包含文件本身的数据,还包含与文件相关的信息数据,如果直接用未处理的文件数据那么就无法还原成原本的文件
  • 为了获取文件数据,如png,必须获取除文件信息数据和边界符之外的文件数据
  1. // 上传文件必须把编码改成 binary
  2. req.setEncoding("binary");
  3. let body = "";
  4. // 拿到文件边界符
  5. let boundary = req.headers["content-type"].split(";")[1];
  6. boundary = boundary.split("=")[1];
  7. req.on("data", (data) => {
  8. body += data;
  9. });
  10. req.on("end", () => {
  11. // 处理body
  12. // 1.获取image/png的位置
  13. const payload = qs.parse(body, "\r\n", ": ");
  14. const type = payload["Content-Type"];
  15. // 2.开始在image/png的位置进行截取
  16. const typeIndex = body.indexOf(type);
  17. const typeLength = type.length;
  18. let imgData = body.substring(typeIndex + typeLength);
  19. // 3.将中间两个空格去掉
  20. imgData = imgData.replace(/^\s\s*/, "");
  21. // 4.将最后的boundary去掉
  22. imgData = imgData.substring(0, body.indexOf(`--${boundary}--`));
  23. fs.writeFile("./foo.png", imgData, { encoding: "binary" }, (err) => {
  24. console.log("文件上传成功");
  25. res.end("文件上传成功");
  26. });

四、Web框架

1.Express框架

本质上是一系列中间件函数的调用

(1)express中间件

本质是传递给express的一个回调函数

  • 三个参数:
    • request
    • response
    • next函数(执行下一个中间件的函数)
  • 中间件可以用来干什么?
    • 执行任何代码
    • 更改request和response
    • 结束请求-响应周期(返回数据)
    • 调用下一个中间件
  • 如果当前中间件没有结束请求-响应周期,则必须调用next()将控制权传递给下一个中间件,否则请求将被挂起
  • 中间件可以连续注册,利用next()调用下一个中间件
  1. app.get('/login',(req,res,next)=>{next()},(req,res,next)=>{next()},(req,res,next)=>{})

(2)使用中间件来解析request body

  • 原生方法
  • 使用express内置中间件解析json、urlencoded、form-data数据
    • express.json() => 使用app.use(express.json())
    • express.urlencoded({extended:true})
      • true:对urlencoded进行解析时,用的是第三方库:qs
      • false:对urlencoded进行解析时,用的是Node内置模块:querystring
    • 通过第三方库 multer对form-data进行解析 ```javascript const multer = require(‘multer’) const upload = multer()

app.post(‘/login’,upload.any(),(req,res,next)=>{ console.log(req.body) // 文本信息会放在body中 }) // 解析所有文件,详细看文档

  1. <a name="ZTwNr"></a>
  2. #### (3)使用multer来上传文件
  3. - 不自定义文件信息
  4. ```javascript
  5. const multer = require('multer')
  6. // 当启动服务器时,会检查是否有该目录,没有的话就会默认创建
  7. const upload = multer({
  8. dest:'./uploads/' // 文件保存的位置
  9. })
  10. // 使用中间件来解析上传的文件
  11. app.post('/upload',upload.single(filename),(req,res,next)=>{
  12. console.log(req.file) // 文件信息会放在req.file、req.files中
  13. console.log(req.files)
  14. // 单个文件时,使用 upload.single(filename)
  15. // 多个文件时,使用 upload.array(fieldname[, maxCount])
  16. })
  • 通过storage自定义文件信息
  1. // 2.通过storage自定义文件信息
  2. const storage = multer.diskStorage({
  3. destination: function (req, file, cb) {
  4. cb(null, '/tmp/my-uploads')
  5. },
  6. filename: function (req, file, cb) {
  7. const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
  8. cb(null, file.fieldname + '-' + uniqueSuffix)
  9. }
  10. })
  11. const upload = multer({ storage: storage })

(4)使用morgan将请求的日志保存,用法看github文档

(5)获取get请求中url数据

  • URL的params
  1. URL:localhost:8000/login/03211/coder
  2. app.get("/login/:id/:name", (req, res, next) => {
  3. console.log(req.params);
  4. res.end("请求成功");
  5. });
  6. ==> { id: '03211', name: 'coder' }
  • URL的query
  1. URL:localhost:8000/login?username=coder&password=123
  2. app.get("/login", (req, res, next) => {
  3. console.log(req.query);
  4. res.end("请求成功");
  5. });
  6. ==> { username: 'coder', password: '123' }

(6)处理错误逻辑

  • next(err)中间件中可以传入参数,该参数包含错误信息(注意如果传入参数,那么会执行处理错误的中间件,而不会执行下一个中间件)
    • next(new Error(“error message”))
  • 通过app.use((err,req,res,next)=>{ 错误逻辑处理 })中间件做统一的错误处理
    • 当回调函数是四个参数时,会告知是错误处理的回调函数

(7)静态服务器

  • 通过app.use(express.static(path))来指定静态资源位置

(8)Express路由

  • 目的:防止app中代码逻辑过于复杂,使用路由进行抽取,更利于维护
  • 使用express.Router来创建一个路由处理程序
    • 一个Router实例拥有完整的中间件和路由系统
    • 它被称为迷你应用程序(mini-app)
  • 用法:
  1. const express = require('express')
  2. const userRouter = express.Router()
  3. userRouter.get('/',(req,res,next)=>{})
  4. userRouter.post('/',(req,res,next)=>{})
  5. userRouter.delete('/',(req,res,next)=>{})
  6. // 使用注册后的路由
  7. app.use('/users',userRouter)

2.Koa框架

koa是一个更加轻量级的框架,相对比express具有更强的异步处理能力

(1)Koa中间件

  • koa注册中间件提供两个参数
    • ctx:上下文(context)对象
      • ctx.request:获取请求对象
      • ctx.response:获取响应对象
    • next:本质上是dispatch,它返回一个promise
      • 当所有中间件都执行完毕后才执行then进行响应,这也就是为什么设置ctx.response.body后还能继续设置,且最终是以最后的body进行响应
      • 而express是执行完res.end()后就已经做出响应了
  • koa通过创建app对象,注册中间件只能通过use方法
    • 没有提供以下注册方式
    • methods方式:app.get()/.post
    • path方式:app.use(‘/home’,()=>{})
    • 连续注册方式:app.use((ctx,next)=>{},(ctx,next)=>{})

(2)Koa路由

  • Koa中并没有内置的路由,需要通过第三方库:koa-router来实现
    • 因为koa中间件没有methods的方式,但koa-router中实现了methods的方式
    • koa路由中也实现了path、连续注册的注册方式
    • 具体使用方式 ```javascript // user.js const Router = require(“koa-router”); const userRouter = new Router({ prefix: “/users” });

userRouter.get(“/“, (ctx, next) => { ctx.response.body = “user list”; });

userRouter.post(“/“, (ctx, next) => { ctx.response.body = “create user info”; });

module.exports = userRouter;

  1. ```javascript
  2. const Koa = require("koa"); // 导出是个类
  3. const app = new Koa();
  4. const userRouter = require("./router/user");
  5. // 注册路由
  6. app.use(userRouter.routes());
  7. // 判断methods是否支持
  8. app.use(userRouter.allowedMethods());
  9. app.listen(8000, () => {
  10. console.log("8000服务器已开启");
  11. });
  • allowedMethods用于判断某一个method是否支持
    • 如果不支持get、put、delete、patch会自动报错:Method Not Allowed,状态码:405
    • 如果不支持link、copy、lock会自动报错:Not Implemented,状态码:501

(3)参数解析

  • 通过路由解析params、query
    • ctx.request.params
    • ctx.request.query
  • 使用koa-bodyparser中间件(第三方库)解析JSON、urlencoded
    • 解析后的对象放到ctx.request.body中
  • 使用koa-multer中间件(第三方库)解析form-data格式
    • 注意:解析后的对象不是放到ctx.request.body中而是放到ctx.req.body中

(4)文件上传与express框架类似

  • 需要注意的是:文件信息放在ctx.req.file / ctx.req.files

(5)Koa响应内容的方式

  • ctx.response.body 与 ctx.body 都可以对客户端做出响应,那么有什么不同呢?
    • 观察源码发现 ctx.response.body对ctx.body做了一层代理,执行ctx.body时,实际上是执行ctx.response.body
    • 同样在ctx.request.xxx中对ctx.xxx也做了一层代理
  • 输出结果的类型
    • string、Buffer、Stream、Object||Array、null

(6)静态资源服务器

  • 通过koa-static第三方库来实现
    • app.use(static(‘./build’))

(7)处理错误的方式

  • 通过ctx.app.emit(‘error’,new Error(‘error message’),ctx)发出错误
  • 如果app.on(‘error’,(err,ctx)=>{})监听错误
  1. app.use((ctx, next) => {
  2. const isLogin = false;
  3. if (!isLogin) {
  4. ctx.app.emit("error", new Error("您还未登录"), ctx); // 发出错误
  5. }
  6. });
  7. // 监听错误
  8. app.on("error", (err, ctx) => {
  9. ctx.status = 401;
  10. ctx.body = err.message;
  11. });

3.Express与Koa的对比

  • 从架构上来说
    • express完整且强大,koa整洁且自由可自定义
    • express中间件是普通函数且直接执行,koa中间件其实是dispatch,返回的是promise
  • koa具有更强的异步处理能力(某个中间件包含异步操作)
    • 因为express中间件的回调是普通函数,所以如果要处理异步操作需要手动去写有关异步的逻辑
    • 而koa中间件的回调是Promise,可以通过 await next()的方式同步执行异步代码,等异步操作完成后再进行下一步操作

五、MySQL