node.js的学习

nodejs

  1. 官网 Node.js (nodejs.org)

    nodejs中代码由V8引擎解析,内置fs,http等api

    不包含dom和bom,不可以在nodejs中使用bom和dom

  2. nodejs可以做什么

    1. 基于Express框架,可以快速构架web应用
    2. 基于Electron框架,构建跨平台的桌面应用
    3. 基于restify框架,构架api接口项目
    4. 读写操作数据库…

环境安装

  1. 官网下载,双击安装(lts版)

    lts为长期稳定版

    current为新特性尝鲜版(测试版)

  2. Node.js 安装配置 | 菜鸟教程 (runoob.com)

    配置的时候如果更改了安装路径,需要配置一下环境变量

  3. 运行nodejs,在终端中输入 node demo.js

  4. 遇到文件命比较长时,输入文件开头,再敲tab键可以自动补充

  5. cls可以清空终端,ecs可以清除当前命令

内置模块

Index | Node.js v17.9.1 Documentation (nodejs.org)官方文档

Node.js 教程 | 菜鸟教程 (runoob.com)菜鸟文档

1.fs模块

  1. fs.readFile()方法,用来读取指定文件内容

    fs.readFile(path[, options], callback)

    path:必选参数,字符串,表示文件路径

    option:可选参数,表示什么编码格式来读取文件

    callback:必选参数,文件读取完成后,通过回调函数拿到结果

    var fs = require("fs");
    fs.readFile('study.md','utf8',function(err,data){
        // console.log(err);
        console.log(data);
        if(err){
            return console.log('读取文件失败'+err.message);
        }
        else{
            console.log('文件读取成功')
        }
    })
    
  2. fs.writeFilr()方法,向指定文件写内容(写入时没有指定文件会自动创建文件,但不会创建文件夹,默认会覆盖之前文件的数据)

    fs.writeFile(file, data[, options], callback)

    file:必选参数,需要指定文件路径字符串,表示文件存放路径

    data:必选参数,表示要写入内容

    option:可选参数,默认utf8

    callback:必选参数,回调函数

    var fs = require("fs");
    fs.writeFile('2.txt','abcd',function(err){
        //err为空时,就表示没有错误
        if(err){
            console.log('失败'+err.message);
        }
        else{
            console.log('成功');
        }
    })
    

    fs模块路径拼接问题

    会根据当前路径拼接成path

    __dirname:表示当前文件所处的目录,可以解决路径拼接问题

    path路径模块

    使用方法

    const path = require('path')
    path.jion([多个路径片段])
    //用于拼接多个路径片段
    path.jion('/a','/b','.../')
    //../会抵消前面的一层路径
    path.jion(__dirnaeme,'/a/1.txt')
    
    const fpath = '/a/b/1.txt'
    var filename = path.basename(fpath)
    //获取文件命(包含扩展命)
    var name = path.basename(fpath,'.txt')
    //去除扩展名
    
    //获取扩展名
    const fpath = '/a/b/1.txt'
    var fext = path.extname(fpath)
    
    //案例
    //拆分html中的style部分
    const fs = require('fs')
    // const path = require('path')
    // 正则表达式
    // \s:匹配空白字符
    // \S:匹配非空白字符
    const restyle = /<style>[\s\S]*<\/style>/
    const rescript = /<script>[\s\S]*<\/script>/
    
    const html = fs.readFile('index.html','utf-8',function(err,data){
        if(err){
            return console.log('读取失败'+err.message);
        }
        // console.log(data)
        resolveCss(data)
    })
    
    // 处理css函数
    function resolveCss(htmlStr){
        const r1 = restyle.exec(htmlStr)
        const newcss = r1[0].replace('<style>','').replace('</style>','')
        fs.writeFile('index.css',newcss,function(err){
            if(err){
                return console.log('写入失败'+err.message);
            }
            console.log('写入成功')
        })
    }
    

2.http模块

  1. 费资源的电脑叫客户端,负责对外提供网络资源的电脑叫服务器(简单理解)

    普通电脑安装web服务器软件,例如apache等,可以将电脑变成服务器

  2. 调用

    const http = require('http')
    

    在nodejs中不用安装web服务器,利用http模块

  3. ip地址:每台计算机的唯一地址,具有唯一性

    互联网中每台web服务器都有自己的ip地址

    ping www.baidu.com
    //可以查看百度的ip地址
    127.0.0.1
    //自己的IP地址
    

    域名服务器(DNS)用于将域名解析为ip地址

    端口号:在同一台电脑中,会有很多服务,端口号用来标识各个服务,同一个端口不可以被多个web服务占用,url中只有80端口可以省略

  4. 创建web服务器基本步骤

    // 导入模块
    const http = require('http')
    //创建web服务器实例
    const serve = http.createServer()
    // 为服务器绑定事件
    // 当有服务请求的时候,就会触发request事件
    serve.on('request',(req,res)=>{
        // 触发request后,会执行回调函数
        //获取url和访问方式
        console.log(req.url);
        console.log(req.method);
        console.log('request');
        const str = req.url+':'+req.method+'你好啊'
        // 调用res.end(),响应一些数据
        //设置编码,解决中文乱码
        res.setHeader('Content-Type','text/html;charset=utf-8')
        res.end(str)
        
    })
    // 启动服务器
    serve.listen(80,()=>{
        // 启动成功之后会调用回调函数
        console.log('start');
    })
    
    1. req请求对象

      只要服务器接收到了客户端的请求,就会调用server.on()中为服务器绑定的request事件处理函数,也就是其中的回调函数

      req为请求对象,可以拿到请求端的数据

    2. res响应对象

      访问服务器相关的数据和属性

    3. 解决中文乱码问题

      需要手动设置编码格式

