node.js小滴课程笔记

 中文文档:http://nodejs.cn/

nodejs是javascript的运行环境

node.js遵守common.js规范

module.exports   用来导出   可以导出对象也可以导出变量

每一个模块的作用域是独立的

初始化node项目   npm init  -y

在node下那个木中定义全局变量用 global.变量=值;

在其它模块可以直接使用   变量

require用来导入模块

 Buffer缓冲器,用来处理二进制

常用api:

Buffer.alloc(10)创建一个长度为10的buffer

Buffer.from([1,2,3]) 将数组[1,2,3]转换成Buffer

Buffer.byteLength(Buffer.from([1,2,3]))buffer的长度

Buffer.isBuffer(Buffer.from([1,2,3]))是否是buffer

Buffer.concat([buf1,buf2])合并buffer    第二个参数可以指定长度   长的截取  短的填充

const buf = Buffer.allocUnsafe(20)  创建一个不安全(可以修改的 buffer)
buf.write("buffer",5,3)   将字符串 从下表为5的位子写入buffer,并且写入字符串的前3个字符    (最后面还可以有一个参数,就是字符编码,默认是utf8)
buf.fill("eric",5,10)  填充buffer,把字符串或其它值,填充进去,填充位置是索引5-10   (最后一个参数也可以指定字符编码)
buf.length   返回buffer的长度
buf.toString()   将buffer对象转换为 字符串
buf.toString('utf8',1,3)  将buffer对象的 1-2索引 解码成 utf8编码  并返回
 buf.toJSON()  将buf对象 转换为json对象格式
 buf1.equals(buf2)   对比两个buffer对象 是否相同
buf1.indexOf('B')  查找字符在buffer的位置索引
buf1.lastIndexOf('B')  查找字符在buffer的位置索引
buf.slice(2,7)   截取buffer对象   [2,7)
buf.copy(buf2)  将buf拷贝进buf2   还有第二个和第三个参数 和第四个参数 分别代表  从目标buffer的第几个索引开始放,将被拷贝buffer的索引几到索引几拷贝进去
nodejs文件系统
fs.readFile("文件路径",["utf8"],(err,data)=>{
  读取文件
})
 
fs.writeFile("./hello.text","this is a text2",["base64"],err =>{
    写入文件
})
 
fs.appendFile("./hello.text",Buffer.from("哈哈"),err=>{
    if(err) throw err;
    console.log("往文件追加内容成功");
})
 
fs.stat("./hello.text",(err,stats) => {
    if(err) throw {msg:"文件不存在"};
    console.log(stats);//文件信息
    console.log(stats.isFile());//是否为文件
    console.log(stats.isDirectory());//是否为文件夹
})
 
fs.rename("./hello.text","./test.text",err=>{//将hello.text冲命名成test.text
    if(err) throw {msg:"重命名失败"};
    console.log("重命名成功");
})
 
fs.unlink("./test.text",err=>{
    if(err) throw err;
    console.log("文件删除成功");
})
 
fs.mkdir("./a",err=>{//默认非递归创建  也就是不能嵌套创建文件夹  存在的文件夹也不能再创建
    if(err) throw err;
    console.log("文件夹创建成功")
})

fs.mkdir("./b/c",{recursive:true},err=>{//递归创建  可以在没有b文件夹的情况下  直接创建b文件夹嵌套c文件夹的形式
    if(err) throw err;
    console.log("文件夹递归创建成功")
})
 
fs.readdir("./",(err,files) => {//读取当前目录的所有文件列表
    if(err) throw err;
    console.log(files)
})

fs.readdir("./",{withFileTypes:true,encoding:"utf8"},(err,files) => {//读取当前目录的所有文件列表 列表中每一项都带上文件的类型
    if(err) throw err;
    console.log(files)
})
 
fs.rmdir("./a",err=>{//默认不能递归删除
    if(err) throw err;
    console.log("删除文件夹成功")
})

