Node.js使用Express创建Web服务器、编写接口

  • Post author:
  • Post category:其他





黑马程序员Node.js全套入门教程,nodejs最新教程含es6模块化+npm+express+webpack+promise等_Nodejs实战案例详解_哔哩哔哩_bilibili


本套课程作为框架前置课,重点为学生铺垫 npm、包、模块化、身份认证、CORS 跨域等主要知识点,为学习后面的 Vue 课程夯实基础。同时,本课程涵盖了 MySQL 数据库、API 接口项目开发等后端内容,拓宽了学生的知识面,为前端学生构建了前后端完整的知识体系,助力学生更好地发展。



https://www.bilibili.com/video/BV1a34y167AZ?p=56&spm_id_from=333.1007.top_right_bar_window_history.content.click



六. Express

Express是一个基于Node.js的Web开发框架,专门用来创建Web服务器,本质就是一个npm上的第三方包。

Express可以快速创建两种服务器:

  • Web网站服务器:对外提供Web网页资源
  • API接口服务器:对外提供API接口

1. 创建基本的Web服务器

# 在项目中下载Express包
npm i express@4.17.1
//1、导入express
const express=require('express');
//2、创建Web服务器
const app=express();
//3、启动Web服务器,调用app.listen(端口号,启动成功后的回调函数)
app.listen(80,()=>{
    console.log('express server running at http://127.0.0.1')
});

//监听GET请求
//参数1:客户端请求的URL地址
//参数2:请求对应的处理函数。
//      req:请求对象(包含了与请求相关的属性和方法)
//      res:响应对象(包含了与响应相关的属性和方法)
app.get('/user',(req,res)=>{
    //把内容响应给客户端
    res.send({name:'25',age:20,gender:'男'});//JSON对象
})

//监听POST请求
//参数与app.get()相同
app.post('/user',(req,res)=>{
    //把内容响应给客户端
    res.send('请求成功');//文本字符串
})

//获取URL中携带的查询参数 req.query
app.get('/',(req,res)=>{//http://127.0.0.1/?name=zs&age=20
    //通过req.query可以获取到客户端发送过来的查询参数
    //注意:默认情况下,req.query是一个空对象
    res.send(req.query);
})

//获取URL中的动态参数 req.params
//这里的:id和:name是动态参数
app.get('/user/:id/:name',(req,res)=>{//http://127.0.0.1/user/123/zs
    //req.params是动态匹配到的URL参数
    //注意:默认情况下,req.params是一个空对象
    res.send(req.params);
})

2.托管静态资源

Express在指定的静态目录中查找文件,并对外提供资源的访问路径。

存放静态文件的目录名不会出现在URL中。

访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需的文件。

const express=require('express');
const app=express();

//在这里,调用express.static()方法,快速对外提供静态资源
//要托管多个静态资源目录,需多次调用express.static()
//http://127.0.0.1/index.html
app.use(express.static('./clock')); //项目根目录下有一静态资源文件夹clock
app.use(express.static('./file')); //项目根目录下有一静态资源文件夹file
//挂载路径前缀
//http://127.0.0.1/public/index.html
app.use('/public',express.static('./public')); //项目根目录下有一静态资源文件夹public

app.listen(80,()=>{
    console.log('express server running at http://127.0.0.1')
});

3.nodemon

使用nodemon可以监听项目文件的变动,当代码被修改后,nodemon会自动帮我们重启项目。

# 安装nodemon
# 全局安装需要管理员权限,所以前面加sudo
sudo npm install -g nodemon

# 使用nodemon
# 用nodemon替代node执行项目的js文件
nodemon 1.js

4.路由

路由是客户端的请求与服务器处理函数之间的映射关系。

Express中的路由分三部分组成:(1)请求类型(2)请求的URL地址(3)处理函数。


1. 路由的匹配过程

每当一个请求到达服务器,需先经路由的匹配,匹配成功后调用对应的处理函数。

匹配会(1)按照路由的顺序进行,(2)若请求类型和请求的URL地址同时匹配成功,Express会将这次请求转交给对应的function函数进行处理。


2.路由的使用

(1)最简单用法:把路由挂载到app上【代码量太多,不方便管理,几乎不用】

const express=require('express');
const app=express();

//挂载路由
app.get('/',(req,res)=>{
    res.send('Hello GET!');
})
app.post('/',(req,res)=>{
    res.send('Hello POST!');
})

app.listen(80,()=>{
    console.log('express server running at http://127.0.0.1')
});

(2)模块化路由:将路由抽离为单独的模块【推荐使用!】

  • 创建路由模块对应的.js文件
  • 调用express.Router()函数创建路由对象
  • 向路由对象上挂在具体的路由
  • 使用module.exports向外共享路由对象
  • 使用app.use()函数注册路由模块
const express=require('express');
const app=express();

//1、导入路由模块
const router= require('./2.js');
//2、注册路由模块
app.use(router);
//注意: app.use()函数的作用就是注册全局中间件!
//为路由模块添加前缀:  app.use('/api',router);

