目 录CONTENT

文章目录

Express框架

Administrator
2020-07-24 / 0 评论 / 2 点赞 / 6497 阅读 / 32629 字

Express框架

Express4.16.3

基于 Node.js 平台,快速、开放、极简的 Web 开发框架

它主要的目的就是使用NodeJS快速的创建Web服务器


安装Express框架

通过npm直接安装

$ npm install express --save

Express创建项目

首先初始化项目以后,安装 express完成以后,在项目的目录下面,创建一个app.js,完成如下代码

const express = require("express"); 
//express是用于创建Web服务器的一个框架 Web Application
const app = express();     
//执行express()方法,生成一个app程序,这个app程序就是项目的程序

创建好Web项目以后,我们启动Express的服务器

//启动服务器,监听一个端口号
app.listen(8888,()=>{
   console.log("服务器创建成功");
});

在控制台执行如下命令

$ node app.js

Express处理GET请求

//app.get()处理服务器的get请求
app.get("/",(req,resp)=>{
    // Req:request 代表浏览器到服务器(客户端到服务器)
    // Resp:response 代表服务器到客户端 
    //resp.send()服务器向浏览器发送了文本信息
    resp.send("你的请求我收到了,我现在给你回了个消息");
});

app.get("/login",(req,resp)=>{
    resp.send("这是登陆页面");
});

app.get("/register",(req,resp)=>{
    resp.send("这是一个注册页面");
})

express通过app.get()的方式来处理浏览器发送过来的请求

浏览器服务器
http://127.0.0.1:8888/app.get("/")
http://127.0.0.1:8888/loginapp.get("/login")
http://127.0.0.1:8888/registerapp.get("/register")

提示: 浏览器里面的地址栏的请求永远都是GET请求

Express页面渲染

Express如果要进行页面渲染需要使用第三方模块,如ejs,jade或目的比较流行的art-template

首先,我们先在当前的项目下面新建一个文件夹views,这个文件夹只存放html文件

然后我们需要借助第三方模块art-template来完成页面的渲染,将这个页面发送给请求者

express如果要与art-template结合 ,则需要安装下面两个包

$ npm install art-template express-art-template --save

安装完成以后,进行模块引擎的配置

第一步:设置视图文件所在的位置

app.set("views",path.join(__dirname,"./views"));     //设置视图文件所在的位置

第二步:设置模板渲染引擎

因为express自己没有模板渲染引擎,所以需要用户手动的设置模板渲染引擎

const template=require("express-art-template");    //导入模板渲染引擎
app.engine("html",template);   //告诉程序,模板的后缀名是.html
app.set("view engine","html"); //设置模板引擎为html,所有的html文件都会通过template来渲染

当设置完成以后,如果希望向前台渲染一个模板文件,这个时候直接调用resp.render()方法

resp.render("index");
//resp.render("views/index.html");

Express向页面传递数据

当通过模板引擎渲染页面的时候,页面经常会有一些数据,这些数据怎么获取得到呢

app.get("/stuList",(req,resp)=>{
    //当请求这个的时候,我们渲染一个模板出去,模板的名称最好与请求的名称保持一致
    let arr=[
        "张三","李四","黄淑玲","张梦雪","向继发"
    ];
    resp.render("stuList",{abc:arr});
})
<div class="main">
    <h2 class="title">学生列表</h2>
    <hr>
    <ul>
        {{each abc item index}}
        <li>{{item}}</li>
        {{/each}}
    </ul>
</div>

现在我们通过读取数据库的数据,向页面传递数据以后显示出来

app.get("/stuList", async (req, resp) => {
    try {
        //当请求这个的时候,我们渲染一个模板出去,模板的名称最好与请求的名称保持一致
        let strSql = "select * from stuinfo";
        let result = await DBUtil.executeSql(strSql);
        resp.render("stuList", {abc: result});
    } catch (error) {
        console.log(error);
        resp.status(500);
        resp.send("服务器报错了");
    }
})

Express路由

在上面的代码里面,我们所有的请求都会到达app,然后通过app.get的方式进行处理,但是试想一下,我们不可能所有的代码全部都写在app.js这个文件里面,这样代友会显得很臃肿