fs.rmdir("./b",{recursive:true},err=>{//递归删除文件夹  没有此文件夹不会报错
    if(err) throw err;
    console.log("递归删除文件夹成功")
})
 
fs.watch("./",(eventType,filename) => {//监听当前目录列表中 文件的变化 eventType:rename、change  通常用于本地构建
    console.log("发生改变的类型:" + eventType);
    console.log("文件【"+ filename+"】发生改变");
})
 
fs.watch("./",{//watch监听文件发生变化时,老是执行两次回调函数,所以我们再尝试一下下面的监听文件发生改变的方法
    recursive:true
},(eventType,filename) => {//可以监听到子目录的变化
    console.log("发生改变的类型:" + eventType);
    console.log("文件【"+ filename+"】发生改变");
})
 
 这里推荐使用一个插件:(避免监听 文件变化时,回调函数执行两次的情况)
npm install chokidar --save-dev

使用:

const chokidar = require("chokidar");
chokidar.watch("./").on("all",(event,path) => {//all代表 监听该目录下所有文件
    console.log(event+" --- "+path)
})

上面的监听发现,把当前文件夹下的node_modules文件都监听了,我们应该如何忽略呢?

chokidar.watch("./",{//ignored  代表忽略的文件夹路径
    ignored:"./node_modules"
}).on("all",(event,path) => {//all代表 监听该目录下所有文件 
    console.log(event+" --- "+path)
})

 文件流:

主要有:读取流和写入流,当然还有其他的分类

下面来介绍一下读取流

const fs = require("fs");

let rs = fs.createReadStream('./streamTest.js',{
    highWaterMark:100,//这里指定每次获取100b的文件大小
});//创建一个读取流 (读取自身)

let count = 1;//这里做一个计数
rs.on("data",chunk => {//读取每一块数据时的监听事件  默认每次读取64*1024b  也就是64kb的数据 流主要是通过buffer的形式传输的
    console.log(chunk.toString())
    console.log(count++);//每次读取完一段后 count计数自增1
})

rs.on("end",()=>{//监听读取结束事件
    console.log("读取完成")
})

下面来写一个  写入流的 例子:

let ws = fs.createWriteStream("./a.txt");//创建一个写入流

let num = 1;//创建一个计数

let timer = setInterval(() => {
    if(num <= 10){
        ws.write(num + "");//将计数 写入  这里参数只能接受 字符串或者 buffer对象 所以转化成了字符串
        num++;//计数 自增1
    }else{
        ws.end("写入完成");//执行结束方法  结束方法也可以写入内容
        clearInterval(timer);//清除计时器
    }
}, 200);

ws.on("finish",()=>{//监听写入完成的事件
    console.log("写入完成")
})

 

 管道流的使用方式,通常就是讲读取流 连接 到写入流‘
let rs = fs.createReadStream("./streamTest.js");//创建一个读取流  读取当前文件
let ws = fs.createWriteStream("./a.txt");//创建一个写入流  写入到当前目录下的a.txt下

ws.on("finish",()=>{//这里加一个 写入流的监听事件
    console.log("写入完成")
})
// 利用管道 将读取流和写入流 连接到一起
rs.pipe(ws);

 nodejs的path模块

const {basename,dirname,extname,join,normalize,resolve,format,parse,sep,win32} = require("path");

console.log(basename("/nodejs/2-6/index.js"));//返回最后一部分的内容  index.js
console.log(basename("/nodejs/2-6/index.js",".js"));//返回最后一部分的内容 并且将后缀名去掉  index
console.log(dirname("/nodejs/2-6/index.js"));//返回最后一部分内容所在的目录  /nodejs/2-6
console.log(extname("index.js"));//返回文件后缀  .js

