nodejs 山羊上山系列(2)- 处理http请求

上一章节,我们搭建了nodejs的server端开发环境,本章节我们就开始对http请求做一些预处理…

本篇幅主要的技术点

[❌] 使用node原生http模块搭建http-server
[❌] 使用nodemon处理热加载
[❌] 使用cross-env处理不同操作系统环境变量(通过nodejs的process全局变量的process.env.NODE_ENV就可以获取)
[✔] 使用querystring处理get请求的参数
[✔] 使用promise的方式获取post请求body参数的获取
[✔] 使用es6 Class的方式定义响应数据的基础格式,然后对成功响应和失败响应extends基础格式
[❌] mySql常用操作汇总
[❌] nodejs链接本地mysql数据库响应客户端请求
[❌] 登陆校验/登陆信息存储
[❌] nodejs链接redis数据库
[❌] nginx的配置反向代理
[❌] 日志管理
[❌] 信息安全(sql注入/xss攻击/md5信息加密)

模块拆分

structure

  • bin/www.js为整个node应用的主入口,简称app,将app拆封成:
    • 跟http-server相关的逻辑(只关注http-server本身的配置)
    • 处理具体后台业务的serverHandler(req对象有req.method可以获取求情类型,req.url可以获取请求的url地址)
1
2
3
4
5
  //http-server 这里的逻辑和具体业务没有关系
const serverHandler = require('./serverHandler') // 统一拦截路由,响应客户端的请求
const http =require('http')
const server = http.createServer((req,res){}) ==== 抽离成==== > const server = http.createServer(serverHandler)
server.listen(3000)

serverHandler预处理http的get请求:

通过req.url和req.method可以分别获取浏览器客户端发送请求的地址信息和请求类型信息:

1
2
3
4
5
6
7
8
9
10
11
// serverHandler
const querystring = require('querystring')
//------------------------- 第一步 处理返回数据的格式为json---------------------------------------------
res.setHeader("Content-type","application/json") // 注意大小写

//------------------------- 第二步 处理get请求的参数---------------------------------------------------
// 解析path,然后赋值给req对象
req.path = querystring.parse(req.url.split('?')[0]);
// 引入querystring处理get请求的参数,然后赋值给req对象
const querystring = require('querystring')
req.query = querystring.parse(req.url.split('?')[1]);

设置http request请求头的content-type

1
2
// 获取请求头的content-type
req.headers['content-type','application/json'] // 注意大小写

通过promise的方式封装post请求获取body参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//---------------------------第三步 通过promise的方式封装post请求获取body参数---------------------------
// 因为post请求获取body的数据是异步的,所以用promise封装一下,方便后面处理代码执行逻辑,等获取到body后处理路由数据
const getPostData=(req)=>{
return new Promise((resolve,reject)=>{
if(req.method==='POST'){
let postData= ''
// 类似stream流的方式获取body中的data
req.on('data',chunk=>{
postData += chunk.toString()
})
// 监听到完成返回数据
req.on('end',()=>{
if(postData){
resolve(postData)
return
}
resolve({})
})
}
})
}

根据业务处理路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// ---------------------------第四步 引入路由处理,然后根据req请求的类型和参数类型处理不同的逻辑-------------------
const blogRouterHandler = require('./src/router/blog')
const userRouterHandler = require('./src/router/user')
// 处理post请求异步的body中携带的数据
getPostData(req).then(postData => {
// 将解析的body数据放入req的body中在路由中使用
req.body = postData;
// 处理页面路由
const blogResult = blogRouterHandler(req, res);
if (blogResult) {
blogResult.then(blogData=>{
res.end(JSON.stringify(blogData))
return
})
}
const userData = userRouterHandler(req, res);
if (userResult) {
userResult.then(blogData=>{
res.end(JSON.stringify(userData))
return
})
}
// 处理异常路由
res.writeHead(404, { 'content-Type': 'text/plain' })
res.write('404 Not Fount \n');
res.end();
});

处理响应数据的数据结构

  • serverHandler中,按照业务又拆分成:处理博客的相关路由/用户相关的路由/以及路由未命中的404异常
  • 为了再响应客户端请求时,返回的数据能够格式统一,于是封装了一个responseModel。包括data,message和state三个参数这样响应的时候就能统一返回一个固定的格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 基类
class BaseModel{
constructor(data,message){
if(typeof data =="String"){
this.message = data
data = null
message = null
}
if(data){
this.data =data
}
if(message){
this.message = message
}

}
}
// 成功类
class SuccessModel extends BaseModel{
constructor(data,message){
super(data,message)
this.state = 0 // 成功的状态
}
}
// 是败类
class ErrorModel extends BaseModel{
constructor(data,message){
super(data,message)
this.state = -1 // 失败的状态
}
}
// 响应的示例:
{
"data""响应数据",
"message":"这是响应的消息"
"state":"这是响应的状态"
}

封装api跟mysql数据库交互查询数据

  • 在博客的相关路由和用户相关路由中又根据各自的业务,设计RestFul接口Api来处理具体的业务和数据返回,然后将api接口返回的数据通过SuccessModel进行包装。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const {
SuccessModel,
ErrorModel
} = require('../model/model')
const {
getBlogList // 这就是从数据库查询数据的接口,需要用nodejs链接mysql数据库,然后封装查询的方法queryData
}= require('../api/blog')
// blogRouterHandler.js
const blogRouterHandler = (req,res)=>{
// 列表
if(req.method === 'GET'&& req.path==="api/blog/list"){
// 需要封装getBlogList的api来获取博客列表
const blogListResult = getBlogList(req);
if(blogListResult){
return new SuccessModel(blogListResult,'请求博客列表成功')
}
return ErrorModel('请求博客列表失败')
}
// 创建
if(req.method === 'POST'&& req.path==="api/blog/create"){
// 需要封装createBlog的api来新增
}
...


}
module.exports = blogRouterHandler

总结

到此位置,我们一步一步的剥离server端的功能,从app.js(搭建http服务和处理请求的serverHandlder)===>serverHandler(预处理get/post请求,处理路由和异常的404,当然也可以处理cookie/session等)===>router(根据业务拆分不同的路由)===>api(在不同的路由中又根据不同的业务封装api和数据库交互)。看似很复杂,但是一步一步又很合理…

初到贵宝地,有钱的给个钱场,没钱的挤一挤给个钱场