1570581626128

我们可以把路由理解成上面的这样的图,之前浏览器所有的请求都应该在app.js里面处理的,现在通过某一种方式 ,我们把它些请求下发到下面的每个模块里面去,这样在这个模块里面,我们也可以处理请求。这样做有以下几个好处

  1. 代码不用全部都写在app.js里面,我们可以根据模块划分成不同的路由,这样整个项目结构清淅
  2. 如果进行团队项目,这个时候,每个人负责的模块不同,这样在开发过程当中,每个人只要负责不同的文件就可以了

路由的本质可以理解为app的本集,它与app是一样的,都是用于处理客户端发送过来的请求,它只处理req与resp

路由Router的创建

路由竟然app的子集,app则是通过express() 来创建的,所以我们就找express()

//route/StuInfoRoute.js文件
const router=require("express").Router();
router.get("/List",(req,resp)=>{
    
})
module.exports=router;

得到路由以后,这个路由所具备的方式与app所具备的方法是一样的。它同样的get()方法与post()方法来处理客户发送过来的 get/post 请求

一个路由创建成功以后,要与app.js保持一定的联系,这个时候,我们要在app.js去加载这个路由

app.use("/StuInfo",require("./router/StuInfoRoute.js"));

当一旦加载这个路由以后,我们就可以通过下面的地址进入到这个路由

http://127.0.0.1:8888/StuInfo/List

1570582251276

Express静态文件区域

本质是因为Express里面所有的文件全部都被控制器控制器,而控制器可以理解为路由与app

Express里面所有的请求全部被app或路由接管了,这个时候我们所有的请求都必须经过router或app的同意,否则是请求不到的,这个时候,有些特殊的文件我们是不需要经过它们处理的,如图片,JS文件与CSS文件等。针对这种情况,express会设置一个静态区域,让这些文件不受控制 ,可以直接访问

app.use("/public",express.static(path.join(__dirname,"./public")));

这个时候public下面的文件夹就公开了,再请求这些文件就在需要使用控制器了

Service业务逻辑操作

在项目里面,所有的项目都会涉及到相应的业务逻辑操作,例如数据库的增删改查等操作,这些操作我们会专门新建一个文件夹,用专门的文件去处理,这些文件对象我们叫Service操作

Service主要的目的是为router提供服务,**提供与数据库操作相关的服务。**所以与数据库操作相关的东西全部都在这里,按照正常开发模式,一个数据表会对应一个Service

BaseService

/**
 * 基础业务逻辑
 */
const DBUtil = require("../utils/DBUtil.js");

/**
 * @class BaseService
 */
const appConfig=require("../config/appConfig.js");
class BaseService extends DBUtil {
    /**
     * @param {string} tableName 数据表的名称
     */
    constructor(tableName) {
        super();
        this.tableName = tableName;
        this.pageSize = appConfig.pageSize;   //分页的时候每页的数据
    }

    /**
     * @name getList  获取所有管理员列表
     * @returns {Promise} 返回executeSql执行的promise
     */
    getAllList() {
        let strSql = `select * from ${this.tableName} where isDel=false `;
        return this.executeSql(strSql); //返回的是一个promise
    }

    /**
     * @name createCountSql 生成count的SQL语句
     * @param {string}} strWhere 查询条件 
     * @returns {string} countSql 生成好的SQL语句
     */
    createCountSql(strWhere){
        let countSql = `select count(*) 'totalCount' from ${this.tableName} where isDel=false ${strWhere} `;
        return countSql;
    }
}

module.exports = BaseService;

上面的BaseService提供了数据库操作的公共方法,我们可以让所有的操作都继续这个BaseService。这样可以大大的降低代码的冗余量

StuInfoService

/**
 * 数据表stuinfo的操作
 */
const BaseService = require("./BaseService.js");

/**
 * @class StuInfoService 学生表stuinfo的数据操作
 * @extends BaseService 继承基础Service
 */
class StuInfoService extends BaseService {
    constructor() {
        super("stuinfo");
    }
    /**
     * @name getListByPage 分页查询方法
     * @returns {Promise} 返回executeSql执行的Promise
     */
    getListByPage({ sid, sname, ssex,cid,pageIndex}={}) {
        //默认情况下,pageIndex是不会有值的
        pageIndex=pageIndex||1;
        let strWhere = "";
        if (sid) {
            //说明学号有值
            strWhere += " and stuinfo.sid like '%" + sid + "%'";
        }
        if (sname) {
            strWhere += " and stuinfo.sname like '%" + sname + "%'";
        }
        if (ssex) {
            strWhere += " and stuinfo.ssex='" + ssex + "'";
        }
        if(cid){
            strWhere+=" and stuinfo.cid='" + cid+"'" ;
        }
        let strSql = `SELECT stuinfo.*,classinfo.cname FROM stuinfo INNER JOIN classinfo on stuinfo.cid=classinfo.cid where stuinfo.isDel=false ${strWhere} limit ?,?`;
        let countSql = this.createCountSql(strWhere);
        return this.executeSql(strSql+";"+countSql,[(pageIndex-1)*this.pageSize,this.pageSize]);
    }

    /**
     * @name addStuInfo 添加学生信息
     * @returns {Promise} 返回executeSql执行的Promise
     */
    addStuInfo({sid, sname, ssex, sbirthday, snation, sIDCard, saddr, smail, stel, cid}){
        //准备一条新增的SQL语句
        let strSql=`INSERT INTO ${this.tableName} (sid, sname, ssex, sbirthday, snation, sIDCard, saddr, smail, stel, cid) VALUES (?,?,?,?,?,?,?,?,?,?)`;
        return this.executeSql(strSql,[sid, sname, ssex, sbirthday, snation, sIDCard, saddr, smail, stel, cid]);
    }
}

module.exports = StuInfoService;

1570582608124

express-art-template分布式视图

在Express的art-template里面,可以使用include去包含一个子视图

<%
    var obj={pageTitle:"学生列表"};
%>
{{include "../layout_header.html" obj }}
    <div>
        
    </div>
{{include "../layout_footer.html"}}

子模板默认是共享父级模板的数据,同时也可以单独去传递数据过去,上面的layout_header.html就使用了父级模板传递过去的数据obj

在上面的模板里面,我们有两种语法,第一种是<%%>的语法,第二种是{{}}这种语法

第一种语法可以书写原生的JS代码

第二各语法书写起来更方便一点

如果我们要在模板里面去书写原生JS代码,例如定义变量,例如执行for循环等

<div class="clearfix">
    <ul class=" pagination pull-right">
        <%for(var i=1;i<=pageModel.pageCount;i++){%>
        <li class="{{pageModel.pageIndex==i?'active':''}}"><a data-pageindex="{{i}}" href="#">{{i}}</a></li>
        <%}%>
    </ul>
</div>

这两种语法可以混合在一起写,根据不同的情况去书写就可以了

分布式视图可以极大的对页面模块进行分割,更有利于页面的模块化

Express里面的Form提交

前端向后端提交数据的方式目前主要有两种

第一种是我们之前所学习的Ajax 提交方式 ,它的核心是创建一个XMLHttpRequest的对象,在后面默默的帮我们提交( 所以Ajax也叫无刷新请求)

第二种方式则是最基本的方式通过 Form 表单提交

在之前学习HTML标签的时候,我们已经学过了form标签,也学过了很多表单元素,在这里,我要重新列举几个重点

  1. 表单元素存放值的属性是value属性
  2. 所有的表单元素最好是放在表单标签form里面
  3. form标签有几个常用属性
    • action代表提交到后台的地址
    • method代表提交方式
  4. 表单里面的数据提交到后台去的时候,是通过表单元素的name来连接的,表单在提交的时候会自动将name与value连接在一起?name=value&name=value,后台在取值的时候,也是通过这个name来取值
  5. Form表单提交会跳转页面
<form action="/StuInfo/doAddStu" method="get">
    <input type="text" name="userName">
    <button type="submit">
        提交数据
    </button>
</form>

当上面的表单点击提交按钮以后,会将表单里面的数据提交到 action 的地址/StuInfo/doAddStu, 通过get的方式提交

Express接收前端提交过来的值

