NodeJs03 express框架 Todo商城
前言
由于NodeJs本身的异步非阻塞特性和对http的天然支持,所以使用NodeJs编写高性能,可伸缩的Web服务器非常简单。开发完整的Web服务器还需要路由,错误处理,请求拦截,请求和响应的解析,模板引擎等功能,所以直接使用NodeJs的http模块开发起来还是挺痛苦的。
目前有很多的Web框架都是基于http模块封装而成,最流行的当属Express框架。
学习资源:
快速开始
npm install express --save
快速开启服务器:
const express = require('express');
const app = express();
app.get('/', function(req, res){
res.send('hello world');
});
app.listen(3000);
静态文件
express提供了托管静态文件的功能,比如html,图片,CSS,JavaScript等文件。
app.use(express.static('public'));
支持指定挂载路径:
app.use('/static', express.static('public'));
一般框架提供的静态文件托管功能,只是在开发环境使用。在生产环境,推荐交给反向代理服务器来管理,比如Nginx;最佳的做法是花点钱交给CDN提供商来服务。
路由
路由就是根据用户请求的url路径,交由对应的响应方法处理。
请求方法
app.get('/', function (req, res) {
res.send('GET request to the homepage');
});
// POST method route
app.post('/', function (req, res) {
res.send('POST request to the homepage');
});
路径匹配
// 匹配根路径
app.get('/', function (req, res) {
res.send('root');
});
// 匹配 /user 路径的请求
app.get('/user', function (req, res) {
res.send('user');
});
// 匹配 /xxx 路径的请求
app.get('/xxx', function (req, res) {
res.send('xxx');
});
支持简单的字符串匹配:
// 匹配 acd 和 abcd
app.get('/ab?cd', function(req, res) {
res.send('ab?cd');
});
// 匹配 abcd、abbcd、abbbcd等
app.get('/ab+cd', function(req, res) {
res.send('ab+cd');
});
// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等
app.get('/ab*cd', function(req, res) {
res.send('ab*cd');
});
也支持完全的正则方式,但是不能用字符串了。
// 匹配以abc开头的所有字符
app.get(/abc[0-9a-zA-Z]*/, function(req, res) {
res.send('ab?cd');
});
模块化路由
模块化路由是指我们可以将一组具有业务相关性的路由封装为一个router对象,然后挂载到指定路径。
const express = require('express');
const router = express.Router();
// 该路由使用的中间件
router.use(function timeLog(req, res, next) {
console.log('Time: ', Date.now());
next();
});
// 定义模块的根路由
router.get('/', function(req, res) {
res.send('auth模块根路由');
});
// 定义登录
router.get('/login', function(req, res) {
res.send('login');
});
module.exports = router;
将router挂载到指定路径:
const auth = require('./auth');
...
app.use('/auth', auth);
中间件
中间件就是整个请求——>响应流程中的一系列处理函数。路由也是属于中间件,是挂载了路径的中间件。
使用中间件有两点注意:
- next() 方法和 res.send() 必须调用一个,否则请求就会挂起,客户端等待响应。
- 中间件的顺序很重要,它是按照你注册的顺序执行的。
应用程序级别中间件
const app = express();
// 没有挂载路径的中间件,应用的每个请求都会执行该中间件
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
// 挂载至 /user/:id 的中间件,任何指向 /user/:id 的请求都会执行它
app.use('/user/:id', function (req, res, next) {
console.log('Request Type:', req.method);
next();
});
路由级别中间件
路由级中间件和应用级中间件一样,只是它绑定的对象为 express.Router()
。
const router = express.Router();
// 没有挂载路径的中间件,通过该路由的每个请求都会执行该中间件
router.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});
// 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息
router.use('/user/:id', function(req, res, next) {
console.log('Request URL:', req.originalUrl);
next();
}, function (req, res, next) {
console.log('Request Type:', req.method);
next();
});
// 一个中间件栈,处理指向 /user/:id 的 GET 请求
router.get('/user/:id', function (req, res, next) {
// 如果 user id 为 0, 跳到下一个路由
if (req.params.id == 0) next('route');
// 负责将控制权交给栈中下一个中间件
else next(); //
}, function (req, res, next) {
// 渲染常规页面
res.render('regular');
});
module.exports = router;
挂载router
const app = express();
app.use('/', router);
错误处理
程序总会遇到错误,比如:传参错误,我们不小心调用了undifined
,或者我们自己也可能抛出异常。当发生这些错误的时候我们需要给用户一个友好的回应,这就是错误处理。
定义错误处理中间件和定义其他中间件一样,除了需要 4 个参数,而不是 3 个,其格式如下 (err, req, res, next)
。例如:
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('出错了!');
});
错误中间件返回的响应是随意的,可以响应一个 HTML 错误页面、一句简单的话、一个 JSON 字符串,或者其他任何您想要的东西。
在其他 app.use()
和路由调用后,最后定义错误处理中间件,比如:
app.use(bodyParser());
app.use(router);
app.use(function(err, req, res, next) {
// ...
});
模板引擎
Html页面本身是静态的,不含动态数据的。模板引擎的作用就是将从数据库读出来的,处理好的数据填充到html页面,然后返回给用户。
express支持很多的模板引擎,每个模板引擎都有自己的语法。这里以jade为例。
需要安装相应的模板引擎 npm 软件包。
npm install jade --save
设置模板的目录,和当前使用的模板引擎
app.set('views', './views') //设置模板存放的目录
app.set('view engine', 'jade'); //设置使用的引擎
在 views
目录下生成名为 index.jade
的 Jade 模板文件,内容如下:
html
head
title=title
body
h1=message
然后创建一个路由渲染 index.jade
文件。
app.get('/', function (req, res) {
res.render('index', { title: 'Hey', message: 'Hello there!'});
});
index.jade
文件会被引擎渲染为html文件,然后发送给用户。jade引擎的更多使用请参考它的官方文档,这里只做简单演示,原因如下:
现代Web开发的模式是逐渐趋向于前后端分离,前端的人负责写Html页面,后台的人写API服务,前端数据通过AJAX得到。这样的好处是开发效率高,沟通成本降低。加上Vue和React等具有高性能和高效率的前端渲染引擎的出现,后端的模板引擎的几乎没有用武之地了。但不排除某些公司的技术人员仍然顽固地选择后端渲染。
WebApp进程管理
在生产环境我们不会使用node app.js
的方式来开启程序,因为我们有这样的一些需求:
- 如果进程崩溃了,需要重启
- 监控当前进程的CPU使用率和内存占用
- 搭建集群
进程管理器就能提供上面的功能,让我们的NodeJs进程永不退出。最流行的进程管理器有3个:
其中PM2除了提供上面的功能外,还可以监视程序的日志,错误提醒,并且内置了负载均衡器,界面还友好。所以我们选择使用PM2。
首先,安装。
npm install pm2 -g
启动程序:
pm2 start app.js
其他常用命令:
pm2 list
pm2 stop app
pm2 reload app
pm2 start xxx.js -i 4
学习资源
第三方中间件:http://www.expressjs.com.cn/resources/middleware.html
API查询:http://www.expressjs.com.cn/4x/api.html
TODO 后台项目
项目介绍
完成对TODO事项的增删改查功能,数据库使用mongodb。
安装依赖
创建项目,新建package.json
文件。
npm i express -S
npm i mongoose -S
npm i morgan -S // 打印log的类库
npm i express-async-errors -S // 用来捕获promise错误
由于express 4x
为了精简框架,移除了很多内置的中间件,比如body-parser
。所以为了能够解析body参数,需要额外安装这个模块。
npm i body-parser -S
项目结构搭建
整个项目结构,仍然是基于MVC架构来分层。
service包:存放所有的业务逻辑类,相当于service层
router包:存放所有的路由,相当于controller层
util包:存放所有的帮助类
model包:存放所有的mongodb模型类,相当于dao层
config.js
:配置文件db.js
:数据库入口文件app.js
:app入口文件
编写项目逻辑
依次编写:
app.js
和config.js
'use strict'
// 连接数据库
require('./db') const config = require('./config')
const bodyParser = require('body-parser');
const morgan = require('morgan');
const express = require('express')
// 引入express异步异常捕获模块
require('express-async-errors');
const app = express(); // 注册log中间件
app.use(morgan('combined')); // 注册body解析中间件
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json()); // 注册路由
app.use('/todos', require('./router/todo')); // 注册错误处理中间件
app.use(function (err, req, res, next) {
console.log(err);
res.send({
code: -1,
msg: err.toString()
})
}); app.listen(config.PORT);
'use strict'
const config = {
PORT: 3000,
DB: 'TODO',
} module.exports = config
router
'use strict' const router = require('express').Router()
const todoController = require('../controller/todo') module.exports = router router.get('/', async (req, res)=>{
let todos = await todoController.getAllTodo();
res.send({
code: 0,
data: todos
})
}); router.post('/', async (req, res)=>{
await todoController.addTodo(req.body)
res.send({code: 0})
}); router.put('/:id', async (req, res)=>{
await todoController.updateTodo(req.params.id, req.body)
res.send({code: 0})
}); // 删除某个todo
router.delete('/:id', async (req, res)=>{
await todoController.deleteTodo(req.params.id)
res.send({code: 0})
});
db.js
和 model'use strict' const config = require('./config')
const mongoose = require('mongoose') mongoose.connect(`mongodb://127.0.0.1/${config.DB}`) const db = mongoose.connection db.on('error', err=>{
console.log(err);
});
db.on('open', ()=>{
console.log('db connect successful!');
});
'use strict'
// To-do: 模型类, 内容,是否完成,创建日期
const mongoose = require('mongoose') const schema = new mongoose.Schema({
content: String,
isDone: {
type:Boolean,
default: false
},
created: {
type: Date,
default: Date.now()
}
}); module.exports = mongoose.model('todo', schema);
controller
'use strict' const Todo = require('../model/todo') // 取出数据库中的所有todo,然后返回
async function getAllTodo() {
return await Todo.find({})
} async function updateTodo(id, update) {
await isIdExist(id) // { n: 1, nModified: 1, ok: 1 }
let res = await Todo.updateOne({_id:id}, update)
if(!res || res.n<1){
throw `${update}更新失败`
}
} async function addTodo(todo) {
// 先查询名字是否存在
let t = await Todo.findOne({content: todo.content})
if(t){
//说明已经存在,则抛出异常,由异常处理中间件负责处理
throw `${todo.content} 已经存在`
}
await Todo.create(todo)
} async function isIdExist(id) {
// 先判断传入的id是否存在
let t = await Todo.findOne({_id: id})
if(!t){
throw `${id}不存在`
}
} async function deleteTodo(id) {
await isIdExist(id) // { n: 1, ok: 1 }
let res = await Todo.deleteOne({_id:id})
console.log(res);
if(!res || res.n < 1){
throw `${id}删除失败`
}
} module.exports = {
getAllTodo, updateTodo, addTodo, deleteTodo
}
API 测试
使用postman进行测试每个接口。
NodeJs03 express框架 Todo商城的更多相关文章
- Vue nodejs商城项目-搭建express框架环境
1.express-project 搭建express框架环境 安装express generator生成器 通过生成器自动创建项目 配置分析 安装 cnpm i -g express-generat ...
- 第一天ci框架开发商城2
ci框架开发商城2 1/28/2016 9:45:52 PM mvc完整案例 mvc完成新闻的增删改查 news控制器news.php class News extends CI_controller ...
- 第一天ci框架开发商城1
ci框架开发商城1 1/28/2016 9:43:52 PM userguide删除 system application controllers 控制器 models 模型 views 视图 模板 ...
- Node.js、Express框架获取客户端IP地址
Node.js //传入请求HttpRequest function getClientIp(req) { return req.headers['x-forwarded-for'] || req.c ...
- Win8.1 安装Express 框架
1.安装Windows Node.js客户端 2.安装Express框架 我本机是Win8.1的,使用命令npm install -g express安装Express,安装完成后显示一些安装明细,刚 ...
- Node.js Express 框架学习
转载:http://JavaScript.ruanyifeng.com/nodejs/express.html#toc0 感觉很牛的样子,不过觉得对初学者没太大用,里面很多例子用的api都没有详细的说 ...
- Node.js Express 框架
Node.js Express 框架 Express 简介 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP ...
- express框架路由配置及congtroller自动加载
express框架在node官方推荐的一个框架,关于如何入门的文章,已经很多了,我就不在累赘了,本文的核心是如何修改文件使得更接近一个MVC的框架 express原生是通过require的方式实现了模 ...
- nodejs学习笔记二:解析express框架项目文件
上一章介绍了如何去创建一个express框架的工程项目,这章介绍一下express框架下的文件和用法解析,上一张我们创建的工程项目结构图如下: models是不属于原工程项目结构,为了实现数据模型后添 ...
随机推荐
- LeetCode804. Unique Morse Code Words
题目 国际摩尔斯密码定义一种标准编码方式,将每个字母对应于一个由一系列点和短线组成的字符串, 比如: "a" 对应 ".-", "b" 对应 ...
- Spring 中IOC(控制反转)&& 通过SET方式为属性注入值 && Spring表达式
### 1. Spring IoC IoC:Inversion of control:控制反转:在传统开发模式下,对象的创建过程和管理过程都是由开发者通过Java程序来实现的,操作权在开发者的Java ...
- Docker自学纪实(四)搭建LNMP部署wordpress
我们在工作中最常用的就是LNMP网站平台 这个架构呢,是整个公司网站的核心 如果对于访问量较小的网站,可以直接在服务器上面部署 而如果是访问量很大的网站,那负载就是个很大的问题. 要么需要再买很多服务 ...
- Docker自学纪实(三)Docker容器数据持久化
谈起数据卷 我一直觉得是个枯燥无聊的话题 但是通过今天的实操和阅读 我发现其实并不是 其实就像走夜路 没有光明,第一次都是恐惧 但是如果走的次数多了 或者静下心来去克制恐惧 也许就会驾轻就熟或者等到黎 ...
- node服务端渲染(完整demo)
简介 nodejs搭建多页面服务端渲染 技术点 koa 搭建服务 koa-router 创建页面路由 nunjucks 模板引擎组合html webpack打包多页面 node端异步请求 服务端日志打 ...
- 【c学习-1】
#include<stdio.h> int main(){ int a,b,max; printf("请输入两个整数:"); //格式化输出函数 scanf(" ...
- C语言字符篇(一)字符串转换函数
#include <stdlib.h> double atof(const char *nptr); 将字符串转换成双精度浮点数 int atoi(const char *npt ...
- 机器学习实战 -- 决策树(ID3)
机器学习实战 -- 决策树(ID3) ID3是什么我也不知道,不急,知道他是干什么的就行 ID3是最经典最基础的一种决策树算法,他会将每一个特征都设为决策节点,有时候,一个数据集中,某些特征属 ...
- 第三章:Web表单
感谢作者 –> 原文链接 本文翻译自 The Flask Mega-Tutorial Part III: Web Forms 这是Flask Mega-Tutorial系列的第三部分,我将告诉你 ...
- 20145202马超 《Java程序设计》 实验一 实验报告
一.实验内容 1.使用JDK编译.运行简单的Java程序: 2.使用Eclipse 编辑.编译.运行.调试Java程序. 二.实验内容 1.完成在Windows环境下对IDEA的配置: 2.实现求正整 ...