您的位置:首页 > Web前端 > Node.js

前端学习 node 快速入门 系列 —— 报名系统 - [express]

2021-04-03 20:46 1096 查看

其他章节请看:

前端学习 node 快速入门 系列

报名系统 - [express]

最简单的报名系统:

  • 只有两个页面
  • 人员信息列表页:展示已报名的人员信息列表。里面有一个
    报名
    按钮,点击按钮则会跳转到报名页
  • 报名页:用于报名。里面是一个表单,可以输入姓名和年龄,点击
    保存
    ,成功后会跳转到人员信息列表页

本文主要分 3 部分:

  1. 使用 node 实现这个项目
  2. 介绍 express 相关知识
  3. 使用 express 重写这个项目

Tip: 有将本文分成两篇的打算,因为篇幅有点长;但最后还是决定写在一起,因为更加紧凑。

node 实现

目录如下:

- demo
- public          // 存放静态资源
- css
- global.css
- views           // 存放模板
- add.html      // 报名页
- list.html     // 列表页
- index.js        // 入口文件
- package.json    // PS: 自己安装依赖包

global.css:

body{color:red;}

add.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/submit" method='get'>
<p><input type="text" name='name'></p>
<p><input type="text" name='age'></p>
<p><input type="submit" value='保存'></p>
</form>
</body>
</html>

list.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/public/css/global.css">
</head>
<body>
<p><a href="/add">报名</a></p>
<section>
{{each rows}}
<li>{{$value.name}} {{$value.age}}</li>
{{/each}}
</section>
</body>
</html>

index.js:

// 模拟数据库
const DB = [
{name: 'ph', age: '18'},
{name: 'lj', age: '19'}
]
const http = require('http')
const fs = require('fs')
const template = require('art-template')

http.createServer(function(req, res){
const url = req.url
// URL模块的 WHATWG API。Constructor: new URL(input[, base])
// api: https://nodejs.org/dist/v12.18.1/docs/api/url.html#url_constructor_new_url_input_base
const urlObj = new URL(url, `https://${req.headers.host}`)
// 列表页面 list.html
if(url === '/'){
fs.readFile('./views/list.html', (err, data) => {
if (err) throw err;
const ret = template.render(data.toString(), {
rows: DB
});
res.end(ret)
})
// 留言页面 add.html
}else if(url.indexOf('/add') === 0){
fs.readFile('./views/add.html', (err, data) => {
if (err) throw err;
res.end(data)
})
// 提交留言
}else if(urlObj.pathname === '/submit'){
// 插入数据
const row = {}
row.name = urlObj.searchParams.get('name')
row.age = urlObj.searchParams.get('age')
DB.unshift(row);
// 临时重定向
res.statusCode = '302'
res.setHeader('Location', '/');
res.end()
}else if(urlObj.pathname.endsWith('.css')){
fs.readFile('./' + url, (err, data) => {
if (err) throw err;
res.end(data)
})
}else{
res.end('404')
}

}).listen(3000)

package.json:

  • 可以先在 demo 路径下执行
    npm init -y
    来帮助我们生成 package.json 文件
  • 接着执行
    npm install art-template
    安装插件即可
{
...
"dependencies": {
"art-template": "^4.13.2"
}
}

运行程序:

$ cd demo

// 自行安装依赖包: npm install

// 启动服务 - 前文已介绍笔者使用 nodemon 来代替 node 启动服务
$ nodemon index

浏览器访问

http://localhost:3000/
,进入列表页(list.html),页面显示:

报名

ph 18
lj 19

注:如果 node 控制台报错,则需要你根据错误提示修改一下,比如你把文件夹 views 一不小心写成了 view。

点击

报名
,进入报名页面(add.html),显示一个表单,输入名字(pm)和年龄(22),点击
保存
,则会重定向到人员信息列表页,页面显示:

报名

pm 22
ph 18
lj 19

至此,这个简单的项目就已经完成。