GET方式提交过来的值

req.query只接收GET传值

无论是在app.js里面还是在某个路由里面,我们都会通过app.get()或router.get()来处理前端通过get方式提交 过来的请求,如果想获取提交过来的参数,我们可以找req这个对象,所以提交过来的参数都在req.query这个对象里面

如果前端提交了一个userName=yangbiao,那么后台在这里就是req.query.userName

let userName=req.query.userName;
//但是我们在项目里面都是直接解构解出来
let {userName}=req.query;

POST方式提交过来的值

req.body只接收POST传值

Express默认情况下是支持GET取值而不支持POST取值的,如果想让Express支持POST取值 ,则需要使用到三方的插件(中间件)body-parser

$ npm install body-parser --save

接下来要在app.js里面去加载插件

//app.js
const bodyParser=require("body-parser");    //POST取值插件
//Express里面加载插件(中间件)使用app.use()
app.use(bodyParser.urlencoded({extended:false,limit:'10mb'}));
app.use(bodyParser.json({limit:'10mb'}));

现在我们就可以通过req.body来接收前面POST传递过来的值

Express文件上传

Express本身不支持文件上传的功能 ,如果要使用文件上传需要依赖于第三方模块multer,可以通过npm进行安装

multer安装

$ npm install multer --save

在使用Form表单进行提交的时候,如果要使用文件上传,只能够使用POST请求,同时还需对表单的属性enctype="multipart/form-data"进行设置

<form action="/StuInfo/doAddStu" method="POST" enctype="multipart/form-data">
   <input type="file" name="sphoto" id="sphoto">
</form>

enctype:全称是Content-Type 默认值为application/x-www-form-urlencoded

如果要使用文件上传要将这个值设置为multipart/form-data

题外话:之前我们已经使用了body-parse来处理POST请求,但是这个body-parse只能够处理Content-Typeapplication/x-www-form-urlencodedapplication/json 的,现在我们的文件上传的Content-Type设置成为了multipart/form-data就需要使用第三方包multer来进行了

multer配置使用

第一步:配置模块

const multer = require("multer"); //接收上传的文件的模块
const upload = multer({
    dest:appConfig.uploadImgs     //设置上传的文件要保存在哪个文件夹
});

调用multer方法,设置上传文件的文件夹

第二步:加载插件

接下来,在需收文件的express路由上面添加一个模块插件

router.post("/doAddStu",upload.single("sphoto"), async (req, resp) => {
	//.....
})

上面的upload.single("sphoto")代表接收一个文件,里面的参数sphoto代表文件的name<input type="file" nane="sphoto">

第三步:接收文件

router.post("/doAddStu",upload.single("sphoto"), async (req, resp) => {
	let file=req.file;  //接收一个文件,如果用户没有上传文件,则是undefined
    if(file){
        //说明接收到了文件
        console.log(file);
        //重命名文件,给文件添加后缀名
        fs.renameSync(file.path,file.path+file.originalname);
    }
})

我们将收到的file打印结果列出来,如下

{ 
 fieldname: 'sphoto',
 originalname: 'item1.jpg',
 encoding: '7bit',
 mimetype: 'image/jpeg',
 destination: 'D:\\H1904\\1007\\code\\StudentManager\\uploadImgs',
 filename: '5c089e0717009a03570556e494d65dd3',
 path:
  'D:\\H1904\\1007\\code\\StudentManager\\uploadImgs\\5c089e0717009a03570556e494d65dd3',
 size: 48228 
}
  • originalname文件的原始名称
  • path代表当前文件存在什么地方
  • filename代表当前这个文件的名子

要注意文件的新名子是没有后缀名的,我们要给文件添加一个后缀名

当图片上传到服务器,并且将文件名保存到数据库以后,我们就要公开个文件夹,以方便浏览器能够访问到这张图片

app.use("/uploadImgs",express.static(appConfig.uploadImgs));
console.log("uploadImgs上传图片夹已公开");

多图片上传

多图片上传的时候,配置有一些不一样

<input type="file" name="sphoto" id="sphoto">
<input type="file" name="sphoto2" id="sphoto2">

