Node.JS后端开发


前言

写在开篇

2021-12-30,23:23,很冷,有点困,但是据刚回到寝室的一位室友所说,寝室竟然只有他一人——太卷了,太卷了,我真的好爱他们啊!

哦对了,本篇博文还有一篇前置博文哦(Node.JS基础),零基础的朋友可以先通过简单阅读那篇文章快速上手哦

更新日志

2021/12/30
开始写吧,希望接下来的学习能够顺利!
2021/12/31
说好一起通宵写代码,结果好家伙拉着我打金铲铲之战打到凌晨五点就为了进一次前四..
2022/1/1
争取今天能够写完吧
2022/1/2
本来还想写实际开发过程中遇到的一些问题,不过3号的下午六点作业提交就要截止了,接口那些都还没开始动,可能就没时间写这部分了,以后有机会再补上

浅谈Node.JS

众所周知,计算机执行代码之前,会先把高级语言/汇编语言编译成机器代码,
但是JS啊,能被我们浏览器的引擎(说的就是你,ChromeV8)编译,但是却不能直接被计算机编译,

这个脚本,就是逊啦!

但是现在好啦,Node出现了,它是一个V8的容器,只需要装上Node,我们就能利用这个V8引擎脱离浏览器运行我们的脚本语言——哦不,此时已经飞升成面向对象语言的JS!

Node究竟是何方神圣能够把JS带出浏览器的小圈子呢?
因为它底层是C++写的,本身就能在浏览器外跑…

请求

要学计网了,这块也得学着走了

什么?你问我为什么这学期计网都结课了还说这话?
我能咋整嘛,太菜了跟不上,还不是只能自己下来补呜呜呜~

HTTP请求的过程

也没啥好说的,这小节不重要(但是还是写下来了,问就是强迫症)

主要分为三步:

1.发送请求

DNS解析,建立TCP连接,发起HTTP请求
(TCP啊,以后学计网再写一篇细说这些)

2.处理请求

服务端接收HTTP请求,进行处理

3.响应数据

服务端发送数据给客户端,客户端接收数据

const http = require('http')
// 创建服务器对象,处理 请求 和 响应
const server = http.createServer((req, res) => {
    // 响应
    res.end('Hello Server')
})
// 监听2333端口,成功监听则执行回调函数
server.listen(2333,() => {
    console.log('服务器已在2333端口开启!')
})

开启成功

GET请求

其实上一节中的代码就处理了一个GET请求
GET来的html文件
我们现在来试着处理一下GET请求
先看一下URL,大概长这样

http://localhost:2333/?name=syy&&password=123456/

我们可以如下书写代码,来基本地处理请求

const http = require('http')
const qs = require('querystring')

// 服务端,处理 请求 和 响应
const server = http.createServer((req, res) => {
    // 处理GET
    console.log('这是method', req.method)
    console.log('这是url', req.url)
    console.log('这是首个参数', qs.parse(req.url.split('/?')[0]))
    
    res.end('Hello Server')
})
server.listen(2333,() => {
    console.log('服务器已在2333端口开启!')
})

哦,需要注意,这里的querystrig包已经被弃用了,但是问题不大,
我们只需要ctrl + 鼠标左键点击方法名称,就可以观察其源码

可以看到这里有个注解
querystring方法的源码
这里我们只需要删掉这一行就可以继续用
(当然,也可以自己在通过cv大法把这段代码封装,避免以后真的被删除了没方法可以用了)

进行URL带参数的GET请求,可以在终端看到如下结果

两个GET请求
至于第二个GET请求,也就是这个favicon.ico,它是网页标题前的一个小图标,比如下面这样

favicon.ico

POST请求

再说POST请求之间,先写一个index.html作为前端页面,然后手写一个简单的AJAX请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button onclick="AJAX(1)">发送POST请求</button>
    <button onclick="AJAX(2)">发送GET请求</button>
