(008)-使用node.js 搭建你的服务器

60 阅读5分钟

node.js是JavaScript的一种运行环境,是对Google V8引擎的封装,是服务器端的JavaScript解释器。NPM全名是Node Package Manager,node.js中含有npm,安装node.js时会一起安装npm,npm作用就是对node.js依赖的包进行管理,是node.js的包管理工具。

Step 1: 创建本地文件夹,server,通过vscode打开该文件夹

Step 2: npm init

在server目录下初始化node,构建package.json

npm init -y

// 或者通过 npm init 自定义配置

Step 3: 创建 app.js文件作为服务器入口

const http = require('http');

const port = 3000;
const hostName = '127.0.0.1';

const server = http.createServer((req,res)=>{
    console.log('server running');
});

server.listen(port,hostName,function(){
    console.log(`server is running at ${hostName}:${port}`);
});

Step 4: 启动服务器

方式一:app.js 文件上右键 Run Code,此时点击vscode右上角🛑可停止服务器

方式二:控制台 TEMINAL运行 node app.js

方式三:npm i -g nodemon 全局安装nodemon,'nodemon app.js'

此时,listen方法监听到服务器已经开始运行,输出 server is running at 127.0.0.1:3000

Step 5: 访问服务器

浏览器输入 127.0.0.1:3000 并回车。此时控制台接收到浏览器发起的请求并响应输出 server running

浏览器此时一直处于等待响应状态。原因在于服务器已经接收到请求,但服务器没有答复。

image.png

Step 6: 服务器响应请求

const server = http.createServer((req,res)=>{
    console.log('server running');
    res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
    res.end('this is a response from server'); // 响应信息
});

重启服务器,使得改动生效。

image.png


Step 7: 尝试向服务器请求一个网页,服务获取该次请求的参数

7.1 改动服务器端代码,增加req信息的输出

const server = http.createServer((req,res)=>{
    console.log('server running');
    res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
    res.end('this is a response from server'); // 响应信息

    console.log(req); // 输出请求信息
});

7.2 浏览器器输入http://127.0.0.1:3000/index.html