上面有两个文件选择框,但是name属性值不一样,所以我们在加载multer这个插件的时候,要如下配置

upload.fields([
    { name: "sphoto" }, { name: "sphoto2" }
]),

这个时候,我们接收到的req.files就是一个如下的对象

{
  sphoto: [
    {
      fieldname: 'sphoto',
      originalname: 's_photo-1533655937571.jpg',
      encoding: '7bit',
      mimetype: 'image/jpeg',
      destination: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs',
      filename: 'aa74d57b51ed5cbb17058835ec331ada',
      path: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs\\aa74d57b51ed5cbb17058835ec331ada',
      size: 5777889
    }
  ],
  sphoto2: [
    {
      fieldname: 'sphoto2',
      originalname: 's_photo-1533799257285.jpg',
      encoding: '7bit',
      mimetype: 'image/jpeg',
      destination: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs',
      filename: 'de1b3e809d3b88683fe69aa4b45268a5',
      path: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs\\de1b3e809d3b88683fe69aa4b45268a5',
      size: 100147
    }
  ]
}

对于上面这种数据格式,我们需要改后缀名,关键就在于怎么处理上面的数据格式

app.post("/doAddStuInfo2", upload.fields([
    { name: "sphoto" }, { name: "sphoto2" }
]), (req, resp) => {
    console.log(req.body);
    let v = Object.values(req.files);
    if(v.length>0){
        v.forEach(item => {
            let file = item[0];
            fs.renameSync(file.path, file.path + file.originalname);
        });
    }
    
    resp.send("我收到你的请求了");
})

多图片上传二

还有一种情况的多图片上传指的是一个文件选择框里面可以选择多个文件,所以有如下情况产生

<input type="file" name="sphoto" id="sphoto" multiple>

针对上面的情况,我们可以采用如下方式解决

//多图片上传,一个选择框先多个文件
app.post("/doAddStuInfo2", upload.array("sphoto"), (req, resp) => {
	console.log(req.files);
});

这个时候,我们接收到的文件数据格式就是如下的数据格式了

[
  {
    fieldname: 'sphoto',
    originalname: 's_photo-1533708439617.JPG',
    encoding: '7bit',
    mimetype: 'image/jpeg',
    destination: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs',
    filename: '502b44475bd6263f92db6e98c48c7f5c',
    path: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs\\502b44475bd6263f92db6e98c48c7f5c',
    size: 997601
  },
  {
    fieldname: 'sphoto',
    originalname: 's_photo-1533709034337.JPG',
    encoding: '7bit',
    mimetype: 'image/jpeg',
    destination: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs',
    filename: 'a84d8ea7c72ef653510cbe20a90dec0d',
    path: 'D:\\杨标的工作文件\\班级教学笔记\\H2003\\0226\\code\\022601\\uploadImgs\\a84d8ea7c72ef653510cbe20a90dec0d',
    size: 747948
  }
]

它是一个数组,我们只要把这个数组去遍历,然后再重命名就可以了

针对上面接收到的数据,我们可以进行如下重命名处理

//多图片上传,一个选择框先多个文件
app.post("/doAddStuInfo2", upload.array("sphoto"), (req, resp) => {
    console.log(req.body);
    if (req.files.length > 0) {
        //说明我接收到文件服
        for (let file of req.files) {
            fs.renameSync(file.path, file.path + file.originalname);
        }
    }
    resp.send("我收到你的请求啦");
});


###  Express验证码生成

在express的框架里面,我们无法生成图片,但是我们可以使用SVG的形式生成图片验证码,SVG是一种新的图片格式,在NodeJS里面可以借助于`svg-captcha`这个模块来完成图片验证码

**第一步:安装模块**

