NodeJS(核心模块→静态文件托管&&动态数据请求)


  本文通过对NodeJS内置的核心模块进行介绍,逐步的实现静态文件托管和动态数据请求的需求(fs模块介绍之后实现静态资源托管,querystring模块介绍后实现动态数据请求,并将二者合二为一实现一个小demo)

一、http模块

  Node.js提供了http模块,http模块主要用于搭建HTTP服务端和客户端,如果要使用HTTP服务器或客户端功能,则必须调用http模块。

1.1 引入http模块

require('http');

1.2 创建一个http对象(服务)

创建服务成功后,每次请求都会触发createServer的回调函数

http.createServer( [callback(req, res)] );

功能:搭建一个服务器
参数(可选):带有两个参数的回调函数,处理服务所有的业务逻辑

  • req对象(请求):浏览器→服务器

    • 属性:req.url:表示地址,提取get数据
    • 方法
      • 提取post数据
        req.on(‘data’,(chunk)=>{
        // chunk表示每次收到的数据buffer
        })
      • 所有的http[s]都会触发end事件
        req.on(‘end’,()=>{ 接收完毕 字符 切/querystring })
  • res对象(响应):服务器→浏览器

    • 方法
      • 设置响应头,解决中文乱码的问题
        res.writeHead(200,{‘Content-Type’:‘text/html;charset=utf-8’});
      • 向前端发送数据
        res.write(字符/数据)
      • 结束响应
        res.end()

返回值:返回http对象

1.3 监听

server.listen( port [, hostname] [, callback()] );

功能:指定这个HTTP服务器监听的端口号或地址,监听端口地址,成功开启服务,执行回调函数
参数

  • port(必选):指定端口号,取值范围:1-65535(1024以下系统占用)
  • hostname(可选):指定地址,默认为localhost
  • callback(可选):监听成功后回调一次

返回值:服务的一些信息

小提示
  每次更新后,需要服务器自动重启
  推荐: supervisor | nodemon 命令行工具,以下二者取其一即可

>npm install supervisor -g
>npm install nodemon -g

  安装完成直接用supervisor || nodemon替换npm || cnpm即可,命令其他组成不变

1.4 场景案例

  通过前三步,就可以搭建一个简易的Web服务器了,虽然不具备任何功能,也不能返回任何数据,但这确实算是一个服务器了,以下是代码实现:

const http = require('http');                   // 引入http模块
const server = http.createServer();       // 创建一个服务
server.listen('3000');                              // 监听端口

终端运行该文件

>node test
_

  会发现光标一直处于闪烁状态,说明运行还没有结束,因为服务器开启后会一直处于运行监听状态(这样的设定很符合服务器,因为服务器本应处于一个永不宕机的状态),下面来通过浏览器访问我们设定的端口:3000
访问3000端口
  会发现logo区域会一直显示正在加载的标识,说明我们的一次服务请求完之后并没有结束,下面来完善一下代码,让这次服务请求看起来更加直观一点:

const http = require('http');
const server = http.createServer((req,res)=>{ 
    console.log("有人访问了");    
    res.write("Hello Node");   
    res.end();
});
server.listen('3000',()=>{
    console.log("服务器开启了");
});

在终端运行该文件

>node test
服务期开启了
_

  这样看起来就比较直观了,但是我们发现运行服务器后并没有打印“有人访问了”这句话,也就是createServer()中的回调函数没有执行,接下来在浏览器中访问这个端口:
在这里插入图片描述
访问成功,而且请求完成自动结束加载状态,然后看刚刚打开的终端:

>node test
服务期开启了
有人访问了
有人访问了
_

  这里打印了两次“有人访问了”,就说明调用了两次回调函数,而我们只访问了一次,这里要说明一点:高版本的浏览器都会去自动检测整个网站里面有没有一个叫favicon.ico的图标(也就是标题栏左上角的图标),如果有,就会渲染到浏览器的左上角,如果请求次数多了,会极大的浪费性能。所以,编码的时候要注意过滤掉这个请求(使用req.url判断即可)。

二、fs模块

  Node.js文件系统被封装在fs模块中,它提供了文件的读取、写入、更名、删除、遍历目录以及链接等POSIX文件系统操作。与其他模块不同的是,fs模块中所有的操作都提供了异步的和同步的两个版本,例如读取文件内容的函数有异步的fs.readFile()和同步的fs.readFileSync()。

2.1 引入fs模块

require('fs');

2.2 读取文件(异步)

fs.readFile( filename [, encoding] [, callback(err, data)])

功能:读取指定路径的文件
参数

  • filename(必选):要读取文件的文件名
  • encoding(可选):指定解析文件内容的编码格式
  • callback(可选):用于接收文件的内容
    • err:当出现错误时,返回错误信息
    • data:如果指定了编码,data是一个解析后的字符串,否则date将会是Buffer形式表示的二进制数据