</body>
<script>
    function AJAX(model){
        
        let xhr = new XMLHttpRequest()

        xhr.withCredentials=true
    
        if(!xhr){
            console.log('XMLHttpRequest对象创建失败')
            return
        }
        if(model == 1)
            xhr.open('POST','http://localhost:2333/index.html')
        else if(model == 2)
            xhr.open('GET', 'http://localhost:2333/index.html')
            xhr.send()
        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4){
                if(xhr.status == 200){
                    let data = JSON.parse(xhr.responseText)
                    console.log('这是响应的数据', data)
                } else {
                    console.warn('没有正确地响应!')
                }
            }
        }
        
    }
</script>
</html>

原生Node解决跨域
之后,我们还需要注意,这里由于端口号不同,所以会有跨域问题出现

这里就要给广大的前端学习者们说明一下,跨域就该由后端来解决,所以很多时候,当后端甩锅给前端让前端配置反向代理什么之类的都是后端懒!

后端大概写成这样

const http = require('http')


// 服务端,处理 请求 和 响应
const server = http.createServer((req, res) => {
    
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500')
    res.setHeader("Access-Control-Allow-Methods", "PUT,POST,DELETE")
    res.setHeader("Access-Control-Allow-Headers", "Content-Type,token")
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Content-Type', 'application/json;charset=utf-8;');

    res.end(JSON.stringfy({
        msg:'成功辣兄弟们!!!'
    }))
})
server.listen(2333,() => {
    console.log('服务器已在2333端口开启!')
})

这里我们采用的是CORS解决跨域
请求成功
请求头

对了,还要很多简单的小框架可以使用,这样用起来更方便
安装express模块:

npm install express --save

别和我说你看到这里的时候还不会用npm

混合请求方式实战

之前简单了解了两种请求,现在来写一写:
首先我们先小小地调整一下AJAX

function AJAX(model){
    
    let xhr = new XMLHttpRequest()

    xhr.withCredentials=true

    if(!xhr){
        console.log('XMLHttpRequest对象创建失败')
        return
    }

    if(model == 1) {
        xhr.open('POST','http://localhost:2333/index.html?user=碳苯&age=19')
        xhr.send({time: new Date()})
    }
        
    else if(model == 2){
        xhr.open('GET', 'http://localhost:2333/index.html')
        xhr.send()
    }
        
        
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
            if(xhr.status == 200){
                let data = JSON.parse(xhr.responseText)
                console.log('这是响应的数据', data)
            } else {
                console.warn('没有正确地响应!')
            }
        }
    }
    
}

服务端也相应地来一点小小的改动

const server = http.createServer((req, res) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5500')
    res.setHeader("Access-Control-Allow-Methods", "PUT,POST,DELETE")
    res.setHeader("Access-Control-Allow-Headers", "Content-Type,token")
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Content-Type', 'application/json;charset=utf-8;');

    const method = req.method
    const url = req.url
    const path = url.split('?')[0]
    const query = qs.parse(url.split('?')[1])

    const resData = {
        method,
        url,
        path,
        query
    }


    if(req.method == 'GET') {
        res.end(JSON.stringify({
            msg:'GET成功辣兄弟们!!!'
        }))
    } else if(req.method == 'POST') {
        let postData = ''
        req.on('data', chunk => {
            postData += chunk.toString()
        })
        req.on('end', () => {
            resData.postData = postData
            res.end(
                JSON.stringify(resData)
            )
        })
        
    }
})

通过阅读以上代码,我们可以看到,我们通过数据流的形式来处理POST请求
响应的数据

虽然有一点中文乱码,不过问题不大,应该设置一下请求头就好了
另外postData没有展开,这个问题不大,实在不行利用深拷贝重新写一个对象传回去

基本操作

在学会请求之后,咱们也得准备正式开发了
为了熟悉每一个文件和相关配置的具体作用,我们就不新建项目了直接在这几个文件的基础上进行改动吧

其实就是懒

基本文件

这里大概是遵循了什么三层架构什么的,但是由于我没有过相关知识的学习经历,所以对这一块的内容还是感到生疏,做的笔记也只能说是照猫画虎有样学样,还不能系统地总结。

1.创建www.js
我们建一个文件叫做www.js,先写个板子

const http = require('http')


const server = http.createServer((req, res) => {

	res.end('WWW.JS')
})