console.log(join("nodejs","//seddir","index.js"));//拼接路径  nodejs\seddir\index.js  并且我们如果多写了/ 他会自动修复 然后拼接
console.log(normalize("/nodejs//seddir/index.js"));// 规范化路径 我们故意多加了一条/ 然后这个方法修复了 \nodejs\seddir\index.js
console.log(resolve("./pathTest.js"));//返回绝对路径  D:\node教程\demo\2-6\pathTest.js

let pathObj = parse("/nodejs/test/index.js");//解析路径成路径对象  parse和format是可以相互转化的
console.log(pathObj);//{root: '/',dir: 'nodejs/test',base: 'index.js',ext: '.js',name: 'index'}
console.log(format(pathObj));//   /nodejs/test\index.js
console.log(sep);;//返回当前系统 路径分隔符  \  记住 他不是方法,是path上的属性  mac下应该是/
console.log(win32.sep);//window系统下的 路径分隔符   \

// __filename :当前文件的 绝对路径   __dirname:当前文件的绝对目录  这两个api是node自带 不用专门引入
console.log(__filename);//D:\node教程\demo\2-6\pathTest.js   他和resolve有一定的区别  当执行文件不同时 resolve 的路径值得注意 它是相对于执行文件的绝对路径将文件名拼接到后面的 而__filename不会受执行文件的影响
console.log(__dirname);// D:\node教程\demo\2-6>

 events事件触发器:大多数api都是基于事件触发器来触发的

 下面来 举个小例子:

const EventEmitter = require("events");

class MyEmitter extends EventEmitter{};//用es6的方式创建一个(事件触发器的)类 此类是通过继承EventEmitter类创建的

let myEmitter = new MyEmitter();//这里创建一个(事件触发器的)实例

myEmitter.on("hi",()=>{//在事件触发器实例上绑定一个事件  hi 
    console.log("hi事件触发了")
})

myEmitter.emit("hi");//通过这个事件触发器实例 的emit方法 触发 hi 事件

那么如何传参呢? 回调函数和 emit参数中 第一个以后的参数 一一对应

myEmitter.on("hi",(a,b)=>{
    console.log("hi事件触发了:"+ (a + b))
})

myEmitter.emit("hi",1,8);

 

用once绑定的事件只能触发一次,多次触发 无效

myEmitter.once("hello",()=>{//once绑定的事件 只能触发一次  多次触发不生效
    console.log("hello事件触发了")
})

myEmitter.emit("hello");

 

监听器实例可以利用removeListener方法  移除指定事件中的回调函数:
 
function fn1(a,b){//方法1
    console.log("hi事件触发了 事件带参:"+ (a + b))
}

function fn2(){//方法2
    console.log("hi事件触发了 事件不带参:")
}
myEmitter.on("hi",fn1);//给hi事件绑定上 fn1 方法
myEmitter.on("hi",fn2);//再给hi事件绑定上 fn2 方法

myEmitter.emit("hi",1,8);

// 将fn2 冲hi事件中移除
myEmitter.removeListener("hi",fn2);
myEmitter.emit("hi",1,8);//再执行一次  此次只执行fn1回调了

 可以用监听器实例上的removeAllListeners方法,将指定事件上的方法全部移除:

myEmitter.removeAllListeners("hi");
myEmitter.emit("hi",1,8);//没执行任何 回调  因为都移除了

 

 核心模块util

 util的callbackify方法可以使async函数变成有回调风格的函数:

const util = require("util");

async function hello(){
    return "hello world";
}

let helloCb = util.callbackify(hello);//将hello方法  转换成有回调风格的 方法

helloCb(((err,data) => {
    if(err) throw err;
    console.log(data);
}))
util.promisify  可以将回调风格的函数转化成promise风格的函数  避免回调地狱
const util = require("util");
const fs = require("fs");

fs.stat("./utilTest.js",(err,data)=>{//fs.stat用来查看 文件信息 是一个回调风格的函数
    if(err) throw err;
    console.log(data)
})