此时,输出req信息如下(部分)。url: '/index.html'

  rawHeaders: [    // 浏览器发起的请求头文件   request  header
    'Host',
    '127.0.0.1:3000',
    'Connection',
    'keep-alive',
    'Cache-Control',
    'max-age=0',
    'sec-ch-ua',
    '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"',
    'sec-ch-ua-mobile',
    '?0',
    'sec-ch-ua-platform',
    '"Windows"',
    'DNT',
    '1',
    'Upgrade-Insecure-Requests',
    '1',
    'User-Agent',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36',
    'Accept',
    'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'Sec-Fetch-Site',
    'none',
    'Sec-Fetch-Mode',
    'navigate',
    'Sec-Fetch-User',
    '?1',
    'Sec-Fetch-Dest',
    'document',
    'Accept-Encoding',
    'gzip, deflate, br',
    'Accept-Language',
    'en,zh;q=0.9,ja;q=0.8,ar;q=0.7,it-IT;q=0.6,it;q=0.5,zh-CN;q=0.4,ko;q=0.3'
  ],
  trailers: {},
  rawTrailers: [],
  aborted: false,
  upgrade: false,
  url: '/index.html',
  method: 'GET',
  statusCode: null,
  statusMessage: null,
  client: <ref *1> Socket {
    connecting: false,
    _hadError: false,
    _parent: null,
    _host: null,
    _readableState: ReadableState {
      objectMode: false,

7.3 服务器端通过req.url获取请求参数 此处即是 http://127.0.0.1:3000/index.html 中的 /index.html

修改服务器代码 console.log(req.url);

image.png

Step 8: 服务器发送HTML文件

8.1 app.js 中引入fs模块

const http = require('http');
const fs = require('fs'); // node自带文件读取模块
// 使用方法
// fs.readFile('文件路径',文件解析方式,回调函数)

8.2 根目录serve下新建src子文件,用于存放静态资源,html文件,css文件,js文件,图片等。src文件夹下新建index.html (! tab)

image.png

8.3 修改app.js 读取该文件

const server = http.createServer((req,res)=>{
    console.log('server running');
    res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
    res.end('this is a response from server'); // 响应信息

    console.log(req.url);

    // 读取文件
    fs.readFile('./src/index.html','utf-8',(err,data)=>{
        console.log('读取信息',data);
    });
});

此时,浏览器发起请求后,控制台会输出html信息

8.4 继续修改,将html信息返回给浏览器

    fs.readFile('./src/index.html','utf-8',(err,data)=>{
        res.end(data); // 响应信息
    });

此时,readFile得到的内容其实是html字符串,通过res.end发送出去,浏览器会直接解析为html字符串。

image.png

8.5 设置响应头

    fs.readFile('./src/index.html','utf-8',(err,data)=>{
        // res.end(data); // 响应信息
        // 1. res.writeHead(响应状态码,'配置解析方法,告诉浏览器这个文件你要解析成啥样')
        res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'});
        // 2. 发送
        res.write(data);
        // 3. 发送结束,告诉浏览器本次响应结束
        res.end();
    });

image.png

Step 9: 服务器新增 css 和 js 文件

image.png

添加简单css样式并引入html。

修改app.js代码,加判断 if(req.url == '/index.html')

    if(req.url == '/index.html'){
        fs.readFile('./src/index.html','utf-8',(err,data)=>{
            // res.end(data); // 响应信息
            // 1. res.writeHead(响应状态码,'配置解析方法,告诉浏览器这个文件你要解析成啥样')
            res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'});
            // 2. 发送
            res.write(data);
            // 3. 发送结束,告诉浏览器本次响应结束
            res.end();
        });
    }

此时,浏览器发起请求后一直处于等待响应状态,通过服务器的命令行可以发现,浏览器多了一个css的请求,

image.png

这是因为浏览器在解析html文件时发现html中引入外部的css文件,浏览器会再次发起请求。这时,服务器只需再次响应浏览器的请求即可。

else if(req.url == '/css/index.css'){
        fs.readFile('./src/css/index.css','utf-8',(err,data)=>{
            res.writeHead(200,{'Content-Type':'Text/css;Chartset=utf-8'});
            res.write(data);
            res.end();
        });
    }

再次启动服务器,浏览器请求后拿到样式文件。

同样的,对于js文件

else  if(req.url=='/js/index.js'){
        fs.readFile('VueDemo/serve/src/js/index.js','utf-8',(err,data)=>{
            res.writeHead(200,{'Content-Type':'Text/javascript;Chartset=utf-8'})
            res.write(data);
            res.end()
        })
    }

优化

const myserver = myhttp.createServer((req,res)=>{ //通过myserver去http模块的createServer方法去创建服务,req为请求对象, res为响应对象
    res.setHeader('Content-Type','text/plain');
    let fileType = req.url.split('.');
    if (fileType[1]=='html') {
        // fs.readFile('文件路径',文件解析方式,回调函数)
        fs.readFile(`./src/${fileType[0]}.html`,'utf-8',(err,data)=>{
            res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'})
            res.write(data);
            res.end()
        })
    }else  if(fileType[1]=='css'){
        fs.readFile(`./src/${fileType[0]}.css`,'utf-8',(err,data)=>{
            res.writeHead(200,{'Content-Type':'Text/css;Chartset=utf-8'})
            res.write(data);
            res.end()
        })
    }else  if(fileType[1]=='js'){
        fs.readFile(`./src/${fileType[0]}.js`,'utf-8',(err,data)=>{
            res.writeHead(200,{'Content-Type':'Text/javascript;Chartset=utf-8'})
            res.write(data);
            res.end()
        })
    }else  if(fileType[1]=='jpg' || fileType[1]=='png'){//或者更多种类
        fs.readFile(`./src/${fileType}`,(err,data)=>{
            res.writeHead(200,{'Content-Type':'Text/javascript;Chartset=utf-8'})
            res.write(data);
            res.end()
        })
    }
})

Step 10: 抽离服务器入口文件里的响应逻辑

新建controller文件夹 -> indexCtrl.js

image.png

将createServer箭头函数中的响应逻辑放到该文件中,indexCtrl.js

const fs = require('fs'); // node自带文件读取模块

module.exports = {
    sendHtml(url,res){
        fs.readFile('./src/index.html','utf-8',(err,data)=>{
            // res.end(data); // 响应信息
            // 1. res.writeHead(响应状态码,'配置解析方法,告诉浏览器这个文件你要解析成啥样')
            res.writeHead(200,{'Content-Type':'Text/Html;Chartset=utf-8'});
            // 2. 发送
            res.write(data);
            // 3. 发送结束,告诉浏览器本次响应结束
            res.end();
        });
    },

    sendCss(url,res){
        fs.readFile('./src/css/index.css','utf-8',(err,data)=>{
            res.writeHead(200,{'Content-Type':'Text/css;Chartset=utf-8'});
            res.write(data);
            res.end();
        });
    },
}

通过 module.exports 公开,然后在app.js中引入,

const control = require('./src/controller/indexCtrl');

    if(req.url == '/index.html'){
        control.sendHtml(req.url,res);
    } else if(req.url == '/css/index.css'){
        control.sendCss(req.url,res);
    }

Step 11: 抽离服务器入口文件的路由逻辑

新建 routers -> indexRouter.js

image.png

const control = require('../src/controller/indexCtrl');

module.exports = {

    pageRoute(res,req){
        if(req.url == '/index.html'){
            control.sendHtml(req.url,res);
        } else if(req.url == '/css/index.css'){
            control.sendCss(req.url,res);
        }
    }
    
}

同时,修改 app.js

const routers = require('./routers/indexRouter');

const server = http.createServer((req,res)=>{
    console.log('server running');
    res.setHeader('Content-Type','text/plain'); // 设置响应头,告知浏览器传递回去的内容格式
    
    console.log(req.url);

    routers.pageRoute(res,req);

});

此时,将服务器入口文件app.js拆分成三个文件,其中 controller用于响应请求,router用于路由处理。

Step 12: express

参考文献:

通过fetch看跨域:是谁阻止了跨域请求?

Fetch

使用NodeJS创建HTTP服务器

node.js从零开始搭建服务器

连接MySQL数据库