app.listen(80,()=>{
    console.log('express server running at http://127.0.0.1')
});
//这是路由模块 2.js文件
//1、导入express
const express=require('express');
const res = require('express/lib/response');
//2、创建路由对象
const router=express.Router();
//3、挂载具体路由
router.get('/user/list',(req,res)=>{
    res.send('Get user list.');
})
router.post('/user/add',(req,res)=>{
    res.send('Add new user.');
})
//4、向外导出路由对象
module.exports=router;

5.中间件

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

  • Express的中间件,本质是一个function处理函数,但中间件的函数形参列表中必须包含next参数(next参数必须放在形参列表最后)。
  • next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。

(1)定义中间件函数

const express=require('express');
const app=express();

//定义一个最简单的中间件函数
const mw=function(req,res,next){
    console.log("这是最简单的中间件函数");
    next();//把流转关系,转交给下一个中间件或路由
}

//将mw注册为全局生效的中间件
app.use(mw);

app.listen(80,()=>{console.log('express server running at http://127.0.0.1')});

(2)全局生效的中间件

客户端发起任何请求到达服务器后,都会触发的中间件。

  • 通过调用app.use(中间件函数)即可定义一个全局生效的中间件。
  • 可以使用app.use()连续定义多个全局中间件。客户端请求到达服务器之后会按照中间件的定义先后顺序依次进行调用。
//定义全局中间件的简写形式
app.use(function(req,res,next){
    console.log("调用了第一个全局中间件");
    next();
})
//可以连续定义多个全局中间件
app.use(function(req,res,next){
    console.log("调用了第二个全局中间件");
    next();
})

(3)局部生效的中间件

不使用app.use()定义的中间件

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.get('/',[mw1,mw2],(req,res)=>{ res.send('Home page.')})
app.get('/',mw1,mw2,(req,res)=>{res.send('Home page.')})
app.get('/user',(req,res)=>{res.send('User page.')})

app.listen(80,()=>{console.log('express server running at http://127.0.0.1')});

(4)中间件的作用

多个中间件之间共享同一份req和res,可以在上游的中间件中统一为req或res对象添加自定义的属性或方法,供下游的中间件或路由进行使用。

const express=require('express');
const app=express();

app.use(function(req,res,next){
    const time=Date.now();//获取请求到达服务器的时间
    //为req对象挂载自定义属性,从而把时间共享给后面的所有路由
    req.startTime=time;
    next();
});

app.get('/',(req,res)=>{res.send('Home page.'+req.startTime)})
app.get('/user',(req,res)=>{res.send('User page.'+req.startTime)})

app.listen(80,()=>{console.log('express server running at http://127.0.0.1')});

(5)中间件的5个注意事项

  • 一定要在路由之前注册中间件。(除了错误级别的中间件,必须注册在所有路由之后)
  • 客户端发送过来的请求,可以连续调用多个中间件函数。
  • 执行完中间件的业务代码之后,不要忘记调用next()函数。
  • 为了防止代码逻辑混乱,调用next()函数后面不要再写额外的业务代码。
  • 连续调用多个中间件时,多个中间件之间共享res和req对象。

(6)中间件的分类

  • ①应用级别的中间件

    • 通过app.user()或app.get()或app.post(),绑定到app实例上的中间件。
  • ②路由级别的中间件

    • 绑定到express.Router()实例上的中间件,它的用法和应用级别的中间件没有区别。
  • ③错误级别的中间件

    • 用于捕获整个项目中的异常错误,从而防止项目异常崩溃。
    • 错误级别的中间件的function处理函数中必须有4个形参,顺序从前到后分别是err、req、res、next。
    • 错误级别的中间件必须注册在所有路由之后!!!!
const express=require('express');
const app=express();

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

//定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err,req,res,next)=>{
    console.log('发生了错误:'+err.message);
    res.send('Error'+err.message);
})

app.listen(80,()=>{console.log('express server running at http://127.0.0.1')});
  • ④Express内置的中间件

    • express.static:用于快速托管静态资源,如HTML文件、图片、CSS样式等(无兼容性)
    • express.json:用于解析JSON格式的请求体数据(有兼容性,仅4.16.0之后的版本可用)
    • express.urlencoded:用于解析URL-encoded格式的请求体数据(有兼容性,仅4.16.0之后的版本可用)
const express=require('express');
const app=express();

//配置解析 application/json 格式数据的内置中间件
app.use(express.json());
//配置解析 application/x-www-form-urlencoded 格式数据的内置中间件
app.use(express.urlencoded({extended:false}));

app.post('/user',(req,res)=>{ 
    //在服务器可以使用req.body接受客户端发送过来的请求体数据
    //默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined
    console.log(req.body);
    res.send('ok');
})

app.listen(80,()=>{console.log('express server running at http://127.0.0.1')});
  • ⑤第三方的中间件

    • 安装中间件:运行npm install 中间件名
    • 导入中间件:使用require
    • 使用中间件:调用app.use()注册并使用中间件