let stat = util.promisify(fs.stat);//将回调风格的函数  转化成 promise风格的函数

stat("./utilTest.js").then(data=>{
    console.log(data)
}).catch(err=>{
    console.log("出错了");
    console.log(err)
})

当然也可以用async的方式来写

const util = require("util");
const fs = require("fs");

let stat = util.promisify(fs.stat);//将回调风格的函数  转化成 promise风格的函数

async function statFn(){
    try{
        let statInfo = await stat("./utilTest.js");
        console.log(statInfo)
    }catch(e){
        console.log(e);
    }
}
statFn()

 

util.types.isDate() 判断是否为日期类型
util.types.isDate(new Date);//判断是否为日期类型 true
util.types.isDate("2020/06/22");//false

当然还可以判断许多其它的,可以查文档

 http:HyperText transfer protocal  超文本传输协议
客户端输入url,这个过程发生了什么事
1、DNS解析:把域名解析成一个ip地址
2、TCP连接:(三次握手)(1):客户端给服务端说,你好我要建立连接了(2)服务端收到后,回复客户端,好的,我们可以连接(3)客户端收到后,回复,那我们发送连接了,然后夫妇段就收到了连接。
3、浏览器发送http请求
4、服务端处理请求
5、浏览器解析渲染页面
6、连接结束(4次握手)
 http请求方法和响应头信息
http请求方法
GET:请求指定的页面信息,并返回实体主体
HEAD:类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头
POST:向指定资源提交数据进行处理请求,数据被包含在请求体中。
PUT:从客户端向服务器传送的数据取代指定的相应的内容
DELETE:请求服务器删除指定的页面
CONNECT:http/1.1协议中预留给能够将链接改为管道方式的代理服务器
OPTIONS:允许客户端查询服务器的性能
TRACE:回显服务器收到的请求,主要用于测试或诊断
 
http响应头信息
Allow:服务器支持哪些请求方法(get、post等)
Content-Encoding:文档的编码方法。只有在解码之后才可以得到Content-type头指定的内容类型。利用gzip压缩,能减少html文档的下载时间
Content-Length:表示内容长度,只有当浏览器使用持久http连接时才需要这个数据
Content-Type:表示文档属于什么MIME类型
Date:当前GMT时间
Expires:资源什么时候过期,不再缓存
Last-Modified:文档最后改动时间
Location:重定向的地址
Server:服务器的名字
Set-Cookie:设置和页面关联的cookie
WWW-Authenticate:定义了使用何种验证方式去获取对资源的连接
 
http状态码
常见的http状态码:
200:请求成功
301:资源被永久转移到其它url
404:请求资源(网页等)不存在
500:内部服务器错误
http状态码分为5类:
1**:信息,服务器收到请求,需要请求者继续执行操作
2**:操作被成功接收,并处理
3**:重定向,需要进一步的操作并完成请求
4**:客户端错误,请求包含语法错误或无法完成请求
5**:服务器错误,服务器在处理请求的过程中发生了错误
Content-Type:内容类型:
text/html:HTML格式
text/plain:纯文本格式
text/XML:XML格式
image/gif:gif图片格式
image/jpeg:jpg图片格式
image/png:png图片格式
multipart/form-data:需要在表单中进行文件上传时,就需要使用该格式
。以application开头的媒体格式类型
application/xhtml+xml:XHTML格式
application/xml:XML数据格式
application/atom+xml:Atom + XML聚合类型
application/json:json数据格式
application/pdf:pdf格式
application/msword:word文档格式
application/octet-stream:二进制流数据(常见的文件下载)
application/x-www-form-urlencoded:表单中默认的encType,表单数据被编码为key/value格式发送到服务器
 
创建一个简单的服务器:
const http = require("http")

const server = http.createServer((req,res) => {
    res.writeHead(200,{'content-type':'text/html'});
    res.end('<h1>hello world</h1>');
    
})