模块化概念

1. 模块化基本概念

自顶向下把系统分成若干个模块的过程,模块是可组合,分解和更换的单元

生活中的例子,如小霸王游戏机,可分为卡带,手柄,主体三部分,三部分都是可以替换组合的

编程领域中的模块化,遵守固定的规则,把一个大文件拆分成独立并且相互依赖的多个小模块

2. 模块化规范

  1. nodejs中模块分类

    1. 内置模块:nodejs自带的模块。如http,fs
    2. 自定义模块:用户创建的,每个js文件都是自定义模块
    3. 第三方模块:使用前需要先下载
  2. 加载模块

    使用require()来加载模块

    //内置模块
    const http = require('http')
    //加载用户自定义模块,需要写路径
    const http = require('./http.js')
    //加载第三方模块,需要先下载
    const http = require('demohttp')
    

    注意:使用require()加载模块时,会执行加载模块中的代码

  3. 模块作用域(模块级别的访问限制)

    在自定义模块中定义的方法,变量等成员,只能在当前模块被访问

    防止全局变量污染的问题

  4. 如何向外共享模块中的成员(变量,方法等)

    每个.js自定义模块中,都有一个module对象

    //在任意js文件中都有这个对象
    console.log(module)
    
Module {
  id: '.',
  path: 'D:\\0.vue\\0.nodejs',
  exports: {},
  filename: 'D:\\0.vue\\0.nodejs\\4.module测试.js',   
  loaded: false,
  children: [
    Module {
      id: 'D:\\0.vue\\0.nodejs\\test.js',
      path: 'D:\\0.vue\\0.nodejs',
      exports: {},
      filename: 'D:\\0.vue\\0.nodejs\\test.js',       
      loaded: true,
      children: [],
      paths: [Array]
    }
  ],
  paths: [
    'D:\\0.vue\\0.nodejs\\node_modules',
    'D:\\0.vue\\node_modules',
    'D:\\node_modules'
  ]
}
  1. module.exports对象

    在自定义模块中,可以使用module.exports对象,将模块内成员共享出去

    外界用require()方法导入的就是module.exports对象

    该对象默认为空

    test.js

    //共享成员
    module.exports.name='zhangsan'
    module.exports.sayHello = function(){
        console.log('你好啊');
    }
    
    或者
    module.exports = {
        一个json对象
    }
    

    执行代码

    const test = require('./test.js')
    console.log(test);
    test.sayHello()
    

    注:导入的对象以module.exports对象为准

  2. exports对象

    nodejs提供exports对象,与module.exports指向同一个对象,但最终结果还是以module.exports指向的对象为准

//共享成员
exports.name='zhangsan'
exports.sayHello = function(){
    console.log('你好啊');
}
  1. exports和module.exports的使用误区

    ####require()得到的一直都是module.exports所指对象

    对象赋值给module.exports时,会开辟新的空间,增加属性则不会

    对象的赋值都会开辟新的空间?

###3. Commonjs规范

​ nodejs遵循了commonjs模块化规范

​ commonjs规定

1. 每个模块内部。moudule变量代表当前模块
2. modoule变量是一个对象,该对象的exports(module.exports)属性是对外的接口
  1. 加载模块,使用require()

4. npm与包

​ npm是包管理器npm (npmjs.com)

​ 网站用于搜索包,下包在另外一个服务器地址