server.listen(6666, () => {
    console.log('server running at port 6666')
})

为了方便维护,我们把createServer里面的回调提出来单独写一个文件叫做app.js
2.创建app.js
先写个基础的板子

const serverHandler = (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
    res.setHeader("Access-Control-Allow-Methods", "PUT,POST,DELETE")
    res.setHeader("Access-Control-Allow-Headers", "Content-Type,token")
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Content-Type', 'application/json;charset=utf-8;');

    const resData = {
        name: 'SYY',
        nickName: '碳苯Carbon',
        id: 1024
    }

    res.end(
        JSON.stringify(resData)
    )
}


module.exports = serverHandler

记得exports导出之后,还要回到www.js里面require引入一下

const app = require('./app.js')

3.设置入口文件
在package.json中修改一下这一行
修改成www.js
package.json文件

开发依赖

开发依赖和依赖的区别就是顾名思义
开发依赖在普通用户使用的时候实际上是不需要的,但是在开发的过程中可能会有比较重要的作用,比如辅助调试之类的

这里要安装的nodemon就是一个做热重载的开发依赖nodemon

# 注意这里要一定写大写D
npm i nodemon -D

然后我们到package.json中康康
package.json
之后我们顺便在package.json添加一个命令dev
添加一个命令

之后我们就可以用npm run dev来执行nodemon ./www.js了,
这样就可以有一个具有热重载的后端了

npm run dev

nodemon启动

淦,这里我的6666端口被占用了,网页打不开,所以只好换了个端口4567
(记得把所有该端口响应地修改一下)

修改的时候就体现出全局变量的好处了啊

在这里插入图片描述
在这里插入图片描述
利用刚才的index.html(记得修改AJAX中的URL)
AJAX请求成功

就很棒

路由配置

由于是原生,所以定义接口的方式很原始,真的很原始啊

1.创建一个文件夹放各个路由

路由测试
2.创建一个路由
先来随便写一个试试手

const handelTestRoute = (req, res) => {
    const method = req.method
    const url = req.url
    const path = url.split('?')[0]
    console.log(path)
    
    if(method == 'GET' && path == '/test_route') {
        return {
            msg: '这是测试用的接口'
        }
    } else {
        return {
            msg: '阿巴阿巴'
        }
    }
}

这里我们有返回值,所以还需要在某个地方接收这个返回值,而这个地方正是我们的app.js
红框中是新增的内容
输入URL测试一下
非常巧妙啊
3.加大难度
首先改一下app.js

}
const testRouteData = handleTestRoute(req, res)

if(testRouteData) {
    res.end(
        JSON.stringify(testRouteData)
    )
} else {
    res.writeHead(404,{'Content-Type':'text/plain'})
    res.write('404 Not Found!!!!')
    res.end()
}

然后是路由本身再改改

const handleTestRoute = (req, res) => {
    const method = req.method
    const url = req.url
    const path = url.split('?')[0]
    console.log(path)
    if(method == 'GET' && path == '/test_route') {
        return {
            msg: '这是测试用的接口'
        }
    } 
}

module.exports = handleTestRoute

还有ajax的url
(略)

好了测试一下
成功
失败

404——这就对了!

规范化类

终于,自从ES6有了class之后,现在才是第一次用

1.新建文件夹和文件
新建model文件夹,放resModel
新建这个文件主要是为了统一各种数据的格式,以达到一种规范化的效果

2.书写如下代码

//  基类
class BaseModel {
    constructor(data, msg) {
        // 理想的参数类型是对象,但是如果传进来的是字符串也需要处理一下
        if(typeof data == 'string') {
            this.msg = data
            // null,接下来就可以视为对象
            data = null
            // 同上
            msg = null
        }
        if(data) {
            this.data = data
        }
        if(msg) {
            this.msg = msg
        }
    }
}
// 成功模型
class SuccessModel extends BaseModel {
    constructor(data, msg) {
        super(data, msg)
        this.err = 0
    }
}

// 失败模型
class ErrorModel extends BaseModel {
    constructor(data, msg) {
        super(data, msg)
        this.err = -1
    }
}