server.listen(3000,()=>{
    console.log("监听了3000端口")
})

 url.parse方法:用来解析路径

下面举个小例子:

const url = require("url")

console.log(url.parse("https://api.xdclass.net/pub/api/v1/web/product/find_list_by_type?type=2"));
Url {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'api.xdclass.net',
  port: null,
  hostname: 'api.xdclass.net',
  hash: null,
  search: '?type=2',
  query: 'type=2',
  pathname: '/pub/api/v1/web/product/find_list_by_type',
  path: '/pub/api/v1/web/product/find_list_by_type?type=2',
  href: 'https://api.xdclass.net/pub/api/v1/web/product/find_list_by_type?type=2'
}

 

 

//加上第二个参数  默认是false 设为true,会解析query参数
console.log(url.parse("https://api.xdclass.net/pub/api/v1/web/product/find_list_by_type?type=2",true));
Url {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'api.xdclass.net',
  port: null,
  hostname: 'api.xdclass.net',
  hash: null,
  search: '?type=2',
  query: [Object: null prototype] { type: '2' },
  pathname: '/pub/api/v1/web/product/find_list_by_type',
  path: '/pub/api/v1/web/product/find_list_by_type?type=2',
  href: 'https://api.xdclass.net/pub/api/v1/web/product/find_list_by_type?type=2'
}

如何处理get请求

const url = require("url")
const http = require("http")

const server = http.createServer((req,res) => {
    // 创建一个对象,将请求的url解析后存起来
    let urlObj = url.parse(req.url,true);
    // 我们将query参数 返回到页面上
    res.end(JSON.stringify(urlObj.query))
});

server.listen(3000,() => {
    console.log("监听3000端口");
})

 如何处理post请求

 

const url = require("url")
const http = require("http")

const server = http.createServer((req,res) => {
    // 监听post请求,因为post请求 是以流的形式 传递参数,所以 需要不断的监听
    let postData = '';
    // 监听post请求的 数据 要用监听流的形式监听
    req.on("data",chunk => {
        //这里chunk本来是buffer对象  由于用了字符串拼接  所以 隐式的将buffer对象转换成了字符串类型
        postData += chunk;
    })
    // 监听传输结束事件
    req.on("end",()=>{
        console.log(postData);
    })
    // 返回
    res.end(JSON.stringify({
        data:"请求成功",
        code:0,
        
    }))
});

server.listen(3000,() => {
    console.log("监听3000端口");
})

 整合get/post请求

const url = require("url")
const http = require("http")

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

    if(req.method === 'GET'){
        let urlObj = url.parse(req.url,true);
        res.end(JSON.stringify(urlObj.query))
    }else if(req.method === 'POST'){
        let postData = '';
        req.on("data",chunk => {
            postData += chunk;
        })
        req.on("end",()=>{
            console.log(postData);
        })
        res.end(JSON.stringify({
            data:"请求成功",
            code:0,
            
        }))
    }    
});

server.listen(3000,() => {
    console.log("监听3000端口");
})

补充一下,post请求可以用postman发送,百度一下可以下载

 使用nodemon自动重启工具

nodemon可以让node项目自动重启,我们之前改点代码,需要手动重启项目才能生效,nodemon工具可以在我们改动代码后自动重启。

推荐全局安装:npm install -g nodemon

比如说要启动 server.js   

之前的启动是:node server

那么用nodemon启动就是:nodemon server

定义命令

先初始化一下项目:npm init -y

之后出现了package.json文件

找到script字段,自定义一个命令

"scripts": {
    "start":"nodemon reqTest.js"
  },

然后我们就可以 通过 npm run start来启动项目了

路由初始化以及接口开发

我们先写个最原始的接口,通过请求的路径和请求方法来判定具体是哪一个接口

server.js

const http = require("http")
const url = require("url")