​ 包是基于内置模块封装出来的,可以极大提高开发效率

  1. 如何下载包

    下包管理工具,在安装nodejs时会同时安装此工具

    npm -v 
    查看当前npm版本
    npm install 包名
    简写
    npm i 包名
    
    npm install --save mybatis-mapper
    
  2. 初次装包后多了一些文件

    node_modules:用来存放包

    package-lock.json:记录下载信息

    npm i 包名

    会安装最新的版本

    npm i moment@2.22.2

    安装指定的包版本(不用先卸载旧版本,会自动替换)

  3. 快速创建package.json

    npm init -y
    项目目录不能有中文
    

    npm i

    会根据package.json的内容,一次安装所有的包

    npm uninstall 包名

    卸载包

    devDependencies节点

    如果有些包只在项目开发的时候用到,上线之后不会用到
    npm i 包名 -D
    
    如果开发和上线都需要用到就正常安装
    
  4. 解决下包速度慢

    查看当前下载源
    npm config get registry
    
    使用淘宝服务器下载
    npm config set registry=https://registry.npm.taobao.org/
    
  5. nrm 方便切换下载源

    npm i nrm -g

    -g为全局安装

    nrm ls
    查看所有可用下载源
    nrm use taobao
    使用淘宝下载源
    
  6. 包的分类

    项目包

    ​ 开发依赖包(包只在项目开发的时候用到,上线之后不会用到)

    npm i 包名 -D

    ​ 核心依赖包(开发和上线都需要用到)

    npm i 包名

    全局包

    ​ 提供-g参数,会把包安装为全局包

    npm i 包名 -g

    npm uninstall 包名 -g 卸载全局包

    ​ 全局包默认会被安装到(打开隐藏文件)

    C:\User\用户\AppData\Roaming\npm\node_modules

    ​ 一般而言,只有工具包才有全局安装必要,如nrm

    ​ 也可以参考官网,看是否建议全局安装

    npm install -g i5ting_toc:把.文档装换成html页面的小工具

    使用

    i5ting_toc -f .\study.md
    
  7. 规范包的结构

    1. 包必须以单独的目录存在

    2. 顶级目录下需要有package.json包管理配置文件

    3. package.json必须包含name, version, main这三个属性

      分别为名字,版本号,包的入口

  8. 发布自己的包

    1. 新建文件夹作为包的根目录,(命名为包名)

    2. 在文件夹中,新建如下三个文件

      · package.json (包管理配置文件)

      · index.js (包的入口文件)

      · README.md (包的说明文档)

    3. 初始化package.js

      name为包的名字,命名前需在npm官网上面查询一下,命名不允许重复

      mian用来指定导包的入口

      {
          "name": "mypackage",
          "version": "1.0.0",
          "main": "index.js",
          "description": "提供了什么什么功能",
          "keywords": ["关键字","第二个关键字"],
          //开源许可协议
          "license": "ISC"
      }
      
    4. index.js (编写实现功能代码)

      // 定义格式化时间函数
      function dataFormat(dataStr){
          const dt = new Date(dataStr)
          const y = padZero(dt.getFullYear())
          const m = padZero(dt.getMonth() +1)
          const d = padZero(dt.getDate())
          
          return `${y}-${m}-${d}`
      }
      
      function padZero(n){
          return n > 9 ? n : '0'+ n
      }
      
      module.exports= {
          dataFormat
      }
      

      使用

       const dt = require('./mypackage/index')
       console.log(dt)
       const dt1 = dt.dataFormat(new Date())
       console.log(dt1)
      
    5. 如果index.js中的功能很多,可以进行模块化拆分

      1. 提取某个函数,拆分到src(新建目录)下的功能名.js文件当中

      2. 在index.js中,导入各个模块(就是src下的各个功能名.js文件)

      3. 在index.js中,使用module.exports把对应的方法暴露出去

    6. 发布自己的包

      在运行npm login之前,必须将下包的服务器切换为官方服务器

      nrm ls
      nrm use npm
      
      1. 注册账号,在官网注册

      2. 登陆npm账号

        在终端输入npm login命令

        依次输入用户名、密码、邮箱

      3. 登陆成功之后,切换到包的根目录

        运行npm publish命令,就可以发布包(包名不能有已经存在的)

      4. 删除已发布的包

        npm unpublish 包名 --force

        该命令只能删除72小时以内的包,72小时之后将永远不能删除

        删除的包,24小时内不准重复发布

      5. 尽量不要往npm中发布没有意义的包

5. 模块加载机制

09.模块的加载机制_哔哩哔哩_bilibili

  1. 模块优先从缓存中加载

    所有的模块在第一次加载后,会被缓存,使用的时候优先从缓存中加载

  2. 内置模块加载优先级最高,第三方模块与内置模块相同时,以内置模块为准

  3. 自定义模块(本地自己写的)需要以./或者../开头,不然会被当成内置模块或者第三方模块

  4. 使用require()导入自定义模块时,如果省略扩展名

    会按照如下顺序寻找文件

    1. 确切文件
    2. 补全.js
    3. 补全.json
    4. 补全.node
  5. 第三方模块加载机制

    调用require()时

    会从当前目录查找node_modules文件夹,到里面查找包

    如果没有node_modules文件夹,就会往上退一级目录,继续找

    直到为根目录为止

Express

1. 初步认识Express

Express - 基于 Node.js 平台的 web 应用开发框架 - Express 中文文档 | Express 中文网 (expressjs.com.cn)

​ 基于nodejs平台,快速,开发,极简的web开发框架