```bash
$ npm install svg-captcha --save

第二步:创建普通验证码

const svgCaptcha = require('svg-captcha');

const cap = svgCaptcha.create();
console.log(cap);
// {data: '<svg.../svg>', text: 'abcd'}

调用 create() 之后,会返回一个对象,结构如下:{data:'',text:''}

  • data:验证码 svg 图片
  • text: 验证码字符

create()的参数如下:

  • size: 4 验证码长度
  • ignoreChars: '0o1i' 验证码字符中排除 0o1i
  • noise: 1 干扰线条的数量
  • color: true 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
  • background: '#cc9966' 验证码图片背景颜色

示例

2.jpg

创建算数式验证码

const cap = svgCaptcha.createMathExpr(options)

示例:

2.jpg

在express 中使用

在网页中使用验证码的时候,无非是请求一个 URL ,返回一个验证码图片。

  1. express 中构建一个 /getVCode的路由

    const express = require('express');
    const captcha = require('svg-captcha');
    const router = express.Router();
    
    router.get('/getVCode',(req,res)=>{
      const cap = captcha.createMathExpr();
      req.session.captcha = cap.text; // session 存储
      resp.setHeader("Content-Type","image/svg+xml"); // 响应的类型
      res.send(cap.data);
    });
    
  2. 前端使用

    <img src="/Main/getVCode" id="VCodeImg" alt="验证码" />
    
  3. 点击切换码

    //点击刷新验证码
    $("#VCodeImg").click(function(){
        var src=$(this).attr(src);
        // 在请求地址的后面加上一个随机数,这样每次请求的SRC都不一样,这样img就会重新发起请求
        // 重新发起请求就相当于重新获取验证码
        $(this).attr("src",src+"?"+Math.random());
    })
    

Express的session使用

Session是服务器的存储机制,它可以在服务器上面开辟一个独立的互不干扰的空间去存储数组,这个空间不受客户端的请求的影响,每个session代表的是浏览器与服务器的连接

Express默认是不支持session的,它依赖于第三方模块express-session

第一步:安装

$ npm install express-session --save

第二步:加载模块,配置Session

app.js文件

//express的session插件
const session=require("express-session");
//加载session插件(中间件)
app.use(session({
    secret:"hellobiaogege",       //是否加密
    resave:true,        //是否可以重新保存
    saveUninitialized:false     //每次保存的时候,是否重新初始化
}));

第三步: session的使用

//检测管理员的登陆
router.post("/checkLogin",async(req,resp)=>{
    //在这里,我们要接收前台传过来的用户名,密码
    //现在我们假定是管理员登陆,所以我们去管理的地方去验证用户名密码
    try {
        let{userName,userPwd}=req.body;
        let adminInfoservice=ServiceFactory.createAdminInfoService();
        let result = await adminInfoservice.checkLogin(userName,userPwd);
        if(result.length>0){
            //把当前用户的登陆信息记录到系统里面去
            req.session.userInfo=result[0];
            //登陆成功,跳转到主页面
            resp.redirect("/Main/Index");
        }
        else{
            //登陆失败
            MessageBox.alertAndBack("用户名或密码错误,请重试",resp);
        }
    } catch (error) {
        resp.status(500).send("服务器错误");
        console.log(error);
    }
});

session可以赋值,也可以取值,它是互不干扰的独立部分

通过session与Express拦截器实现登陆验证

express默认是支持拦截器的,它可以在某一个点上面拦截请求,也可以在某一个路由上面拦截请求

express的拦截器最好写在路由的前面,这样就是一个全局的拦截器,同时也要写在/public的静态路径后面,因为所有的静态路径我们都要放行

//拦截器要设置在路由的前面 ,如果设置在路由的前面,由对所有的路由进行拦截
app.use((req,resp,next)=>{
    //如果用户没有登陆,怎么办
     if(req.session.userInfo){
        //这说明用户有登陆  正常放行
        next();
     }
     else{
        //这说明没有登陆
        if(appConfig.excludePath.includes(req.path)){
            //如果当前访问的路径包含在排除的数组里面,说明这个路径不需要登陆就可以放行
            next();
        }
        else{
            //跳转到登陆页
            resp.redirect("/Main/Login");
        }
     }
});

因为我们将登陆成功的信息放置在了session里面,这样我们只需要在拦截器里面判断是否有登陆的session信息就可以了,如果没有,就做进一步的判断

同时有一点非常重要,在这个里面,我们要对一些特殊的路径进行放行,例如当用户输入的是登陆页面的时候,我们就要对这个请求进行放行,让用户去登陆(如果我们不放行,这个时候,用户连登陆也进不去了,这就会造成一个死循环【我没登陆你不让我进,我要去登陆你又不让我进】)

为了对特殊的路径去放行,我们把要放行的路径信息写在了一个数组里面,放在了appConfig.js这个文件里面

let appConfig={
    //......之前的代码
    excludePath:[
        "/Main/Login",                                  //登陆页面的路径 
        "/Main/getVCode",                               //获取验证码的路径
        "/AdminInfo/checkVCode",                        //检测验证码的路径
        "/AdminInfo/checkLogin",                         //检测用户名与密码登陆的路径 
        "/Main/fromDing"
    ]
}
module.exports=appConfig;

Express的弹窗消息使用

express本身不支持对浏览器进行消息提示的功能 ,我们可以通过自定义的方式来完成,思路则是对resp.send()方式的使用,通过这个方法,我们可以向页面发送字符串,所以我们也可以向页面发送JS脚本代码

resp.send(`<script>alert("${这是弹出消息}");</script>`);
resp.send(`<script>alert("${这是弹出消息}");history.back();</script>`);
resp.send(`<script>alert("${这是弹出消息}");location.href="/Main/Index";</script>`);

我们可以将上面的方法进行封装,在需要的地方进行调用就可以了

/**
 * 消息对象框 
 */
 /**
  * @class MessageBox  消息对话框
  */
 class MessageBox{  
     constructor(){
         throw new Error("当前对象不允许new调用");
     }
     /**
      * @name alertAndBack 弹窗消息,并返回
      * @param {string} msg 要弹出的消息 
      * @param {*} resp Response对象
      */
    static alertAndBack(msg,resp){
        resp.send(`<script>alert("${msg}");history.back();</script>`);
    }
    /**
     * @name alertAndRedirect 弹窗消息,并跳转到指页的定页  
     * @param {string} msg 弹出的消息
     * @param {string} url 要跳转的页面
     * @param {*} resp Response对象
     */
    static alertAndRedirect(msg,url,resp){
        resp.send(`<script>alert("${msg}");location.href="${url}";</script>`);
    }
 }
 module.exports=MessageBox;

在utils的文件夹里面,我们可以将上面的方法放在这个文件夹当中,在需要的地方导入这个对象就可以了

MessageBox.alertAndBack("这是消息框",resp);
MessageBox.alertAndRedirect("这是消息框 ,点击确定后转向","/Main/Index",resp);

Express的文件下载

在之前的项目式教学里面,我们涉及到了一个点就是将生成好的文件导出来了以后要从服务器发送到浏览器,这个时候,我们可以使用express里面的两个方法向浏览器发送文件

  • resp.sendFile()
  • resp.download()

上面的两个方法都可以发送文件到浏览器

//发起一个像这样的请求,我们就导出excel文件
router.get("/exportToExcel", async (req, resp) => {
    try {
        //调用Service生成相应的数据
        let stuInfoService = ServiceFactory.createStuInfoService();
        let result = await stuInfoService.exportToExcel(req.query);
        //第一步,准备一个文件名
        let excelName = Date.now() + ".xlsx";
        let excelPath = path.join(appConfig.excelDirectory, excelName);
        //第二步,生成Excel文件
        ExcelUtil.resultToExcel(result, excelPath);
        //第三步,将刚生成好的excel文件发送到浏览器,提供用户下载
        resp.download(excelPath);
    } catch (error) {
        resp.status(500).send("服务器出错了");
        console.log(error);
    }
});

上面的代码是将生成好的excel文件发送到了浏览器,这里使用的resp.download()方法

NodeJS配置启动信息

一般情况下,我们可以使用node命令直接启动某一个项目或运行某一个JS文件,但是在大型的项目里面,我们可以需要将项目的启动命令配置在项目信息文件package.json 里面

//package.json
{
    //......
    "scripts":{
        "start": "node app.js"
    }
    //......
}

当我们在package.json里面配置了信息以后,我们就可以直接启动这个命令了

$ npm run start

这个start的关键命令是package.json自带的命令,如下图所示

1570871480754

这些提示信息的命令都是系统内置命令,如果是系统内置的命令,我们可以省略掉run

$ npm start

但是对于非系统的自带命令,我们一定要加上run

{
    //....
    "scripts":{
        "biaogege":"node app.js"
    }
    //......
}

biaogege不是系统自带的命令,所以run关键字我们就不能省略

$ npm run biaogege

以后在使用的时候尽量添加run关键字启动,以防止报错


Express创建服务器[补充]

在之前大家通过Express创建服务器的步骤如下

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

app.listen(80,()=>{
     console.log("服务器启动成功"); 
});

在开发环境下面,我们像上面写是没有问题的。但是项目做完以后最终是要上线的,上线的时候,我们要把项的服务器启动改一下

const express=require("express");
const app=express();
const http=require("http");
const server=http.createServer(app);

server.listen(80,()=>{
   console.log("服务器启动成功"); 
});

Express对Cookie的操作

express默认是不支持cookie的,如果要进行cookie的操作,需要借用第三方的模块cookie-parser

第一步:安装模块

$ npm install cookie-parser --save

$ yarn add cookie-parser

第二步:加载模块

const cookieParser=require("cookie-parser");
//express加载第三方插件使用use方法
app.use(cookieParser());

第三步:cookie取值

cookie是随着浏览器的请求到达服务器,所以我们要找request对象req

req.cookies.userName;  //取值cookie里面的userName

第四步:cookie赋值

cookie应该随着随着服务器的响应response的对象resp返回到浏览器

resp.cookie("sex","女",{
    maxAge:1000*60*30,           //设置cookie的过期时间是30分钟以后
    path:"/"
});

清除cookie

 resp.clearCookie("age");  //清除age的cookie

加密的Cookie

因为cookie是直接存放在浏览器的,这个时候,所有用户都可以看到这个cookie的信息,这样做很不安全,所以express里面内置了一套cookie的加密机制

第一步:在加载cookieParser这个插件的时候,给一个加密码

app.use(cookieParser("aaabbb"));

第二步:在设置cookie的时候,把这个cookie设置为签名cookie(可以理解为加密cookie)

 resp.cookie("userName","杨标",{
     maxAge:1000*60*30,
     signed:true         //代表使用签名的cookie(加密cookie)
 })

第三步:获取加密的cookie

通过普通方式req.cookie.userName这只能获取普通的未加密的cookie , 如果是加密的 cookie则不能这么获取

req.signedCookies.userName; //获取到加密的cookie值“杨标”

始终都要刻是一点, cookie是以文档(document)为单位存储的,它会随着浏览器的请求到服务器,也会随着服务器的响应到浏览器,所以cookie的存值不应过大,过大则可能出现丢失的情况,也有可能出现访问变慢的情况


MVC开发模式

MVC是一种开发模式,并不是一种技术。它不针对于语言,而针于对于平台与架构

c#语言对应的是asp.net MVC或MVC core框架

Java里面以前是Structs,现在是 springMVC框架

php里面则是thinkPHP框架

在我们的NodeJS里面,有很多MVC类型的框架 ,Express、KOA、EGG

所谓的MVC其实就是三大块

M:Model模型

V:View视图

C:Controller控制器

img

请求首先到达的是控制器,控制器根据你的请求路径去决定做相应的操作,例如通过service调用数据库的方法,获取数据(Model),然后将这个模块渲染到一个特定的视图模板文件里面,然后再将渲染好的页面返回到浏览器

优点

  1. 层次结构非常明确,控制器和视图与模型分开,可以构建低耦合的开发模式

  2. MVC并不是全套,可以根据相应的需求来决定是否结合

    例如如果仅仅只是需要返回一个视图view,则不需要model

    如果仅仅只是希望返回一串json数据,则不需要view

缺点

  1. 前后端结合的开发模式,视图并且要控制器渲染能够显示,前端的页面代码与后端的逻辑处理,数据库处理,模块处理的代码都放在了一起,项目比较臃肿

  2. 视图与模型的结合渲染是在服务器完成的,当结合完成以后,再通过resp返回给浏览器展示出来,这样会消耗服务器的性能

    (为了解决这样的缺点,后期出现的开发模式就是MVVM的开发模式)

2

评论区