const server = http.createServer((req,res)=>{
    // 将返回类型变成json格式  编码为utf-8  防止中文乱码
    res.writeHead(200,{'content-type':'application/json;charset=UTF-8'});
    // 拿到路径参数
    let urlObj = url.parse(req.url,true);
    if(urlObj.pathname === '/api/aa' && req.method === 'GET'){
        //若路径名是/api/aa  并且是get请求  则返回query参数
        res.end(JSON.stringify(urlObj.query))
    }else{
        res.end('404 not found')
    }
})

server.listen(3000,()=>{
    console.log("服务启动 3000端口")
})

可以看到以上是写了一个接口,如果写很多个接口,都放到server.js文件中,那么这个文件就会显的臃肿,那么接下来我们就可以整合一下接口。

创建一个router文件夹,创建index.js

const url = require("url")
function handleRequest(req,res){
    // 拿到路径参数
    let urlObj = url.parse(req.url,true);
    if(urlObj.pathname === '/api/aa' && req.method === 'GET'){
        //若路径名是/api/aa  并且是get请求  则返回query参数
        return{
            msg:"获取成功",
            code:0,
            data:urlObj.query
        }
    }

    if(urlObj.pathname === '/api/post' && req.method === 'POST'){
        //若路径名是/api/post  并且是post请求  
        return{
            msg:"获取成功",
            code:0,
        }
    }
}

module.exports = handleRequest;

server.js对应改为:

const http = require("http")
const routerModal = require("./router/index");
const server = http.createServer((req,res)=>{
    // 将返回类型变成json格式  编码为utf-8  防止中文乱码
    res.writeHead(200,{'content-type':'application/json;charset=UTF-8'});
    let resultData = routerModal(req,res);
    if(resultData){
        //如果resultData有数据  就把数据返回出去
        res.end(JSON.stringify(resultData));
    }else{
        res.writeHead(404,{'content-type':'text/html'})
        res.end('404 not found')
    }
    
})

server.listen(3000,()=>{
    console.log("服务启动 3000端口")
})

 实战用户列表增删改查:(不过现在还没连接数据库,数据仍然是假的)

很显然,我们需要些4个接口,用户列表获取、新增、删除、更新

server.js:比之前多了个获取post数据的方法,自定义给req对象上加了个body属性,将post传来的数据赋值给了它

const http = require("http")
const routerModal = require("./router/index");
const getPostData = function(req){
    //封装一个获取post的数据的方法
    return new Promise((resolve,reject) => {
        if(req.method !== 'POST'){
            //如果不是post请求 就返回个空对象
            resolve({});
            return;
        }
        let postData = '';
        req.on("data",chunk => {
            postData += chunk;
        })
        req.on("end",()=>{
            resolve(JSON.stringify(postData))
        })
    })
}

const server = http.createServer((req,res)=>{
    // 将返回类型变成json格式  编码为utf-8  防止中文乱码
    res.writeHead(200,{'content-type':'application/json;charset=UTF-8'});
    getPostData(req).then(data=>{//这里的data就是post请求传来的数据
        req.body = data;//自定义给req对象加一个body属性,将post传过来的数据赋值给body
        let resultData = routerModal(req,res);
        if(resultData){
            //如果resultData有数据  就把数据返回出去
            res.end(JSON.stringify(resultData));
        }else{
            res.writeHead(404,{'content-type':'text/html'})
            res.end('404 not found')
        }
    })
        
    
})

server.listen(3000,()=>{
    console.log("服务启动 3000端口")
})

router/index.js

const url = require("url")
const {getUserList,addUser,deleteUser,updateUser} = require("../controller/user")
function handleRequest(req,res){
    // 拿到路径参数
    let urlObj = url.parse(req.url,true);
    if(urlObj.pathname === '/api/getUserList' && req.method === 'GET'){
        //获取用户列表接口
        let resultData = getUserList();
        return resultData;
    }

    if(urlObj.pathname === '/api/addUser' && req.method === 'POST'){
        //用户新增接口
        let resultData = addUser(req.body);
        return resultData;
    }

    if(urlObj.pathname === '/api/deleteUser' && req.method === 'POST'){
        //删除用户接口
        let resultData = deleteUser(urlObj.query.id);
        return resultData;

    }

    if(urlObj.pathname === '/api/updateUser' && req.method === 'POST'){
        //删除用户接口
        let resultData = updateUser(urlObj.query.id,req.body);
        return resultData;

    }
}

module.exports = handleRequest;

controller/user.js:这一个文件专门是用来处理数据的,user.js是专门用来处理用户数据的,分好模块。现在是模拟数据,将来会从操作数据库

module.exports = {
    getUserList(){
        //获取用户列表
        return [
            {id:1,name:"tom",city:"北京"},
            {id:2,name:"xiaoming",city:"广州"},
            {id:3,name:"xiaohua",city:"上海"},
        ]
    },
    addUser(userObj){
        // 新增用户
        console.log(userObj);
        return {
            code:0,
            msg:"新增成功",
            data:null
        }
    },
    deleteUser(id){
        //删除用户
        console.log(id)
        return {
            code:0,
            msg:"删除成功",
            data:null
        }
    },
    updateUser(id,userObj){
        //更新用户信息
        console.log(id,userObj);
        return {
            code:0,
            msg:"更新成功",
            data:null
        }
    }
}

 解决接口跨域问题

我们在项目中写个html页面,用jquey去请求我们之前写的接口:

这里给大家推荐个vscode插件:live server  下载安装后,打开当前html文件,点击右下角GO live 就可以将当前html放到一个服务器上面运行,很方便‘

我们在html发送了一条请求;

       $.ajax({
            url:"http://127.0.0.1:3000/api/getUserList",
            success:function(res){
                console.log(res)
            }

        })

然后报了跨域。

如何解决跨域呢?

我们只需要在服务器的响应对象上加上:这样所有不同的域就可以跨域请求了

// 设置跨域处理
    res.setHeader("Access-Control-Allow-Origin","*");

 正常在服务器上是不会设置  *  号的,这样就相当于把接口共享了,一般会设置指定的可访问的地址

// 设置跨域处理
res.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:5500");

 

 结合数据库改造用户列表接口  (增)

数据库配置:

config/db_config.js:

let dbOption;

dbOption = {
    connectionLimit:10,//同时创建连接的最大连接数
    host:"localhost",//连接地址
    user:"root",//用户
    password:"123456",//密码
    port:'3306',//端口
    database:"user_test",//要连接的数据库
}

module.exports = dbOption;

数据库连接以及query方法封装:

db/conn.js:

const mysql = require("mysql")
const dbOption = require("../config/db_config")

// 创建连接池
const pool = mysql.createPool(dbOption);

// 封装一个 sql语句执行方法  因为这个执行方法有可能在很多文件重复调用 所以封装一下
// 接受sql语句,以及参数
function query(sql,params){
    return new Promise((resolve,reject) => {
        pool.getConnection((err,conn) => {
            if(err){
                reject(err);
                return;
            }
            // 执行sql语句
            conn.query(sql,params,(err,result) => {
                // 不管是否 报错  首先将连接 释放掉
                conn.release()
                if(err){
                    reject(err);
                    return;
                }
                resolve(result);
            })
        })
    })
}

module.exports = query

server.js:响应有点更改

const http = require("http")
const routerModal = require("./router/index");
const getPostData = function(req){
    //封装一个获取post的数据的方法
    return new Promise((resolve,reject) => {
        if(req.method !== 'POST'){
            //如果不是post请求 就返回个空对象
            resolve({});
            return;
        }
        let postData = '';
        req.on("data",chunk => {
            postData += chunk;
        })
        req.on("end",()=>{
            resolve(JSON.parse(postData))
        })
    })
}