​ 类似于nodejs中的http模块,专门用来创建web服务器的

​ 本质上就是一个npm的第三方包

​ 基于http模块封装出来的

2. 基本使用

  1. 安装

    npm i express@4.17.1
    
    1. 创建最基本的服务器
    // 导入包
    const express = require('express')
    // 创建web服务器
    const app = expres()
    // 调用app.listen(端口号,启动成功后的回调函数)
    app.listen(80,()=>{
        console.log('express server running');
    })
    
    1. 监听get请求
    // 导入包
    const express = require('express')
    // 创建web服务器
    const app = express()
    // 调用app.listen(端口号,启动成功后的回调函数)
    app.listen(80,()=>{
        console.log('express server running');
    })
    // get第一个参数为url
    // 第二个参数:请求对应的函数
    //     req:请求对象,包含请求的属性和方法
    //     res:响应对象,包含响应的属性和方法
    app.get('/', (req, res) => {
    // app.send()向客户端发送响应数据
      res.send('Hello World!')
    })
    
    

    post请求:

    get请求相同
    // post第一个参数为url
    // 第二个参数:请求对应的函数
    //     req:请求对象,包含请求的属性和方法
    //     res:响应对象,包含响应的属性和方法
    app.post('/', (req, res) => {
    // app.send()向客户端发送响应数据
      res.send('post请求成功')
    })
    
    1. 获取url中携带的查询参数

    通过req.query对象,可以访问到客户端通过查询字符串的形式发送到服务器的参数(默认为空对象)

    app.get('/', (req, res) => {
    //   客户端使用?name=zs&age=20这种查询字符串的形式发送到服务器的参数
    // 可以通过req.query对象访问到
     console.log(req.query)
     res.send(req.query)
    })
    

    访问localhost/?name=zs&age=1

    获取url中的动态参数

    通过req.params对象,可以访问到url中,通过:匹配的动态参数

    //多个参数时url为
    //		/user/:id/:name
    app.get('/user/:id', (req, res) => {
        // req.params 默认是一个空对象
        // 里面存放动态匹配到的参数
         console.log(req.params)
         res.send(req.params)
    })
    

    访问localhost/user/2

    1. express.static()

    通过该函数,可以非常方便的创建一个静态资源服务器

    app.use(express.static('public'))
    //之后可以访问public目录下所有的文件了,不用加public前缀
    //如果需要加上public前缀
    