接下来用 express 框架重写该项目之前,我们得先介绍一下 express 相关的知识。

express 基础知识

笔者通过 express 中文网 来介绍 express。这类技术网站称之为 cooking(烹饪) 网站。好比教我们如何烹饪,得先买菜(安装),然后放油、放葱姜蒜,爆炒1分钟...,一步一步告诉我们怎么做,相对比较简单。

进入 express 中文网,导航的菜单如下:

  • 首页
  • 快速入门 安装
  • hello-world
  • 基本路由
  • 静态文件
  • FAQ
  • ...
  • 指南
      路由
    • 开发中间件
    • 使用模板引擎
    • 集成数据库
  • API参考手册
  • ...
  • Tip: 主要介绍 express 重写报名系统需要用到的知识点,更多细节请参考 express 官网

    首页

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

    快速入门 - 安装

    $ npm install express

    快速入门 - hello-world

    const express = require('express')
    const app = express()
    const
    ad8
    port = 3000
    
    app.get('/', (req, res) => {
    res.send('Hello World!')
    })
    
    app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
    })

    启动服务,访问

    http://localhost:3000/
    页面会输出
    Hello World!
    ;如果访问其他路径(例如
    http://localhost:3000/a
    ),则会以 404 响应。

    快速入门 - 基本路由

    路由是指确定应用程序如何响应客户端对特定端点的请求,该特定端点是URI(或路径)和特定的HTTP请求方法(GET,POST等)。

    语法:

    app.METHOD(PATH, HANDLER)

    以下定义了 4 个路由,请看示例:

    app.get('/', function (req, res) {
    res.send('Hello World!')
    })
    
    app.post('/', function (req, res) {
    res.send('Got a POST request')
    })
    
    app.put('/user', function (req, res) {
    res.send('Got a PUT request at /user')
    })
    
    app.delete('/user', function (req, res) {
    res.send('Got a DELETE request at /user')
    })

    快速入门 - 静态文件

    利用 Express 托管静态文件。

    为了提供诸如图像、CSS 文件和 JavaScript 文件之类的静态文件,请使用 Express 中的 express.static 内置中间件函数。

    语法:

    express.static(root, [options])

    如果需要将 public 目录下的图片、CSS 文件、JavaScript 文件对外开放,下面两种方式都可以。

    方式1:

    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/hello.html

    方式2:

    app.use('/static', express.static('public'))
    
    // 现在,你就可以通过带有 /static 前缀地址来访问 public 目录中的文件了。
    
    http://localhost:3000/static/images/kitten.jpg
    http://localhost:3000/static/css/style.css

    Tip:提供给express.static函数的路径是相对于您启动节点进程的目录的。 如果从另一个目录运行Express App,则使用要提供服务的目录的绝对路径更为安全:

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

    更多关于 __dirname,请看本文

    path 模块
    章节

    快速入门 - FAQ

    如何处理 404 响应?

    app.use(function (req, res,
    ad0
    next) {
    res.status(404).send("Sorry can't find that!")
    })

    如何设置一个错误处理器?

    app.use(function (err, req, res, next) {
    console.error(err.stack)
    res.status(500).send('Something broke!')
    })

    如何渲染纯 HTML 文件?
    不需要!无需通过 res.render() 渲染 HTML。 你可以通过 res.sendFile() 直接对外输出 HTML 文件。 如果你需要对外提供的资源文件很多,可以使用 express.static() 中间件。

    指南 - 路由

    路由路径匹配 acd 和 abcd:

    app.get('/ab?cd', function (req, res) {
    res.send('ab?cd')
    })

    路由参数:

    Route path: /users/:userId/books/:bookId
    Request URL: http://localhost:3000/users/34/books/8989
    req.params: { "userId": "34", "bookId": "8989" }
    })

    express 提供了一些响应方法,res.end()、res.redirect()、res.render()、res.sendStatus()...,可以将响应发送到客户端,并终止请求-响应周期。 如果没有从路由处理程序中调用这些方法,则客户端请求将被挂起。

    指南 - 开发中间件

    从接收请求,到发送响应,我们可以加入各种中间件来做一些处理。中间件又可以传给下一个中间件处理。下面我们定义了一个 myLogger 的中间件,请看示例:

    var express = require('express')
    var app = express()
    
    var myLogger = function (req, res, next) {
    console.log('LOGGED')
    next() // {1}
    }
    
    // 使用中间件
    app.use(myLogger)
    
    app.get('/', function (req, res) {
    res.send('Hello World!')
    })
    
    app.listen(3000)

    每次请求,node 都会输出

    LOGGED
    。如果将 next() (行{1})注释,再次请求,页面将一直转圈圈,因为响应被挂起了。

    指南 - 使用模板引擎

    笔者使用的模板引擎是前文已使用过的 art-template。打开 art-template 官网,点击 Express 菜单就能看到该模板在 express 中使用的方法。请看:

    npm install express-art-template
    
    var express = require('express');
    var app = express();
    
    // view engine setup
    app.engine('art', require('express-art-template')); // {20}
    
    app.set('views', path.join(__dirname, 'views'));
    
    // routes
    app.get('/', function (req, res) {
    res.render('index.art', {                       // {21}
    user: {
    name: 'aui',
    tags: ['art', 'template', 'nodejs']
    }
    });
    });
    15b0

    模板文件默认是 .art,可以改成 .html,只需要将 art(行{20}和行{21}) 改为 html 即可。

    指南 - 集成数据库

    MongoDB

    Mongoose

    Tip: 后续将会使用 Mongoose 依赖包来将 MongoDB 数据库加入我们的项目。

    API参考手册

    由于我的下载的 express 是 4.17.1,所以我参考的 API 是 4.x。

    req.body - 包含在请求正文中提交的数据的键值对。 默认情况下,它是未定义的,并且在使用诸如 body-parser 和 multer 之类的 body-parsing 中间件时填充。请看示例:

    var express = require('express')
    
    var app = express()
    
    app.use(express.json()) // for parsing application/json
    app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
    
    app.post('/profile', function (req, res, next) {
    console.log(req.body)
    res.json(req.body)
    })

    express 重写

    在 node 实现的项目(demo)的基础上,共 3 处变化: add.html、index.js 和 package.json。

    1、add.html:

    method='get'
    改为
    method='post'

    2、index.js:

    const path = require('path')
    const express = require('express')
    const app = express()
    // 填充 req.body
    app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
    
    // view engine setup
    app.engine('html', require('express-art-template'));
    // 可以通过下面语句更改模板视图的文件夹,默认是 views。
    // app.set('views', path.join(__dirname, 'views'));
    // 模拟数据库
    const DB = [
    {name: 'ph', age: '18'},
    {name: 'lj', age: '19'}
    ];
    const port = 3000
    // 将静态资源对外开放
    app.use('/public', express.static('public'))
    
    app.get('/', function (req, res) {
    res.render('list.html', {
    rows: DB
    });
    });
    
    app.get('/add', function (req, res) {
    res.render('add.html', {
    rows: DB
    });
    });
    
    app.post('/submit', function (req, res) {
    const row = {}
    row.name = req.body.name
    row.age = req.body.age
    DB.unshift(row);
    res.redirect('/')
    });
    app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
    })
    
    // 处理 404 响应
    app.use(function (req, res, next) {
    res.status(404).send("404")
    })

    3、package.json(即依赖包的变化):

    {
    ...
    "dependencies": {
    "express": "^4.17.1",
    "express-art-template": "^1.0.1"
    }
    }

    运行的效果和用node写的一样,但编码更优雅。

    path 模块

    路径模块提供了用于处理文件和目录路径的实用程序。 可以使用以下命令访问它:

    const path = require('path');

    path.basename() 方法返回路径的最后一部分。尾部目录分隔符将被忽略。请看示例:

    > path.basename('/foo/bar/baz/asdf/quux.html');
    quux.html
    > path.basename('/foo/bar/baz/asdf/quux.html', '.html');
    quux
    > path.basename('/foo/bar/baz/asdf/');
    asdf

    path.dirname() 方法返回路径的目录名称。尾部目录分隔符将被忽略。请看示例:

    > path.dirname('/foo/bar/baz/asdf/quux');
    /foo/bar/baz/asdf

    path.extname(path) 返回扩展名

    > path.extname('index.html');
    .html
    > path.extname('index.coffee.md');
    .md
    > path.extname('index.');
    .
    > path.extname('index');
    ''

    path.parse() 方法返回一个对象,该对象的属性表示路径的重要元素。请看示例:

    > path.parse('/home/user/dir/file.txt');
    {
    root: '/',
    dir: '/home/user/dir',
    base: 'file.txt',
    ext: '.txt',
    name: 'file'
    }
    > path.parse('C:\\path\\dir\\file.txt');
    {
    root: 'C:\\',
    dir: 'C:\\path\\dir',
    base: 'file.txt',
    ext: '.txt',
    name: 'file'
    }

    path.join() 方法使用特定于平台的分隔符作为分隔符,将所有给定的路径段连接在一起,然后对结果路径进行规范化。请看示例:

    > path.join('/foo', 'bar', 'baz/asdf', 'quux');
    \\foo\\bar\\baz\\asdf\\quux
    > path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
    \\foo\\bar\\baz\\asdf
    > path.join('/foo', 'bar', 'baz/asdf', 'quux', './../..');
    \\foo\\bar\\baz
    > path.join('foo', {}, 'bar');
    TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received an instance of Object

    path.isAbsolute() 是否是绝对路径。请看示例:

    > path.isAbsolute('/foo/bar');
    true
    > path.isAbsolute('qux/');
    false
    > path.isAbsolute('.');
    false

    __dirname

    如果你将 express 项目放在 demo 目录上一层运行

    $ nodemon index
    ,在通过浏览器访问
    http://localhost:3000/
    ,页面会出现报错信息:
    Error: Failed to lookup view "list.html" in views directory "D:\实验楼\node-study\views"

    在文件里面用相对路径是不靠谱的。相对于 15b0 运行 node 的目录,node 就是这么设计。

    每个模块都有 __dirname,表示该文件的目录,是一个绝对路径,还有 __filename。请看示例:

    Running node example.js from /Users/mjr
    
    console.log(__filename);
    // Prints: /Users/mjr/example.js
    console.log(__dirname);
    // Prints: /Users/mjr

    我们可以通过

    path.join(__dirname, 'xxx')
    来修复上面的问题。将 index.js 改为下面的代码即可:

    const path = require('path')
    const express = require('express')
    const app = express()
    app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded
    
    // view engine setup
    app.engine('html', require('express-art-template'));
    // 可以通过下面语句更改模板视图的文件夹,默认是 views
    // app.set('views', path.join(__dirname, 'views'));
    // 模拟数据库
    const DB = [
    {name: 'ph', age: '18'},
    {name: 'lj', age: '19'}
    ];
    const port = 3000
    // 将静态资源对外开放
    app.use('/public', express.static(path.join(__dirname, 'public')))
    
    app.get('/', function (req, res) {
    res.render(path.join(__dirname, 'views', 'list.html'), {
    rows: DB
    });
    });
    
    app.get('/add', function (req, res) {
    res.render(path.join(__dirname, 'views', 'add.html'), {
    rows: DB
    });
    });
    
    app.post('/submit', function (req, res) {
    const row = {}
    row.name = req.body.name
    row.age = req.body.age
    DB.unshift(row);
    res.redirect('/')
    });
    app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
    })
    
    // 处理 404 响应
    app.use(function (req, res, next) {
    res.status(404).send("404")
    })

    其他章节请看:

    前端学习 node 快速入门 系列

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: