总结Node.js模块开发及常用技巧分享
一、模块化
模块化做为一种现代化的设计方法,这个概念最早起源于生产制造行业。如今这个概念已经被各行各业来衍生应用,在软件开发中也大量的采用了模块化思想。
所谓的模块化思想,将一个大程序按照功能划分为若干个小的模块,每个小程序模块完成一个特定的功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求功能的程序设计方法。【推荐:node.js视频教程】
(一)、为什么需要模块化
模块化可以使你的代码低耦合,功能模块直接不相互影响。
为什么需要模块化:
程序复杂度上升代码越写越多,在一个文件里代码就会越来越长,不易维护。(把完成特定功能的代码分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少)
JavaScript 有复杂的依赖关系的时候就很容易出现一些变量的属性或方法被覆盖或改写,导致变量污染。这是因为js没有命名空间,不像其他语言通过命名空间可以有效的避免重名问题。
想要存在JavaScript 私有的变量
模块化思想解决问题:
可维护性:每个模块都是单独定义的,之间相互独立。模块尽可能的需要和外部撇清关系,方便我们独立的对其进行维护与改造。维护一个模块比在全局中修改逻辑判断要好的多。
命名冲突:为了避免在JavaScript中的全局污染,我们通过模块化的方式利用函数作用域来构建命名空间,避免命名冲突。
文件依赖:一个功能可能依赖一个或多个其他文件,使用是除了引入它本身还需要考虑依赖文件,通过模块化 我们只需要引入文件,无需考虑文件依赖(模块化可以帮助我们解决文件依赖问题)。
可复用性:虽然粘贴复制很简单,但是要考虑到我们之后的维护以及迭代。
(二)、什么是Nodejs模块
为了让Nodejs的文件可以相互调用,Nodejs基于CommonJS规范提供了一个简单的模块系统。(nodejs实现并遵守CommonJS规范的)。
把具有公共功能的,抽离成一个单独的js文件作位一个模块。默认情况下,模块里的方法或属性,外面是访问不到的。如果想要在外面访问这些属性,方法,就必须在模块里通过exports
和module.exports
暴露,在需要使用的地方通过require()
进行引入。
//exports语法示例 // sum.js exports.sum = function(a,b){ return a+b; } // main.js var m = require("./sum"); var num = m.sum(10,20); console.log(num); //modules.exports语法示例 //sum.js function sum(a,b){ return a+b; } module.exports= sum; //main.js var sum = require('./sum'); sum(10,20);// 30
CommonJS 规定:
每个模块内部,module 变量代表当前模块
module 变量是一个对象,它的 exports 属性(即 module.exports)是对外的接口
加载某个模块,其实是加载该模块的 module.exports 属性。require() 方法用于加载模块。
(三)、nodejs中的模块分类与加载方式
Node.js 中根据模块来源的不同,将模块分为了 3 大类,分别是:
内置模块(内置模块是由 Node.js 官方提供的,例如 fs、path、http 等)
自定义模块(用户创建的每个 .js 文件,都是自定义模块)
第三方模块(包)(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)
//1.加载内置模块不需要指定路径 var http = require('http'); //2.加载用户的自定义模块 var sum = require('./sum.js'); //3.加载第三方模块 const md5=require("md5");
(四)、模块作用域
- 和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。
- 模块作用域的好处:防止了全局变量污染的问题
二、npm与包
(一)、包的介绍
- Node.js 中的第三方模块又叫做包。包是由第三方个人或团队开发出来的,免费供所有人使用。
- Node.js 的内置模块仅提供了一些底层的 API,导致在基于内置模块进行项目开发的时,效率很低。
包是基于内置模块封装出来的,提供了更高级、更方便的 API,极大的提高了开发效率。 - 国外有一家 IT 公司,叫做 npm, Inc. 这家公司旗下有一个非常著名的网站: https://www.npmjs.com/ ,它是全球最大的包共享平台。
- 我们可以使用这个包管理工具, npm ,来对包进行管理,这个包管理工具随着 Node.js 的安装包一起被安装到了用户的电脑上。检测其版本。
npm -v
(二)、npm的使用
npm init
进行初始化,生成package.json
文件,记录项目的信息,记录包的信息npm install 包名
npm i 包名
下载包,放到node_modules
文件夹里npm i 包名 --save
npm i 包名 -S
(开发环境中)npm i 包名 --save-dev
npm i 包名 -D
(生产环境中)npm list
列举当前目录下安装的包npm i 包名@1
安装指定的版本npm i 包名 -g
安装全局包npm uninstall 包名
卸载包
(三)、全局安装nrm nodemon
nrm 是一个管理 npm 源的工具。有时候国外资源太慢,使用这个就可以快速的在npm源间切换。
手动切换方法:
npm config set registry=https://registry.npm.taobao.org
安装 nrm:
$ npm i nrm -g
查看nrm 内置的几个 npm 源的地址:
$ nrm ls
结果如下:
npm ---- https://registry.npmjs.org/ cnpm --- http://r.cnpmjs.org/ * taobao - https://registry.npm.taobao.org/ nj ----- https://registry.nodejitsu.com/ rednpm - http://registry.mirror.cqupt.edu.cn/ npmMirror https://skimdb.npmjs.com/registry/ edunpm - http://registry.enpmjs.org/
切换nrm:
$ nrm use npm
查看当前的镜像源:
npm config get register
(四)、淘宝cnpm工具
淘宝 NPM 镜像是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。
你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm:
npm install -g cnpm -registry=https://registry.npm.taobao.org
安装包
cnpm install [模块名]
三、路由
路由:根据不同的路径返回不同的页面。
案例:server.js;static;route.js;route对象;render();api.js;封装server.js到index.js;合并对象
获取请求参数:login页面发get,post请求,到/api/login
静态资源托管:
express
Express是一个基于 Node.js 平台,快速、开放、极简的 Web 开发框架。
一、下载与安装
$ npm install express --save
案例:第一个express案例。
二、路由
所谓的路由就是客户端用来与后端服务器进行交互的一种方式,客户端采用特定的URL与请求方法来访问服务器端,服务器端通过响应返回特定资源。
路由的定义由如下结构组成:
app.METHOD(PATH, HANDLER)
名称 | 描述 |
---|---|
app | app 是一个 express 实例 |
METHOD | METHOD用于指定要匹配的HTTP请求方式 |
PATH | PATH是服务器端的路径 |
HANDLER | 是当路由匹配时需要执行的处理程序(回调函数) |
路由路径和请求方法一起定义了请求的端点,它可以是字符串,字符串模式以及正则表达式。
app.get('/', function (req, res) { res.send('root')})app.get('/about', function (req, res) { res.send('about')})app.get('/random.text', function (req, res) { res.send('random.text')})
使用字符串模式的路由路径示例:
app.get('/ab?cd', function (req, res) { res.send('ab?cd') //abcd ,acd})app.get('/ab/:id', function (req, res) { res.send('ab/:id') })app.get('/ab+cd', function (req, res) { res.send('ab+cd') //b可以一次或者多次的重复})app.get('/ab*cd', function (req, res) { res.send('ab*cd') //在ab,cd之间随意写入任意字符})app.get('/ab(cd)?e', function (req, res) { res.send('ab(cd)?e')})
可以为请求处理提供多个回调函数,其行为类似中间件。
案例:中间件f1,f2
app.get('/example/b', function (req, res, next) { console.log('the response will be sent by the next function ...') next()}, function (req, res) { res.send('Hello from B!')})
var cb0 = function (req, res, next) { console.log('CB0') next()}var cb1 = function (req, res, next) { console.log('CB1') next()}var cb2 = function (req, res) { res.send('Hello from C!')}app.get('/example/c', [cb0, cb1, cb2])
var cb0 = function (req, res, next) { console.log('CB0') next()}var cb1 = function (req, res, next) { console.log('CB1') next()}app.get('/example/d', [cb0, cb1], function (req, res, next) { console.log('the response will be sent by the next function ...') next()}, function (req, res) { res.send('Hello from D!')})
三、中间件
1.应用级中间件
在Express程序中,使用
app.use()
和app.METHOD()
方法将中间件绑定到应用程序对象(app)。凡是挂载在app身上的都是应用级中间件。
app.use() : 应用中的每个请求都可以执行其代码
//万能中间件var express = require('express')var app = express()app.use(function (req, res, next) { console.log('Time:', Date.now()) next()})
//特定路由的应用中间件app.use("/login",function (req, res, next) { console.log('Time:', Date.now())})
2.路由级中间件
路由器级中间件的工作方式与应用级中间件相同,只是它绑定到express.Router()
.
var router = express.Router()
var express = require('express')var app = express()var router = express.Router()router.use(function (req, res, next) { console.log('Time:', Date.now()) next()})router.get('/user/:id', function (req, res, next) { console.log('Request URL:', req.originalUrl) next()}, function (req, res, next) { console.log('Request Type:', req.method) next()})
案例: / ; /home , /list
3.错误处理中间件
与其他中间件函数相同的方式定义错误处理中间件函数,但是使用四个参数
(err, req, res, next)
,放到最后。
app.use(function (err, req, res, next) { console.error(err.stack) res.status(404).send('Something broke!')})
4.内置中间件
express.static提供静态资源,例如 HTML 文件、图像等。
express.json使用 JSON 有效负载解析传入请求。注意:可用于 Express 4.16.0+
application/json
由于JSON规范的流行,现在越来越多的开发者将application/json这个Content-Type作为响应头。用来告诉服务端消息主体是序列化后的JSON字符串。除了低版本IE之外各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理JSON的函数,JSON能格式支持比键值对复杂得多的结构化数据,普通键值对中的值只能是字符串,而使用json,键值对可以重复嵌套。
// 应用级别中间件,获取post--json参数app.use(express.json());
express.urlencoded解析带有 URL 编码负载的传入请求。 注意:可用于 Express 4.16.0+
application/x-www-form-urlencoded
浏览器的原生form表单,如果不设置enctype属性,那么最终就会以 application/x-www-form-urlencoded方式提交数据。Content-Type被指定为application/x-www-form-urlencoded提交的数据按照key=val&key=val的方式进行编码,并且key和val都进行了URL转码。// 应用级别中间件,获取post--form参数app.use(express.urlencoded({extended:false}));
5.第三方中间件
四、获取请求参数
- req.query 是一个可获取客户端get请求 查询字符串 转成的对象,默认为{}。
- req.body 包含在请求体中提交的数据键值对。默认情况下undefined,当使用解析中间件
express.json()
、express.urlencoded()
五、静态资源托管
为了提供诸如图像、CSS 文件和 JavaScript 文件之类的静态文件,请使用 Express 中的 express.static 内置中间件函数。
语法:
express.static
内置中间件函数语法:
express.static(root, [options])
- root参数指定提供静态资源的根目录。
静态资源示例:
例如,通过如下代码就可以将 public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:
app.use(express.static('public'))
现在,你就可以访问 public 目录中的所有文件了:
http://localhost:3000/images/kitten.jpg http://localhost:3000/css/style.css http://localhost:3000/js/app.js http://localhost:3000/images/bg.png http://localhost:3000/hello.html
Express 在静态目录查找文件,因此,存放静态文件的目录名不会出现在 URL 中。
多个静态资源目录:
如果要使用多个静态资源目录,请多次调用 express.static
中间件函数:
app.use(express.static('public')) app.use(express.static('uploads'))
访问静态资源文件时,express.static
中间件函数会根据目录的添加顺序查找所需的文件。
虚拟路径:(非得添加static,没有意义)
express.static
中间件函数可以为某些静态资源服务创建虚拟路径前缀(该路径实际上并不存在于文件系统中),请指定静态目录的挂载路径,如下所示:
app.use('/static', express.static('public'))
现在,你就可以通过带有 /static
前缀地址来访问 public
目录中的文件了。
http://localhost:3000/static/images/kitten.jpg http://localhost:3000/static/css/style.css http://localhost:3000/static/js/app.js http://localhost:3000/static/images/bg.png http://localhost:3000/static/hello.html
六、模板引擎
一、.服务端渲染 SSR
后端嵌套模板,后端渲染模板 (后端把页面组装起来)
- 做好静态页面,动态效果
- 把前端代码提供给后端,后端要把静态html以及里面的假数据给删掉,通过模板进行动态生成html的内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jL70ca3p-1658387147221)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\服务器端渲染.png)]
二、前后端分离,BSR
(前后端分离,通过通用的json数据形式,不挑后端的语言)
- 做好静态页面,动态效果
- json模拟,ajax动态创建页面
- 真实接口数据,前后联调
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pXRCidw2-1658387147223)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\前后端分离.png)]
三、模板引擎
模板引擎能够在应用程序中使用静态模板文件。在运行时,模板引擎将模板文件中的变量替换为实际值,并将模板转换为发送给客户端的 HTML 文件。这种方法使设计 HTML 页面变得更加容易。
Express模板引擎
与 Express 一起使用的一些流行模板引擎是Pug、 Mustache和EJS。Express 应用程序生成器默认使用Pug,但它也支持其他几个。
需要在应用中进行如下设置才能让Express渲染模板引擎:
- views,放模板文件的目录。例如:
app.set('views', './views')
。 - view engine,要使用的模板引擎。例如,要使用 Pug 模板引擎:
app.set('view engine', 'pug')
.
在路由中渲染模板
在路由渲染模板并将渲染后的 HTML 字符串发送到客户端。
res.render(view [, locals] [, callback])
- view:一个字符串,view是要渲染的模板文件的文件路径。
- locals:一个对象,其属性定义视图的局部变量。
app.get('/', function (req, res) { res.render('index', { title: 'Hey', message: 'Hello there!' }) })
四、ejs模板引擎的使用
安装ejs
npm install ejs
在express配置ejs模板引擎
使用ejs模板引擎
在app.js中添加如下代码,配置Express使用ejs模板引擎。
app.set('views',path.join(__dirname,'views')); //设置模板存储位置app.set('view engine','ejs');
注意:此时指定的模板目录为
views
,且模板文件的后缀名为.ejs
。
设置模板后缀为html
在app.js中添加如下代码,配置Express使用ejs模板引擎。并指定模板后缀名为html。
app.set('views',path.join(__dirname,'views')); //设置模板存储位置app.set('view engine','html');app.engine('html',require('ejs').renderFile); //使用ejs模板引擎解析html
注意:此时指定的模板目录为
views
,且模板文件的后缀名为.html
。
ejs模板语法
<%= %> 输出标签 <%- %> 输出html标签(html会被浏览器解析) <%# %> 注释标签 <% %> 流程控制标签(写的是if,else,for) <%- include("user/show",{user:user})%> 导入公共的模板内容
MVC框架:
是一种设计模式,是软件架构得模式,是在web开发过程中总结的一些套路或者是模块化的内容。M是指业务模型(module),V是指用户界面(view),C则是控制器(controller)。使用mvc最大的优势就是分层,目的是将M和V的实现代码分离,存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。MVC是相互独立的,M,C(后端); V(前端)。路由规划为Controller。
JSON:
一、概念:
JSON(
J
avaS
criptO
bjectN
otation, JS对象表示法)简单来讲,JSON 就是 Javascript 对象和数组的字符串表示法,因此,JSON 的本质是字符串。
作用:JSON 是一种轻量级的文本数据交换格式,在作用上类似于 XML,专门用于存储和传输数据,但是 JSON 比 XML 更小、更快、更易解析。
现状:JSON 是在 2001 年开始被推广和使用的数据格式,到现今为止,JSON 已经成为了主流的数据交换格式。
二、JSON的两种结构
JSON 就是用字符串来表示 Javascript 的对象和数组。所以,JSON 中包含对象和数组两种结构,通过这两种结构的相互嵌套,可以表示各种复杂的数据结构。
对象结构:对象结构在 JSON 中表示为 { } 括起来的内容。数据结构为 { key: value, key: value, … } 的键值对结构。其中,key 必须是使用英文的双引号包裹的字符串,value 的数据类型可以是数字、字符串、布尔值、null、数组、对象6种类型。
{ "name": "zs", "age": 20, "gender": "男", "hobby": ["吃饭", "睡觉"]}
数组结构:数组结构在 JSON 中表示为 [ ] 括起来的内容。数据结构为 [ “java”, “javascript”, 30, true … ] 。数组中数据的类型可以是数字、字符串、布尔值、null、数组、对象6种类型。
[ 100, 200, 300 ][ true, false, null ][ { "name": "zs", "age": 20}, { "name": "ls", "age": 30} ][ [ "aaa", "bbb", "ccc" ], [ 1, 2, 3 ] ]
三、JSON语法的注意事项
- 属性名必须使用双引号包裹
- 字符串类型的值必须使用双引号包裹
- JSON 中不允许使用单引号表示字符串
- JSON 中不能写注释
- JSON 的最外层必须是对象或数组格式
- 不能使用 undefined 或函数作为 JSON 的值
- JSON 的作用:在计算机与网络之间存储和传输数据
- JSON 的本质:用字符串来表示 Javascript 对象数据或数组数据
四、JSON和JS对象的关系
JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
//这是一个对象var obj = {a: 'Hello', b: 'World'}//这是一个 JSON 字符串,本质是一个字符串var json = '{"a": "Hello", "b": "World"}'
五、JSON和JS对象的互转
- 要实现从 JSON 字符串转换为 JS 对象,使用 JSON.parse() 方法
- 要实现从 JS 对象转换为 JSON 字符串,使用 JSON.stringify() 方法
HTTP
一、概念
HTTP 协议即超文本传送协议 (HyperText Transfer Protocol) ,它规定了客户端与服务器之间进行网页内容传输时,所必须遵守的传输格式。(是一种约定与规则)
二、请求(请求报文)
客户端发起的请求叫做 HTTP 请求,客户端发送到服务器的消息,叫做 HTTP 请求消息,又叫做 HTTP 请求报文。
请求消息的组成
HTTP 请求消息由请求行(request line)、请求头部( header ) 、空行 和 请求体 4 个部分组成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G502I2rT-1658387147224)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\请求消息组成部分.png)]
- 请求行:由请求方式(get,post)、URL (/login?)和 HTTP 协议版本 3 个部分组成,他们之间使用空格隔开。
- 请求头部:用来描述客户端的基本信息,从而把客户端相关的信息告知服务器。请求头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ImfA8U4y-1658387147225)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\请求头部字段.png)]
- 空行:最后一个请求头字段的后面是一个空行,通知服务器请求头部至此结束。请求消息中的空行,用来分隔请求头部与请求体。
- 请求体:请求体中存放的,是要通过 POST 方式提交到服务器的数据。只有 POST 请求才有请求体,GET 请求没有请求体!
三、响应(响应报文)
响应消息就是服务器响应给客户端的消息内容,也叫作响应报文。
响应消息的组成:
HTTP响应消息由状态行、响应头部、空行 和 响应体 4 个部分组成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N0rfsHa8-1658387147225)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\响应消息组成部分.png)]
- 状态行:由 HTTP 协议版本、状态码和状态码的描述文本 3 个部分组成,他们之间使用空格隔开;
- 响应头部:用来描述服务器的基本信息。响应头部由多行 键/值对 组成,每行的键和值之间用英文的冒号分隔。
- 空行:在最后一个响应头部字段结束之后,会紧跟一个空行,用来通知客户端响应头部至此结束。响应消息中的空行,用来分隔响应头部与响应体。
- 响应体:中存放的,是服务器响应给客户端的资源内容。
四、HTTP请求方法
HTTP 请求方法,属于 HTTP 协议中的一部分,请求方法的作用是:用来表明要对服务器上的资源执行的操作。最常用的请求方法是 GET 和 POST。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vwQMQrAp-1658387147226)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\http请求的方法.png)]
五、响应的状态码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28YRkP8m-1658387147227)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\http响应状态码.png)]
Ajax:
一、概念
AJAX 全称为(Asynchronous JavaScript And XML),是异步的JavaScript和XML。通过 ajax可以在浏览器向服务器发送异步请求。最大的优势,
无刷新获取数据
。也就是说AJAX可以在不重新加载整个页面的情况下,与服务器交换数据。这种异步交互的方式,使用户单击后,不必刷新页面也能获取新数据。使用Ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的Web用户界面。
二、XML
- XML 指可扩展标记语⾔,HTML 超文本标记语言
- XML被设计用来传输和存储数据
- XML和HTML类似,不同的是HTML都是预定义的标签,而XML没有预定义标签,全部都是自定义的标签,用来表示一些数据。
最开始是前后台进行数据交互的语言,现在是JSON。
'{"title":"三体","author":"zs","price":30}'<book> <title>三体</title> <author>刘慈欣</author> <price>30.00</price></book>
三、特点
优点:
- 可以无需刷新页面与服务器进行通信
- 允许你根据用户事件来更新部分页面内容
缺点:
- 没有浏览历史不能回退
- 存在跨越问题 同源策略:协议,域名,端口号
- SEO不友好
四、发送请求
- get方式,以及请求参数
// 1.创建对象var xhr=new XMLHttpRequest();// 2.初始化,设置请求方法和urlxhr.open("GET","http://127.0.0.1:3000/server?username=zs&password=1234");// 3.发送xhr.send();// 4.绑定事件,处理服务端返回得结果// readstate xhr得属性 状态 0,1,2,3,4xhr.onreadystatechange=function(){ // 服务器返回得结果 if(xhr.readyState==4){ // 判断响应得状态码 if(xhr.status==200){ // 行,头,体 console.log(xhr.status); console.log(xhr.statusText); console.log(xhr.response); console.log(xhr.getAllResponseHeaders()); } }}
项目:
一、对密码进行加密处理
JavaScript使用
CryptoJS
加解密。CryptoJS
时一个JavaScript的加解密的工具包。它支持多种的算法:MD5、SHA1、SHA2、SHA3、RIPEMD-160 哈希散列,进行 AES、DES、Rabbit、RC4、Triple DES 加解密。在项目中如果要对前后端传输的数据双向加密, 比如避免使用明文传输用户名,密码等数据。 就需要对前后端数据用同种方法进行加密,方便解密。
使用步骤:
- 运行如下命令,安装
CryptoJS
:
npm i crypto-js
- 在
/login.js
中,导入crypto-js
:
const crypto = require('crypto-js')
- 以
ASE
进行纯文本加密:
//加密var a=crypto.AES.encrypt("abc","key").toString(); //SecrectKey 秘钥//解密var b=crypto.AES.decrypt(a,"key").toString(crypto.enc.Utf8); //按照UTF8编码解析出原始字符串
二、表单校验规则
在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且,后端做为数据合法性验证的最后一个关口,在拦截非法数据方面,起到了至关重要的作用。使用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性。
步骤:
- 安装
joi
包,为表单中携带的每个数据项,定义验证规则:
npm install joi
- 安装
@escook/express-joi
中间件,来实现自动对表单数据进行验证的功能:
npm i @escook/express-joi
- 新建
/schema/login.js
用户信息验证规则模块:
const joi = require('joi')/** * string() 值必须是字符串 * alphanum() 值只能是包含 a-zA-Z0-9 的字符串 * min(length) 最小长度 * max(length) 最大长度 * required() 值是必填项,不能为 undefined * pattern(正则表达式) 值必须符合正则表达式的规则 */// 用户名的验证规则const username = joi.string().required();// 密码的验证规则const password = joi.string().pattern(/^[\S]{6,12}$/).required();// 登录表单的验证规则对象exports.login_schema = { // 表示需要对 req.body 中的数据进行验证 body: { username, password, },}
- 修改
/router/admin/login.js
中的代码:
// 1. 导入验证表单数据的中间件const expressJoi = require('@escook/express-joi')// 2. 导入需要的验证规则对象const { login_schema } = require('../../schema/login');// 3登录功能router.post("/",expressJoi(login_schema),(req,res)=>{}];
- 在
index.js
的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端:
const joi = require('joi')// 错误中间件app.use(function (err, req, res, next) { // 数据验证失败 if (err instanceof joi.ValidationError) return res.send(err); // 未知错误 res.send(err)})
三、四种常见的POST提交数据方式
HTTP协议是以ASCII码传输,建立在TCP/IP协议之上的应用层规范。规范把 HTTP 请求分为三个部分:状态行、请求头、消息主体。协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式Content-Type。
服务端根据请求头(headers)中的Content-Type字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以说到POST提交数据方案,包含了Content-Type 和消息主体编码方式两部分。Content-Type的四种值分别代表四种方式,具体如下:
- 方式一:application/x-www-form-urlencoded
浏览器的原生form表单,如果不设置enctype属性,那么最终就会以 application/x-www-form-urlencoded方式提交数据。Content-Type被指定为application/x-www-form-urlencoded提交的数据按照key=val&key=val的方式进行编码,并且key和val都进行了URL转码。服务端例如 PHP 中,使用$_POST[′key′]可以获取到值。
- 方式二:multipart/form-data
常见的POST数据提交的方式。这种方式支持文件上传,不过必须要设置form的enctyped等于这个值。使用multipart/form-data方式会生成了一个boundary 来分割不同的字段,为了避免与正文重复,boundary是一段很长的随机拼接的字符串。然后Content-Type指明数据是以mutipart/form-data来编码并包括进本次请求的boundary 值。消息主体最后以 --boundary–标示结束。
- 方式三:application/json
由于JSON规范的流行,现在越来越多的开发者将application/json这个Content-Type作为响应头。用来告诉服务端消息主体是序列化后的JSON字符串。除了低版本IE之外各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理JSON的函数,JSON能格式支持比键值对复杂得多的结构化数据,普通键值对中的值只能是字符串,而使用json,键值对可以重复嵌套。
- 方式四:text/xml
它是一种使用HTTP作为传输协议,XML作为编码方式的远程调用规范。不过后来使用很少。也许在十多年前,在json还未出来之前数据交互对接。
总之application/x-www-form-urlencoded和multipart/form-data两种POST方式都是浏览器原生支持的,是普遍使用的两种方式,application/json是现在比较流行的新趋势。
四、multer
- 安装multer
npm i multer
- 导入multer
const multer = require('multer')
- 配置multer接收到的文件存储的文件夹和,存放的图片的名字
//上传文件存放路径、及文件命名const storage = multer.diskStorage({ destination: path.join(__dirname ,'../static/uploads'), //确定上传文件的物理位置 filename: function (req, file, cb) { //自定义设置文件的名字,根据上传时间的时间戳来命名 let type = file.originalname.split('.')[1] cb(null, `${file.fieldname}-${Date.now().toString(16)}.${type}`) }})
1.sotrage是一个配置对象。他是通过multer.diskstorage存储引擎生成的。multer.diskstorage需要传递一个配置对象,这里的配置对象里面接收两个参数。第一个参数为destination,它的值为一个路径字符串,代表的含义是当multer处理完成前端传递过来的文件之后要把这个文件存储在哪里。这个文件夹无需手动创建,在接受文件的时候会自动创建。这里建议使用path模块进行拼接,不容易出错。第二个参数为一个回调函数,这里形参要用三个进行占位。multer中间件在解析前端提交的文件的时候会调用这个方法,调用的时候会给这个filename指向的函数传递三个参数,第二个值为前端传递过来文件信息,第三个参数cb为一个函数,cb函数调用的第二个参数指定的就是当前解析完成后的保存到destination指向的目录的文件名。
- 应用这个配置到multer实例里面
const upload = multer({storage});
- 在需要接收文件的路由里面应用upload.single(‘file’)中间件
(1).这个file是前端提交表单过来的时候表单的字段名称。(2).upload是用multer这个库里的顶级构造函数生成的实例。
- 总体思路梳理:
实际网页开发当中,我们前端需要向后端提交一些像mp4,mp3,图片系列的东西,需要在后端进行接收。那么这里就可以使用Multer中间件来接收文件,对前端传递过来的文件做一些处理。
multer是啥? Multer是Express官方推出的,用于node.js 处理前端以multipart/form-data请求数据处理的一个中间件。注意: Multer 不会处理任何非 multipart/form-data 类型的表单数据
原理: Multer实例的single(‘###’) 是一个方法,这个方法被当作中间件放在某一个路由上时。就会给express 的 request 对象中添加一个 body 对象 以及 file 或 files 对象 。 body 对象包含表单的文本域信息,file 或 files 对象包含对象表单上传的文件信息。下图就是req.file的模样。当前端请求后台接口。匹配当前路由的时候,先经过这个multer中间件,这个中间件就会解析前端传过来的文件,会把文件保存在上面第三步配置文件解析完成后的文件夹内,名字也会重命名成上面第三步配置文件解析完成的文件名。同时,会在req的身上挂载一个file对象。这个file对象就是当前上传文件的信息。我们可以通过req.file.filename拿到这个重命名后的文件名,然后把这个文件名保存到数据库里面。前端如果想访问之前上传的图片,后台只需要把数据库里的文件名取到,随后映射成我们请求的路径,去请求public静态资源下的存放这些文件的文件夹就可以了。
multer是一个中间件,我建议把这个中间件写成一个单独的文件,最后把配置好的multer实例暴露出去,在需要他的路由里面当作中间件去应用它。就可以很快捷的处理前端发送过来的文件。而无需每个文件都写一遍。也更加符合我们模块化编程的思想。下图是我multer文件的配置。
五、富文本编辑器
富文本编辑器,Multi-function Text Editor, 简称 MTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器。它提供类似于 Microsoft Word 的编辑功能,容易被不会编写 HTML 的用户并需要设置各种文本格式的用户所喜爱。
富文本编辑器不同于文本编辑器,程序员可到网上下载免费的富文本编辑器内嵌于自己的网站或程序里(当然付费的功能会更强大些),方便用户编辑文章或信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9D1VzMYl-1658387147228)(C:\Users\11933\Desktop\前端全栈\课件\前后台交互\imgs\富文本编辑器.png)]
layui 富文本编辑器(layedit):
使用:
<textarea id="demo" style="display: none;"></textarea>layui.use(['layedit'],function(){ layedit.build("demo")})
layedit基础的方法:
方法名 描述 var index = layedit.build(id, options) 用于建立编辑器的核心方法index:即该方法返回的索引参数 id: 实例元素(一般为textarea)的id值参数 options:编辑器的可配置项,下文会做进一步介绍 layedit.set(options) 设置编辑器的全局属性即上述build方法的options layedit.getContent(index) 获得编辑器的内容参数 index: 即执行layedit.build返回的值 layedit.getText(index) 获得编辑器的纯文本内容参数 index: 同上 layedit.sync(index) 用于同步编辑器内容到textarea(一般用于异步提交)参数 index: 同上 layedit.getSelection(index) 获取编辑器选中的文本参数 index: 同上 编辑器属性设置:
属性 类型 描述 tool Array 重新定制编辑器工具栏,如: tool: [‘link’, ‘unlink’, ‘face’] hideTool Array 不显示编辑器工具栏,一般用于隐藏默认配置的工具bar height Number 设定编辑器的初始高度 uploadImage Object 设定图片上传接口,如:uploadImage: {url: ‘/upload/’, type: ‘post’} 富文本编辑器工具栏:
let richtextInex = layedit.build('richtext', { tool: [ 'strong' //加粗 , 'italic' //斜体 , 'underline' //下划线 , 'del' //删除线 , '|' //分割线 , 'left' //左对齐 , 'center' //居中对齐 , 'right' //右对齐 , 'image' //插入图片 ], uploadImage:{url:'/uploadrichtext',type:'POST'} })
六、express-session
(一)、Session简单介绍
session 是另一种记录客户状态的机制,不同的是 Cookie 保存在客户端浏览器中,而 session 保存在服务器上。Session的用途:
session运行在服务器端,当客户端第一次访问服务器时,可以将客户的登录信息保存。
当客户访问其他页面时,可以判断客户的登录状态,做出提示,相当于登录拦截。
session可以和Redis或者数据库等结合做持久化操作,当服务器挂掉时也不会导致某些客户信息(购物车)
丢失。
(二)、Session的工作流程
当浏览器访问服务器并发送第一次请求时,服务器端会创建一个session对象,生成一个类似于
key,value的键值对,然后将key(cookie)返回到浏览器(客户)端,浏览器下次再访问时,携带key(cookie),
找到对应的session(value)。 客户的信息都保存在session中。
(三)、Cookie和Session区别:
cookie 数据存放在客户的浏览器上,session 数据放在服务器上。
cookie 不是很安全,别人可以分析存放在本地的 COOKIE 并进行 COOKIE 欺骗 考虑到安全应当使用 session。
session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能 考虑到减轻服务器性能方面,应当使用 COOKIE。
单个 cookie 保存的数据不能超过 4K,很多浏览器都限制一个站点最多保存 20 个 cookie。
(四)、express-session的使用
安装 express-session:
npm install express-session
引入express-session:
var session = require("express-session");
设置官方文档提供的中间件:
app.use(session({ secret: 'keyboard cat', resave: true, saveUninitialized: true }))
使用:
设置值 req.session.username = "张三"; 获取值 req.session.username
express-session的常用参数:
1. name - cookie的名字(原属性名为 key)。(默认:’connect.sid’)2. store - session存储实例3. secret - 用它来对session cookie签名,防止篡改4. cookie - session cookie设置 (默认:{ path: ‘/‘, httpOnly: true,secure: false, maxAge: null })5. genid - 生成新session ID的函数 (默认使用uid2库)6. rolling - 在每次请求时强行设置cookie,这将重置cookie过期时间(默认:false)7. resave - 强制保存session即使它并没有变化 (默认: true)8. proxy - 当设置了secure cookies(通过”x-forwarded-proto” header )时信任反向代理。当设定为true时, ”x-forwarded-proto” header 将被使用。当设定为false时,所有headers将被忽略。当该属性没有被设定时,将使用Express的trust proxy。9. saveUninitialized - 强制将未初始化的session存储。当新建了一个session且未设定属性或值时,它就处于 未初始化状态。在设定一个cookie前,这对于登陆验证,减轻服务端存储压力,权限控制是有帮助的。(默认:true)10. unset - 控制req.session是否取消(例如通过 delete,或者将它的值设置为null)。这可以使session保持
- express-session的常用方法:
1. Session.destroy():删除session,当检测到客户端关闭时调用。2. Session.reload():当session有修改时,刷新session。3. Session.regenerate():将已有session初始化。4. Session.save():保存session。
- 使用案例:
//配置中间件app.use(session({ secret: 'this is string key', // 可以随便写。一个 String 类型的字符串,作为服务器端生成 session 的签名 name:'session_id',/*保存在本地cookie的一个名字 默认connect.sid 可以不设置*/ resave: false, /*强制保存 session 即使它并没有变化,。默认为 true。建议设置成 false。*/ saveUninitialized: true, //强制将未初始化的 session 存储。 默认值是true 建议设置成true cookie: { maxAge:5000 /*过期时间*/ }, /*secure https这样的情况才可以访问cookie*/ //设置过期时间比如是30分钟,只要游览页面,30分钟没有操作的话在过期 rolling:true //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)}))
七、表单事件
- Form 对象属性:
属性 | 描述 |
---|---|
action | 接收请求的URL |
elements | 表单中的所有控件元素集合 |
length | 表单控件的个数 |
enctype | 编码类型 例:enctype=“multipart/form-data” |
name | 表单元素名称 |
- Form 对象方法:
方法 | 描述 |
---|---|
reset() | 把表单的所有输入元素重置为它们的默认值。 |
submit() | 提交表单。 |
- Form 对象事件:
事件 | 描述 |
---|---|
onreset | 在重置表单元素之前调用。 |
onsubmit | 在提交表单之前调用。 |
- 表单控件的属性:
属性 | 描述 |
---|---|
value | 获取和设置值 |
disabled | 获取或设置表单控件是否禁用值为true或 false |
type | 读取表单控件的类型 |
form | 所在表单元素对象 |
readOnly | 控件只读属性 Boolean 不能更改只能复制和读取 |
name | 获取与设置name字段名 |
- 表单控件的事件:
事件 | 描述 |
---|---|
onblur | 当失去焦点的时候 |
onfocus | 当获取焦点的时候 |
onchange | 当内容改变并失去焦点的时候 |
oninput | 在用户输入时触发 |
- 表单控件的方法:
方法 | 描述 |
---|---|
focus() | 获得焦点 |
blur() | 失去焦点 |
select() | 选择文本控件中的所有文本内容 |
以上就是总结Node.js模块开发及常用技巧分享的详细内容,更多请关注其它相关文章!