6.编写接口

const express=require('express');
const app=express();

//配置解析表单数据的中间件
app.use(express.json());
app.use(express.urlencoded({extended:false}));

//一定要在路由之前配置cors中间件,从而解决接口跨域问题
const cors=require('cors');//使用前先用npm下载
app.use(cors());

//导入路由模块
const router=require('./2.js');
//把路由模块注册到app上
app.use('/api',router);
// http://127.0.0.1/api/get?name=zs&age=20&gender=男

app.listen(80,()=>{
    console.log('express server running at http://127.0.0.1')
});
//这是API路由模块  2.js文件
const express=require('express');
const router=express.Router();

//在这里挂载对应的路由
//1、编写GET接口
router.get('/get',(req,res)=>{
    //通过req.query获取客户端通过查询字符串,发送到服务器的数据
    const query=req.query;
    //调用res.send()方法向客户端响应处理的结果
    res.send({
        status:0, //0 表示处理成功,1 表示处理失败
        msg:'GET请求成功', //状态的描述
        data:query //需要响应给客户端的数据
    })
})
//2、编写POST接口
router.post('/post',(req,res)=>{
    //通过req.body获取请求体中包含的url-encoded格式的数据
    const body=req.body;
    res.send({
        status:0, 
        msg:'POST请求成功', 
        data:body 
    })
})

module.exports=router;

7.跨域

协议、域名、端口号任何一项不同就存在跨域问题。

解决跨域问题的方案有两种:(1)CORS(主流的解决方案,推荐使用);(2)JSONP(有缺陷的解决方案,只支持GET请求)

(1)使用CORS中间件解决跨域问题的步骤

  • 安装中间件:运行npm install cors
  • 导入中间件:使用const cors=require(‘cors’)
  • 配置中间件:在路由之前调用app.use(cors())

(2)CORS(跨域资源共享)由一系列HTTP响应头组成,它们决定浏览器是否阻止前端JS代码跨域获取资源。

  • CORS主要在服务器端进行配置,客户端浏览器无需配置,即可请求开启CORS接口。
  • CORS在浏览器中有兼容性,只支持IE10+、Chrome4+、FireFox3.5+这些支持XMLHttpRequest Level2的浏览器。

(3)CORS响应头

  • Access-Control-Allow-Origin:orgin参数的值制定了允许访问该资源的外域URL。

    • * 是通配符,表示允许来自任何域的请求。
  • Access-Control-Allow-Headers:对额外的请求头进行声明。

    • 默认情况下,CORS仅支持客户端向服务器发送以下9个请求头(没写上具体的请求头)。
  • Access-Control-Allow-Methods:指明实际请求所允许使用的HTTP方法,如PUT、DELETE。

    • 默认情况下,CORS仅支持客户端发起GET、POST和HEAD请求。

(4)CORS请求的分类

  • 简单请求:同时满足(1)请求方式属于GET、POST和HEAD之一。(2)HTTP头部信息不超过以下几个字段(没写上具体的字段)。

    • 特点:客户端与服务器之间只会发生一次请求。
  • 预检请求:只要满足任一(1)请求方式为GET、POST和HEAD之外的请求Method类型。(2)请求头中包含自定义头部字段。(3)向服务器发送了application/json格式的数据。

    • 特点:客户端与服务器之间会发生两次请求。
    • 在浏览器与服务器正式通信之前,浏览器会发送OPTION请求进行预检,以获知服务器是否允许该实际请求,这一次OPTION请求称为“预检请求”。
    • 服务器成功相应预检请求后,才会发送真正的请求,并携带真实数据。

(5)JSONP

  • 概念:浏览器通过<script>标签的src属性请求服务器上的数据,同时服务器返回一个函数的调用,这种请求方式叫JSONP。
  • 特点:(1)JSONP不属于真正的Ajax请求,因为它没有使用XMLHttpRequest对象。(2)JSONP仅支持GET请求。
  • 注意:如果项目中已经配置了CORS,为防止冲突,必须在配置CORS之前声明JSONP的接口。否则JSONP接口会被处理成开启了CORS的接口。
app.get('/api/jsonp',(req,res)=>{
    //1、获取客户端发送过来的回调函数名字
    const funcName=req.query.callback;
    //2、得到要通过JOSNP形式发送给客户端的数据
    const data={name:'zs',age:20};
    //3、根据前两步得到的数据,拼接出一个函数调用的字符串
    const scriptStr=`${funcName}(${JSON.stringify(data)})`;
    //4、把上一步拼接得到的字符串,响应给客户端的<script>标签进行解析执行
    res.send(scriptStr);
})
$.('#btnJSONP').on('click',function(){
    $ajax({
        method:'GET',
        url:'http://127.0.0.1/api/jsonp',
        dataType:'jsonp',
        success:function(res){
            console.log(res)        
        }    
    })
})



版权声明:本文为SongD1114原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。