2.3 场景案例

  与AI-fisher项目同级的js文件读取项目中的index.html文件

const fs = require('fs');

fs.readFile('./AI-fisher/index.html',(err, data)=>{
    if(err){
        console.log(err);
    }else{
        console.log(data); 
    }
});
  • 当文件存在时,运行结果如下:
<Buffer 3c 21 44 4f 43 54 59 50 45 20 68 74 6d 6c 3e 0d 0a 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 20 6c 61 6e 67 3d 22 65 6e 22 3e 0d 0a 20 20 20 20 3c 6d 65 ... >

  获取数据成功,不过是Buffer流,如果想要转化成我们能看懂的信息,设置第二个参数即可:

const fs = require('fs');

fs.readFile('./AI-fisher/index.html','utf-8',(err, data)=>{
    if(err){
        console.log(err);
    }else{
        console.log(data); 
    }
});

  设置了编码之后的运行结果:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

</body>
</html>

  可以看到控制台打印出了这个html文件的内容

  • 当文件不存在时,运行结果如下:
{ Error: ENOENT: no such file or directory, open 'C:\Users\Administrator\Desktop\练习\AI-fisher\index.html'                                                                                     
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: 'C:\\Users\\Administrator\\Desktop\\练习\\AI-fisher\\index.html' }

获取数据失败,并给出错误信息

2.4 静态文件托管

  通过之前的一些操作,可以实现一个静态文件托管的需求。

2.4.1 简介

  在服务器上,一切都是资源, 我们发出的任何请求,都是请求资源。当我们在浏览器中输入www.baidu.com的时候,我们是向www.baidu.com服务器请求index.html资源,发送的请求是get请求。页面中可能会有css, img, js等文件,当html 文件在进行解析的时候,它碰到css, img, js时,就会向服务器发送请求,请求这些资源,这些请求也是get 请求。我们平时写js 代码,发送ajax 请求, 它请求的也是资源,只不过通常是json 字符串或xml。 在web 的领域,一切请求全都是为了资源,资源也可以任意的形式,文本文件,图片文件, 字符片段等。
而NodeJS通过fs.readFile()方法可以读取到前端请求的静态文件,然后把静态文件作为数据发送到前端去,这样就实现了前端通过请求被托管的静态文件来达到请求数据的需求。

2.4.2 静态页面托管模块

  这里封装了一个静态文件托管的模块,传入静态资源托管的根目录作为基地址,以助于后面找到静态文件托管的位置,前端请求哪个文件就返回哪个文件。特别地,如果访问了’/’,也返回’index.html’。
static.js

const fs = require('fs');

let static = (baseUrl,req,res) => {  
    let path = req.url === '/' ? '/index.html' : req.url; 
    fs.readFile(baseUrl + path, (err, data) => {   
        if (err) {     
            //b 读不到  -> readFile(404)      
            fs.readFile(baseUrl + '/error.html', (err, data) => { 
                res.write(data);
                res.end();
            })
        } else {      
            res.write(data);      
            res.end();
        }  
   })
}
module.exports = static;

三、url模块

  之前处理了静态文件托管,但是如果遇到动态数据请求就处理不了了,还需要引入一个url模块,该模块能够解析url,也就是前端通过get/post请求发送过来的url。

3.1 引入url模块

require('url');

3.2 解析url

url.parse(urlStr [, parseQueryString] [, slashesDenoteHost]);

功能:解析url
参数

  • urlStr(必选):url字符串
  • parseQueryString(可选)
    • false(默认值):不作解析,query属性的值为字符串
    • true:将query属性的值解析成对象
  • slashesDenoteHost(可选)
    • false(默认值)://foo/bar 形式的字符串将被解释成 { pathname: ‘//foo/bar’ }
    • true://foo/bar 形式的字符串将被解释成 { host: ‘foo’, pathname: ‘/bar’ }

返回值:将url解析后的对象

3.3 转为字符串

url.format(urlObj)

功能:输入一个解析过的URL对象,返回格式化过的字符串。(url.parse()的逆向过程)
参数:url对象
返回值:字符串

3.4 场景案例

const url = require('url');
let str = 'https://www.baidu.com/app/a/b/c.html?a=1&b=2#title';
console.log(url.parse(str));

运行结果如下:

Url {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'www.baidu.com',
  port: null,
  hostname: 'www.baidu.com',
  hash: '#title',
  search: '?a=1&b=2',
  query: 'a=1&b=2',
  pathname: '/app/a/b/c.html',
  path: '/app/a/b/c.html?a=1&b=2',
  href: 'https://www.baidu.com/app/a/b/c.html?a=1&b=2#title' }

如果在url.parse()的参数中加入一个true,即输出语句改为:

......
console.log(url.parse(str,true));
......

则运行结果变为:

Url {
  ......
  query: { a: '1', b: '2' },
  ......
  }

  对比之前的结果,可以发现只有query属性的值发生了变化,由字符串转为了对象。