module.exports = {
    SuccessModel,
    ErrorModel
}

注意,其中基类的代码,
因为理想的参数是对象,但是如果强行传进一个字符串作为参数,那么就需要特殊处理一下方便后续流程的进行

这里所谓的后续流程,看似只有后面两个if,感觉改为else就足够了,但考虑到后续的维护,我们还是有必要做这种处理
这种处理在开发中很常见

把字符串信息保存进this.msg,然后把msg和data置为null,这样就能够看做一个为空的对象了

相关方法

1.创建Controller文件夹和文件
在这里插入图片描述
因为书写过程中会使用到一些自定义的方法,为了方便维护我们把它们放到一起

即这里创建的controllers文件夹

2.书写如下代码

const getData = () => {
    return [
        {
            id: 1,
            name: '香甜的甲基橙',
            appearance: '(owo)',
            motto: '爱自己,更要爱别人'
        },
        {   
            id: 2,
            name: '碳苯',
            appearance: '(nvn)',
            motto: '胸怀明月,目蕴骄阳'
        }
    ]
}

module.exports = {
    getData
}

由于改动太大了,比如上面改个端口都要到各个文件里面相应地改一下,或者说写完方法之后在某个文件中添加调用的语句…这样的一些改动我就不演示了,不然篇幅实在是太过于冗余,还望海涵

连接数据库

本来是想用MySQL,结果期末作业要求SQLServer我人麻了,试着连SQLServer整了一晚上还是没有成功

所以我放弃了,还是用MySQL

(MySQL对应的包是mysql,SQLserver对应的包是mssql)

还是先新建文件啦
在这里插入图片描述

这里用的是mysql,代码如下

// const { connect } = require('mssql')
// 垃圾mssql给爷爬,还得是我mysql啊
const mysql = require('mysql')

// 连接
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'root',
    port: 3306,
    database: 'syyselectcourse'
})

connection.connect()

// 执行sql
const sql = `select * from syystudent`
connection.query(sql, (err, result) => {
    if(err) {
        console.log(err)
        return
    }
    console.log('result', result)
})
// 关闭连接
connection.end()

运行结果
数据库连上啦!!!
这里为了演示,就把执行sql和关闭连接写到一个文件里面了,后面记得要拆出来单独写

这里先改成这样:

// const { connect } = require('mssql')
const mysql = require('mysql')

// 连接
const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'root',
    port: 3306,
    database: 'syyselectcourse'
})

connection.connect()

// 执行sql
// const sql = `select * from syystudent`
// connection.query(sql, (err, result) => {
//     if(err) {
//         console.log(err)
//         return
//     }
//     console.log('result', result)
// })


// function executeSQL(sql, callback) {
//     connection(query, callback)
// }

// 还可以进一步优化,用promise来做这个
function executeSQL(sql) {
    const promise = new Promise((resolve, reject) => {
        connection.query(sql, (err, res) => {
            if(err) {
                reject(err)
                return
            }
            resolve(res)
        })
    })
    return promise
}

// 关闭连接
// connection.end()

module.exports = {
    executeSQL
}

之后如果要执行SQL语句,我们放到路由这里做
比如这样

if (path === '/student') {
    const sql = `SELECT * FROM syystudent`
    executeSQL(sql).then(res => {
        console.log('~~~~~SQL执行成功~~~~~')
        console.log(res)
    })
}

发一下ajax请求,果然如我所料,稳得一批
那么

后记

大概,就这样了?

当然不是,时间是有限的,但是知识可是无限的!
Node.JS的学习只是告一段落了,但是我并不会停下学习的脚步,在完成作业项目的开发之后,我会把相关内容上传到我的Gitee仓库,并且把更多更详细的内容放到我的个人博客。
我的仓库
Serio的Gitee仓库希望能得到更多星星哦!
我的博客
Serio的个人博客!访问获得更多学习资料!

之后,咱先去复习计网了,再过几天又是要考试考试考试了哈哈哈~~
2022新的一年,大家继续加油啊!


文章作者: Serio
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Serio !
  目录