app.use(‘/public’,express.static(‘public’))
```

访问[demo.jpg (1196×1982)](http://localhost/demo.jpg)

url不用包含public

托管多个静态资源目录,多次调用`app.use(express.static('public'))`就行

3. nodemon的使用

nodemon会自动帮我们重启项目,方便开发和调试

安装 npm i -g nodemon

使用nodemon

之前是使用 
node demo.js
来运行代码,需要手动重启
使用
nodemon demo.js
会自动监听代码改动
保存之后会自动重启

4. Express中路由的学习

Express中的路由指的是客户处理请求服务器处理函数之间的关系

Express中的路由分三部分组成,请求类型请求url地址处理函数

格式如下:

app.METHOD(PATH,HANDLER)

定义路由(挂载路由)

app.get('/', (req, res) => {
// app.send()向客户端发送响应数据
  res.send('Hello World!')
})
  1. 路由的匹配过程

    按照代码中定义的先后顺序

    并且需要请求类型请求url地址都匹配

  2. 路由的模块化

    不建议将路由直接挂载到app上

    将路由分离为单独模块的步骤

    1. 创建路由模块的.js文件
    2. 调用express.Router()函数创建路由对象
    3. 向路由对象上挂载具体路由
    4. 使用module.exports()向外共享路由对象
    5. 使用app.use()函数注册路由模块

创建router.js文件(作为路由模块)

const express = require('express')
// 调用express.Router()函数创建路由对象
const router = express.Router()
// 向路由对象上挂载具体路由
router.get('/user',(req,res)=>{
    res.send('get user')
})
router.get('/user2',(req,res)=>{
    res.send('get user2')
})
// 使用module.exports向外共享路由对象
module.exports = router

执行文件

const express = require('express')
//导入自定义的路由模块
const router = require('./router')
const app = express()
// 注册路由模块
app.use(router)

app.listen(80,()=>{
    console.log('server start')
})

注意:app.use()函数的作用,是用来注册全局中间件的

  1. 为路由模块添加前缀

    //之后要访问router中所有的路由,都需要加api前缀
    app.use('/api',router)
    

5. Express中间件

业务处理过程中的中间处理环节

都有输入和输出

例子:污水处理

一级处理 二级处理 三级处理

前一个的输出作为后一个的输入

处理污水的这三个环节,就可以叫作中间件

  1. 中间件调用流程

    浏览器发起一次请求之后,可能需要多个中间件介入处理

    在请求经过多个中间件处理完成之后

    最后通过路由来响应这次请求

当一个请求到达express服务器之后,可以连续调用多个中间件,对这次请求进行预处理

  1. Express中间件的格式

    本质是一个function处理函数

    app.get('/', (req, res,next) => {
        next()
    })
    

注意:中间件的形参列表中,必须包含next参数,而路由处理函数只包含req和res

  1. next()函数的作用

    实现多个中间件连续调用,把流转关系转交给下一个中间件,或者下一个路由

  2. 定义中间件函数

    const express = require('express')
    const app = express()
    //定义一个简单的中间件函数
    const mw = function(req,res,next){
        console.log('这是最简单的中间件')
        //把流转关系交给中间件或者路由,没有下一个中间件就交给路由
        next()
    }
    app.listen(80,()=>{
        console.log('server start')
    })
    
  3. 全局生效的中间件

    任何请求,到达服务器之后,都会触发的中间件,叫全局生效的中间件

    // 通过app.use(中间件函数),来注册一个全局生效的中间件
    //全局生效的中间件,发起任何请求都会被执行
    //接上面
    app.use(mw)
    
    app.get('/user',(req,res)=>{
        res.send('get user')
    })
    app.get('/user2',(req,res)=>{
        res.send('get user2')
    })
    

    多个中间件和路由之间共享同一份req和res,在上游添加的属性可以在下游被使用

    const express = require('express')
    const app = express()
    //定义一个的中间件函数
    // 需要获取每次响应时间
    const mw1 = function(req,res,next){
        
        next()
    }
    app.listen(80,()=>{
        console.log('server start')
    })
    app.use(mw)
    app.get('/user',(req,res)=>{
        res.send('get user'+req.startTime)
    })
    app.get('/user2',(req,res)=>{
        res.send('get user2'+req.startTime)
    })
    

    连续定义多个中间件(按照先后顺序执行)

    const express = require('express')
    
    const app = express()
    
    //定义第一个的中间件函数
    const mw1 = function(req,res,next){
        console.log('调用第一个中间件');
        next()
    }
    const mw2 = function(req,res,next){
        console.log('调用第二个中间件');
        next()
    }
    app.listen(80,()=>{
        console.log('server start')
    })
    
    app.use(mw1)
    app.use(mw2)
    app.get('/user',(req,res)=>{
        res.send('get user')
    })
    app.get('/user2',(req,res)=>{
        res.send('get user2')
    })
    

    局部生效的中间件

    const express = require('express')
    const app = express()
    //定义局部中间件
    const mw1 = function(req,res,next){
        console.log('调用一个中间件');
        next()
    }
    
    app.listen(80,()=>{
        console.log('server start')
    })
    // 创建路由
    // 让中间件只在第一个路由里面生效
    app.get('/user',mw1,(req,res)=>{
        //当请求地址为/user时,中间件生效
        res.send('get user')
    })
    app.get('/user2',(req,res)=>{
        //当请求地址为/user2时,中间件不会生效
        res.send('get user2')
    })
    

    定义多个中间件

    方式1:
    app.get('/user2',mw1,mw2,mw3,(req,res)=>{
        //当请求地址为/user2时,中间件不会生效
        res.send('get user2')
    })
    
    方式2:
    app.get('/user2',[mw1,mw2,mw3],(req,res)=>{
        //当请求地址为/user2时,中间件不会生效
        res.send('get user2')
    })
    

    中间件的五个注意事项

    1. 一定要在路由之前注册中间件
    2. 执行完中间件业务代码后,一定要调用next()函数
    3. 多个中间件之间,共享req和res

6. 中间件分类

  1. 应用级别中间件

    通过app.use()或者app.get()或者app.post()绑定到app实例身上的中间件

    1. 路由级别的中间件

    绑定到express.Router()实例上的中间件

    router.use(中间件)

    1. 错误级别的中间件

    专门捕获整个项目中发生的异常错误

    错误级别的中间件function处理函数中,必须有四个参数

//错误级别中间件可以用来捕获错误
app.use(function(err,req,res,next){
    console.log('发生了错误'+err.message)
    res.send('error'+err.message)
})

错误发生时,服务器会崩溃

const express = require('express')
const app = express()
app.listen(80,()=>{
    console.log('server start')
})
app.get('/user',(req,res)=>{
    // 人为制造错误
    throw new Error('服务器内部发生了错误')
    res.send('get user')
})
app.get('/user2',(req,res)=>{
    res.send('get user2')
})

捕获错误:(错误级别中间件要写在路由后面)

错误级别的中间件,必须注册在所有中间件之后

const express = require('express')

const app = express()

app.get('/user',(req,res)=>{
    // 人为制造错误
    throw new Error('服务器内部发生了错误')
    res.send('get user')
})

//定义错误级别中间件
app.use((err,req,res,next)=>{
    console.log('发生了错误'+err.message)
    res.send('error'+err.message)
})

app.listen(80,()=>{
    console.log('server start')
})
  1. Express内置的中间件

    1. express.static 快速托管静态资源的内置中间件,(无兼容性)

    2. express.json 解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)

      app.use(express.json())
      
    const express = require('express')
    
    const app = express()
    
    app.listen(80,()=>{
        console.log('server start')
    })
    // 配置josn解析中间件
    app.use(express.json())
    app.post('/user',(req,res)=>{
        // 在服务器,可以使用req.body这个属性,接收客户端发送的数据
        // 默认情况下,如果配置解析表单数据的中间件
        //body默认等于underfind
        console.log(req.body)
        res.send('get user')
    })
    
    1. express.urlencoded解析URL-encoded格式的请求数据(有兼容性,4.16.0+)
    app.use(express.urlencoded({extended: false}))
    
    const express = require('express')
    const app = express()
    app.listen(80,()=>{
        console.log('server start')
    })
    // 配置josn解析中间件
    app.use(express.json())
    //配置url-encoded解析中间件
    //解析url-encoded格式数据
    app.use(express.urlencoded({extended: false}))
    app.post('/user',(req,res)=>{
        // 在服务器,可以使用req.body这个属性,接收客户端发送的数据
        // 默认情况下,如果配置解析表单数据的中间件
        //req.body默认等于underfind
        console.log(req.body)
        res.send('get user')
    })
    app.post('/user2',(req,res)=>{
        // 在服务器,可以使用req.body这个属性,接收客户端发送的数据
        // 默认情况下,如果配置解析表单数据的中间件
        //body默认等于underfind
        console.log(req.body)
        res.send('get user')
    })
    
  2. 第三方中间件

    非官方内置的,由第三方开发出来的中间件

    举例: body-parser使用步骤如下

    运行 npm i body-parser 安装

    使用require导入

    app.use()调用

    注意:Express.urlencoded中间件,就是基于body-parser这个第三方中间件封装出来的

7. 案例,手动模拟express.urlencoded中间件

const express = require('express')
// 内置模块querystring,专门用来处理查询字符串
// 该模块的parse()函数可以将查询字符串解析成对象的格式
const qs = require('querystring')
const app = express()

app.listen(80,()=>{
    console.log('server start')
})
//解析中间件
app.use((req,res,next)=>{
    let str = ''
    // 监听req的data事件
    //只要有数据到达服务器,就会触发data事件
    req.on('data',(chunk)=>{
        str += chunk
    })
    // 监听req的end事件
    // 当请求体数据接收完毕之后,会自动触发req的end事件
    req.on('end',()=>{
        // 打印完整的请求体
        console.log(str)
        //解析请求体
        console.log(qs.parse(str))
        // 将解析出来的数据,挂载到自定义属性body里面去
        req.body=qs.parse(str)
        next()
    })
})
app.post('/user',(req,res)=>{
    console.log(req.body)
    res.send(req.body)
})

将自定义中间件封装为模块

// 内置模块querystring,专门用来处理查询字符串
// 该模块的parse()函数可以将查询字符串解析成对象的格式
const qs = require('querystring')
//解析中间件
const fun = (req,res,next)=>{
    let str = ''
    // 监听req的data事件
    //只要有数据到达服务器,就会触发data事件
    req.on('data',(chunk)=>{
        str += chunk
    })
    // 监听req的end事件
    // 当请求体数据接收完毕之后,会自动触发req的end事件
    req.on('end',()=>{
        // 打印完整的请求体
        console.log(str)
        //解析请求体
        console.log(qs.parse(str))
        // 将解析出来的数据,挂载到自定义属性body里面去
        req.body=qs.parse(str)
        next()
    })
}
module.exports = fun

测试

const fun = require('./模块化自定义函数.js')
const express = require('express')
const app = express()
app.listen(80,()=>{
    console.log('server start')
})
//自定义中间件函数,注册为全局中间件
app.use(fun)
app.post('/user',(req,res)=>{
    console.log(req.body)
    res.send(req.body)
})

8. 使用Express写接口

编写get接口

router.js

const express = require('express')
// 调用express.Router()函数创建路由对象
const router = express.Router()

router.get('/get',(req,res)=>{
    //获取查询字符串
    const query =req.query
    //响应结果
    res.send({
        statu:0,
        msg:'请求成功',
        data:query
    })
})
module.exports = router

test.js

const express = require('express')
const router = require('./router')
const app = express()
//配置解析表单中间件
app.use(express.urlencoded({extended: false}))
app.use('/api',router)
//启动服务器
app.listen(80,()=>{
    console.log('server start')
})

编写post接口

app.use(express.urlencoded({extended: false}))

使用post需要解析请求体时,要加入上面代码才能显示body

在router.js中添加

router.post('/post',(req,res)=>{
    //获取请求体url-encoded格式的数据
    const body =req.body
    //响应结果
    res.send({
        statu:0,
        msg:'请求成功',
        data:body
    })
})

9. 接口的跨域问题

协议,域名,端口号都需要相同才能进行访问

访问端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>

<body>
    <button id="get">get</button>
    <button id="post">post</button>
</body>
<script>
    $(function(){
        //测试get接口
        $('#get').on('click',function(){
            $.ajax({
                type:'GET',
                url:'http://localhost:80/api/get',
                data:{name:'zs',age:'20'},
                //成功的回调函数
                success:function(res){
                    console.log(res)
                }
            })
        })
        //测试post接口
        $('#post').on('click',function(){
            $.ajax({
                type:'POST',
                url:'http://localhost:80/api/post',
                data:{name:'zs',age:'20'},
                //成功的回调函数
                success:function(res){
                    console.log(res)
                }
            })
        })
    })
</script>
</html>

服务器端用上面写的接口

解决跨域问题的方案主要有两种:

1. CORS(主流的解决方案,推荐使用)
2. JSONP(只支持GET请求)

使用CORS中间件解决跨域问题

1.安装中间件
 npm i cors
2.使用require('cors')导入中间件
const cors= require('cors')
3.在路由之前调用中间件
app.use(cors())

只需要在上面服务端代码加上

const express = require('express')
const router = require('./apiRouter')
// 2.使用require('cors')导入中间件
const cors= require('cors')

const app = express()
app.use(express.urlencoded({extended: false}))
//解决跨域问题
app.use(cors())

app.use('/api',router)
//启动服务器
app.listen(80,()=>{
    console.log('server start')
})

10.CORS(三个请求头)

CORS跨域资源共享
什么是CORS
CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列HTTP响应头组成,这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源。
浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了CORS相关的HTTP响应头,就可以解除浏览器端的跨域访问限制。

CORS的注意事项
CORS主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了CORS的接口。

CORS在浏览器中有兼容性。只有支持XMLHttpRequest Level2的浏览器,才能正常访问开启了CORS的服务端接口(例如:IE10+、Chrome4+、FireFox3.5+)。

CORS响应头部:
CORS响应头部– Access-Control-Allow-Origin

响应头部中可以携带一个Access-Control-Allow-Origin字段,其语法如下:

Access-Control-Allow-origin: <origin> |*

其中,origin参数的值指定了允许访问该资源的外域URL。例如,下面的字段值将只允许来自http://itcast.cn的请求:

res.setHeader( 'Access-Control-Allow-Origin','htp://itcast.cn')

默认情况下,CORS仅支持客户端向服务器发送如下的9个请求头:
Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width ,Content-Type (值仅限于text/plain、multipart/form-data、application/x-www-form-urlencoded三者之一)如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access-Control-Allow-Headers 对额外的请求头进行声明,否则这次请求会失败!

//允许客户端额外向服务器发送Content-Type 请求头和X-Custom-Header请求头
//注意:多个请求头之间使用英文的逗号进行分割
res.setHeader( 'Access-Control-Allow-Headers ' ,'Content-Type,x-Custom-Header')

CORS响应头部–Access-Control-Allow-Methods
默认情况下,CORS仅支持客户端发起GET、POST、HEAD请求。
如果客户端希望通过PUT、DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Alow-Methods来指明实际请求所允许使用的HTTP方法。
示例代码如下:

//只允许POST、GET、DELETE、HEAD请求方法
res.setHeader ( ' Access-Control-Allow-Methods ''POST,GET,DELETE,HEAD')
//允许所有的 HTTP请求方法
res.setHeader( ' Access-Control-Allow-Methods ', '*')

11. CORS请求的分类

  1. 简单请求

    同时满足两个条件

    请求方式为:get,post,head三者之一

    并且头部信息不超过九个请求体,(就是无自定义头部字段)

  2. 预检请求

    在浏览器与服务器正式通信之前,浏览器会先发送OPTION请求进行预检,以获知服务器是否允许该实际请求,所以这一次的OPTION请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。

    不满足简单请求条件的,都是预检请求

    只要符合以下任何一个条件的请求,都需要进行预检请求:

    一、请求方式为GET、POST、HEAD之外的请求Method类型
    二、请求头中包含自定义头部字段
    三、向服务器发送了application/json格式的数据

  3. 简单请求和预检请求的区别

    简单请求的特点:客户端与服务器之间只会发生一次请求。
    预检请求的特点∶客户端与服务器之间会发生两次请求,OPTION预检请求成功之后,才会发起真正的请求。

    测试时,不能用chome浏览器

    //预检请求代码
    $(function(){
            //测试get接口
            $('#get').on('click',function(){
                $.ajax({
                    type:'GET',
                    url:'http://localhost:80/api/get',
                    data:{name:'zs',age:'20'},
                    //成功的回调函数
                    success:function(res){
                        console.log(res)
                    }
                })
            })
            //测试post接口
            $('#post').on('click',function(){
                $.ajax({
                    type:'POST',
                    url:'http://localhost:80/api/post',
                    data:{name:'zs',age:'20'},
                    //成功的回调函数
                    success:function(res){
                        console.log(res)
                    }
                })
            })
    
            //为删除按钮绑定点击事件处理函数
            $('#delete').on('click',function(){
                $.ajax({
                    type:'DELETE',
                    url:'http://localhost:80/api/delete',
                    //成功的回调函数
                    success:function(res){
                        console.log(res)
                    }
                })
            })
        })
    

12.jsonp接口

1.回顾JSONP的概念与特点
概念:浏览器端通过

​ JSONP仅支持GET请求,不支持POST、PUT、DELETE 等请求。

创建jsonp接口的注意事项:

如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须在配置CORS 中间件之前声明JSONP的接口。否则JSONP接口会被处理成开启了CORS的接口。示例代码如下:

//优先创建JSONP接口【这个接口不会被处理成CORS接口】
app.get('/api/jsonp',(req,res) =>{ })
//再配置 CORS中间件【后续的所有接口,都会被处理成CORS接口】
app.use(cors())
//这是一个开启了CORS的接口
app.get( '/api/get', (req,res) =>{ })

实现JSONP 接口的步骤

  1. 获取客户端发送过来的回调函数的名字

  2. 得到要通过JSONP形式发送给客户端的数据

  3. 根据前两步得到的数据,拼接出一个函数调用的字符串

  4. 把上一步拼接得到的字符串,响应给客户端的

在网页中使用jQuery发起JSONP请求

//调用ajax函数,提供JSONP的配置选项,从而发起JSONP请求,示例代码如下:
$('#btnJSONP').on('click',function(){
    $.ajax({
        method:'GET',
        url:'http://localhost/api/jsonp',
        dataType:'jsonp',
        success:function(res){
            console.log(res)
        }
    })
})

13.完整代码

apiRouter.js

const express = require('express')
// 调用express.Router()函数创建路由对象
const router = express.Router()

router.get('/get',(req,res)=>{
    //获取查询字符串
    const query =req.query
    //响应结果
    res.send({
        statu:0,
        msg:'请求成功',
        data:query
    })
})

router.post('/post',(req,res)=>{
    //获取请求体
    const body =req.body
    //响应结果
    res.send({
        statu:0,
        msg:'请求成功',
        data:body
    })
})

router.delete('/delete',(req,res)=>{
    res.send({
        status:0,
        message:'delete请求成功'
    })
})
module.exports = router

apiRun.js

const express = require('express')
const router = require('./apiRouter')
// 2.使用require('cors')导入中间件
const cors= require('cors')

const app = express()
app.use(express.urlencoded({extended: false}))
//优先创建JSONP接口【这个接口不会被处理成CORS接口】
app.get('/api/jsonp',(req,res) =>{ 
    //1.获取客户端发送过来的回调函数的名字
    const funcName = req.query.callback
//得到要通过JSONP形式发送给客户端的数据
    const data = {name: 'zs ', age: 22 }
//根据前两步得到的数据,拼接出一个函数调用的字符串
   const scriptstr =`${funcName}(${JSON.stringify(data)})`
//把上一步拼接得到的字符串,响应给客户端的<script>标签进行解析执行			
    res.send(scriptstr)
})
//再配置 CORS中间件【后续的所有接口,都会被处理成CORS接口】
app.use(cors())

app.use('/api',router)
//启动服务器
app.listen(80,()=>{
    console.log('server start')
})

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>

<body>
    <button id="get">get</button>
    <button id="post">post</button>
    <button id="delete">delete</button>
    <button id="jsonp">jsonp</button>
    
</body>
<script>
    $(function(){
        //测试get接口
        $('#get').on('click',function(){
            $.ajax({
                type:'GET',
                url:'http://localhost:80/api/get',
                data:{name:'zs',age:'20'},
                //成功的回调函数
                success:function(res){
                    console.log(res)
                }
            })
        })
        //测试post接口
        $('#post').on('click',function(){
            $.ajax({
                type:'POST',
                url:'http://localhost:80/api/post',
                data:{name:'zs',age:'20'},
                //成功的回调函数
                success:function(res){
                    console.log(res)
                }
            })
        })

        //为删除按钮绑定点击事件处理函数
        $('#delete').on('click',function(){
            $.ajax({
                type:'POST',
                url:'http://localhost:80/api/delete',
                //成功的回调函数
                success:function(res){
                    console.log(res)
                }
            })
        })

        //
        //调用ajax函数,提供JSONP的配置选项,从而发起JSONP请求,示例代码如下:
        $('#jsonp').on('click',function(){
            $.ajax({
                method:'GET',
                url:'http://localhost/api/jsonp',
                dataType:'jsonp',
                success:function(res){
                    console.log(res)
                }
            })
        })

    })
</script>

</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值