const server = http.createServer((req,res)=>{
    // 设置跨域处理
    res.setHeader("Access-Control-Allow-Origin","http://127.0.0.1:5500");
    // 将返回类型变成json格式  编码为utf-8  防止中文乱码
    res.writeHead(200,{'content-type':'application/json;charset=UTF-8'});
    getPostData(req).then(data=>{//这里的data就是post请求传来的数据
        req.body = data;//自定义给req对象加一个body属性,将post传过来的数据赋值给body
        let result = routerModal(req,res);
        if(result){
            result.then(resultData => {
                //把数据返回出去
                res.end(JSON.stringify(resultData));
            })
        }else{
            res.writeHead(404,{'content-type':'text/html'})
            res.end('404 not found')
        }
    })
        
    
})

server.listen(3000,()=>{
    console.log("服务启动 3000端口")
})

router/index.js:没有发生改变

const url = require("url")
const {getUserList,addUser,deleteUser,updateUser} = require("../controller/user")
function handleRequest(req,res){
    // 拿到路径参数
    let urlObj = url.parse(req.url,true);
    if(urlObj.pathname === '/api/getUserList' && req.method === 'GET'){
        //获取用户列表接口
        let resultData = getUserList();
        return resultData;
    }

    if(urlObj.pathname === '/api/addUser' && req.method === 'POST'){
        //用户新增接口
        let resultData = addUser(req.body);
        console.log(resultData,"index.js")
        return resultData;
    }

    if(urlObj.pathname === '/api/deleteUser' && req.method === 'POST'){
        //删除用户接口
        let resultData = deleteUser(urlObj.query.id);
        return resultData;

    }

    if(urlObj.pathname === '/api/updateUser' && req.method === 'POST'){
        //删除用户接口
        let resultData = updateUser(urlObj.query.id,req.body);
        return resultData;

    }
}

module.exports = handleRequest;

controller/user.js:的addUser方法发生了改变

const query = require("../db/conn");
module.exports = {
    getUserList(){
        //获取用户列表
        return [
            {id:1,name:"tom",city:"北京"},
            {id:2,name:"xiaoming",city:"广州"},
            {id:3,name:"xiaohua",city:"上海"},
        ]
    },
    async addUser(userObj){
        // 新增用户
        console.log(userObj);
        let {name,city,sex} = userObj;
        let sql = 'insert into user (name,city,sex) values (?,?,?)';
        let resultData = await query(sql,[name,city,sex]);
        console.log(resultData,"user.js");
        if(resultData){
            return {msg:"新增成功"}
        }else{
            return {msg:"新增失败"}
        }
    },
    deleteUser(id){
        //删除用户
        console.log(id)
        return {
            code:0,
            msg:"删除成功",
            data:null
        }
    },
    updateUser(id,userObj){
        //更新用户信息
        console.log(id,userObj);
        return {
            code:0,
            msg:"更新成功",
            data:null
        }
    }
}

目录结构:

 

 结合数据库改造用户列表接口:(查

user.js:

async getUserList(urlParams){
        let {name,city} = urlParams;
        let sql = 'select * from user where 1=1';
        if(name){
            sql += ' and name = ?';
        }
        if(city){
            sql += ' and city = ?';//注意前面流一个空格 要不查询数据库就会 报错 说太靠近了
        }
        console.log(sql);
        let resultData = await query(sql,[name,city]);
        //获取用户列表
        return resultData;
    },

urlParams参数记得在index.js中传过来

// 拿到路径参数
    let urlObj = url.parse(req.url,true);
    if(urlObj.pathname === '/api/getUserList' && req.method === 'GET'){
        //获取用户列表接口
        let resultData = getUserList(urlObj.query);
        return resultData;
    }

 

 

                                                                                                                                                                                                                                   

 
 
 
 
 
 
 
 
 
 

 

 

 

 
 
posted @ 2020-06-10 22:44  古墩古墩  Views(231)  Comments(0Edit  收藏  举报