const url = require('url');
let obj =  {    
    protocol: 'https:',    
    slashes: true,    
    auth: null,    
    host: 'www.baidu.com',    
    port: null,    
    hostname: 'www.baidu.com',    
    hash: '#title',    
    search: '?a=1&b=2',    
    query: 'a=1&b=2',    
    pathname: '/app/a/b/c.html',    
    path: '/app/a/b/c.html?a=1&b=2',    
    href: 'https://www.baidu.com/app/a/b/c.html?a=1&b=2#title' 
};
console.log(url.format(obj));

运行结果是:

https://www.baidu.com/app/a/b/c.html?a=1&b=2#title

  可以看到,url.format()方法把一个url对象拼接成了一个url字符串。

四、querystring模块

  该模块提供了一些处理query strings的工具,处理查询字符串(?key=value&key2=value2)

4.1 引入querystring模块

require('querystring');

4.2 字符串 → 对象

querystring.parse(str)

功能:将query string反序列化为对象
参数:字符串
返回值:对象

4.3 对象 → 字符串

querystring.stringify(obj)

功能:将一个对象序列化化为一个query string
参数:对象
返回值:字符串

4.4 场景案例

const querystring = require('querystring');
let str = 'a=1&b=2';
console.log(querystring.parse(str));        // { a: '1', b: '2' }

let obj = {    a: '1',    b: '2'};
console.log(querystring.stringify(obj));        // admin=1&password=2

4.5 动态数据请求

4.5.1 简介

  通过之前的介绍,我们可以就可以实现动态数据请求了,前端通过get/post/put/delete/ajax/jsonp等方式发送请求,后端通通过req.url或req.on()来接收请求中的参数,经过逻辑业务处理,发送到前端,从而实现了动态数据请求的过程。

4.5.2 动态数据请求模块

form.js

const urlLib = require('url');
const querystring = require('querystring')
let form = (req,res) => {
    //返回,接受浏览器带过来的参数数据
    console.log('地址栏数据',urlLib.parse(req.url,true).query);  
    let str = '';  
    req.on('data',(chunk)=>{     
        //chunk 一部分数据
        console.log('...data...') 
        str += chunk;  
    })  
    req.on('end',()=>{    
        console.log('拼接后的body数据',querystring.parse(str)) 
        res.write(`      
            {        
                url: ${req.url},        
                body: ${str}      
            }    
        `);    
        res.end();  
    })  
}
module.exports = form;

五、整合(demo)

  将之前提到的静态资源托管和动态数据请求结合到一起,实现前端的静态请求和动态请求,例如:访问项目文件夹下托管的静态文件,当用get请求拼接url或post提交data数据时,能够拿到提交的参数,并返回至前端。
【注】本案例只注重于功能的实现,忽略了项目结构的合理性,以下是项目结构

  • project
    • AI-fisher
      • error.html
      • index.html
    • mod
      • static.js
      • form.js
    • server.js

mod->static.js——静态资源托管模块

const fs = require('fs');

let static = (baseUrl,req,res) => {  
    let path = req.url === '/' ? '/index.html' : req.url; 
    fs.readFile(baseUrl + path, (err, data) => {   
        if (err) {     
            //b 读不到  -> readFile(404)      
            fs.readFile(baseUrl + '/error.html', (err, data) => { 
                res.write(data);
                res.end();
            })
        } else {      
            res.write(data);      
            res.end();
        }  
   })
}
module.exports = static;

mod->form.js——动态数据请求模块

const urlLib = require('url');
const querystring = require('querystring')
let form = (req,res) => {
    //返回,接受浏览器带过来的参数数据
    console.log('地址栏数据',urlLib.parse(req.url,true).query);  
    let str = '';  
    req.on('data',(chunk)=>{     
        //chunk 一部分数据
        console.log('...data...') 
        str += chunk;  
    })  
    req.on('end',()=>{    
        console.log('拼接后的body数据',querystring.parse(str)) 
        res.write(`      
            {        
                url: ${req.url},        
                body: ${str}      
            }    
        `);    
        res.end();  
    })  
}
module.exports = form;

server.js——开启服务,并提供入口

const http = require('http');
let static = require('./mod/static')
let form = require('./mod/form')
//搭建服务器
let server = http.createServer((req, res) => {  
    console.log(req.url)  
    if(req.url.indexOf('/form') !== -1){    
        //处理的是表单接口    
        form(req,res)  
    }else if(req.url.indexOf('/ajx') !== -1){    
        //ajax接口    
        form(req,res)  
    }else{    
        //处理静态资源,“AI-fisher”为项目文件夹   
        static('./AI-fisher',req,res);
    }  
});

server.listen(3000)
发布了24 篇原创文章 · 获赞 43 · 访问量 9802

猜你喜欢

转载自blog.csdn.net/weixin_37844113/article/details/99940519