Logo Vincent
返回文章列表

一篇文章开发Node.js-WebServer

Node.js
一篇文章开发Node.js-WebServer

【前言】

Node.js的服务端框架很多,耳熟能详的有express,koa等,

本文从零到一开发一个Node.js的web server。

https://nodejs.org/en/

https://expressjs.com/

https://koa.bootcss.com/

【http】

要实现nodejs web server,需要依赖于nodejs的http模块,

https://nodejs.org/dist/latest-v16.x/docs/api/http.html

这里直接切入主题,使用http模块启动一个web server,

代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');
});

// listen
server.listen(8080);

启动后可以本地访问http://localhost:8080/,

这时可以看到控制台打印日志,说明一个最简单的web server启动成功,

【res.end】

虽然已经收到了请求,但是没有对请求作出响应,浏览器一直在转圈,

这里可以通过http提供的response进行响应,代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  response.end('hello world');
});

// listen
server.listen(8080);

再次请求,已经可以看到响应了,如下

【parseurl】

目前已经可以启动一个简单的nodejs web server,

并且返回一段文字给浏览器,

接着就是解析用户访问的url,

这里推荐使用parseurl这个npm包进行解析,

https://www.npmjs.com/package/parseurl

这个库可以将url解析为js对象,代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  const url = require('parseurl')(request);
  console.log('url:', url);

  response.end('hello world');
});

// listen
server.listen(8080);

然后请求一个复杂的url, http://localhost:8080/get/info?id=1

可以看到控制条输出,将url解析为了js对象,

【query】

解析url后,可以看到返回了query参数,

例如,请求地址为:http://localhost:8080/get/info?id=1,

其中的id=1就是query,

这里推荐使用qs这个npm包进行解析,

https://www.npmjs.com/package/qs

解析代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  const url = require('parseurl')(request);
  const query = require('qs').parse(url.query);
  console.log('query:', query);

  response.end('hello world');
});

// listen
server.listen(8080);

再次请求,可以看到控制台输出,将query解析为了js对象,

【headers】

怎么获取一个请求中的headers呢,

可以通过request中的rawHeaders获取,代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  console.log('headers:', request.rawHeaders);

  response.end('hello world');
});

// listen
server.listen(8080);

再次请求,可以看到控制台输出,

headers数组,奇数为key,偶数为value

【cookies】

接着来获取cookies,

先手动在浏览器devtools中写一下cookie,

key是test,value是hello,如下

再次请求,可以看到控制台输出,headers中多了Cookie,

这里推荐使用cookie这个npm包解析,

https://www.npmjs.com/package/cookie

代码如下:

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  const cookies = 'test=hello';
  const obj = require('cookie').parse(cookies);
  console.log('cookies:', obj);

  response.end('hello world');
});

// listen
server.listen(8080);

再次请求,可以看到控制台输出,将cookie解析为了js对象,

【useragent】

同样的,也可以解析useragent,

首先,从headers中获取useragent,

这里推荐qiao-ua这个npm库进行解析,

https://www.npmjs.com/package/qiao-ua

解析代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  const ua =
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36';
  const obj = require('qiao-ua')(ua);
  console.log('user-agent', obj);

  response.end('hello world');
});

// listen
server.listen(8080);

再次请求后,可以看到控制台输出,将useragent解析为js对象,

返回了浏览器信息,操作系统信息,平台信息,引擎信息,是否为手机等,

【body】

最常见的http请求方式包括get请求,post请求,

get请求的参数可以通过上述query解析获取,

post请求的参数一般要获取body参数,

发起post最常见的方式是通过form表单,

form表单常见的内容格式有:

1.application/x-www-form-urlencoded,默认格式

2.multipart/form-data,上传文件时使用

3.application/json,json格式

这里先解析除了第二种格式的请求,

推荐使用raw-body这个npm包来解析,

https://www.npmjs.com/package/raw-body

代码如下,

// http
const http = require('http');

// raw body
const getRawBody = require('raw-body');

// server
const server = http.createServer();

// request
server.on('request', async (request, response) => {
  console.log('request');

  // headers
  const headers = handleHeaders(request);

  // content type
  const contentType = headers['content-type'];

  // options
  const options = {
    length: headers['content-length'],
    limit: '1mb',
    encoding: true,
  };

  // body str
  const bodyString = await getRawBody(request, options);
  console.log(contentType, bodyString);

  response.end('hello world');
});

// listen
server.listen(8080);

// handle headers
function handleHeaders(request) {
  const headers = {};

  // check
  const rawHeaders = request.rawHeaders;
  if (!rawHeaders || !rawHeaders.length) return headers;

  // handle
  rawHeaders.forEach((h, i) => {
    if (i % 2 == 0) headers[h.toLowerCase()] = rawHeaders[i + 1];
  });
  return headers;
}

这次需要发起一个post请求,可以使用apipost发起,https://www.apipost.cn/

发起post请求后,可以看到控制台输出,打印了content-type和body,

这时就可以根据content-type的类型来解析body了,

// body str
let body;
const bodyString = await getRawBody(request, options);
if (contentType.indexOf('application/x-www-form-urlencoded') > -1) {
  body = require('qs').parse(bodyString);
}
if (contentType.indexOf('application/json') > -1) {
  body = JSON.parse(bodyString);
}
console.log(contentType, bodyString, body);

再次请求,可以看到控制台输出,打印了content-type,bodyString,以及解析后的body

【upload】

上面body解析,第二种格式比较特殊,

multipart/form-data,是上传文件时的格式,

这里推荐使用qiao-z-upload这个npm包解析上传时的body,

https://www.npmjs.com/package/qiao-z-upload

代码如下

// body
if (contentType.indexOf('multipart/form-data') > -1) {
  const body = await require('qiao-z-upload').uploadSync(request);
  console.log(contentType, body);
}

解析后会返回文件和字段

{
    fields: fields,
    files: files
}

【cros】

跨域请求比较常见,怎么处理跨域请求呢,

只需要在response的时候设置对应的头即可,

这里都返回了*,实际中可以按需返回,

代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  response.writeHead(200, {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': '*',
    'Access-Control-Allow-Headers': '*',
  });

  response.end('hello world');
});

// listen
server.listen(8080);

再次请求,可以看到devtools中的请求,

其中响应头已经有了对应信息,

【redirect】

如何将一个请求重定向,代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  const url = 'https://insistime.com/';

  response.writeHead(302, { Location: url });
  response.end();
});

// listen
server.listen(8080);

再次请求http://localhost:8080,

可以看到直接重定向到了 https://insistime.com/

【send】

如何返回文字响应,代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  response.writeHead(200, { 'Content-Type': 'text/plain' });
  response.end('hello world');
});

// listen
server.listen(8080);

再次请求,可以看到devtools中的响应头如下,

【json】

如何返回json响应,代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  const obj = {
    name: 'jack',
  };
  const json = JSON.stringify(obj);

  response.writeHead(200, { 'Content-Type': 'application/json' });
  response.end(json);
});

// listen
server.listen(8080);

再次请求,可以看到devtools中的响应头如下,

【render】

如何渲染文件呢,最常见的是渲染html,代码如下

// http
const http = require('http');

// server
const server = http.createServer();

// request
server.on('request', (request, response) => {
  console.log('request');

  const htmlPath = require('path').resolve(__dirname, '../views/index.html');
  const html = require('fs').readFileSync(htmlPath, { encoding: 'utf8' });

  response.writeHeader(200, { 'Content-Type': 'text/html' });
  response.write(html);
  response.end();
});

// listen
server.listen(8080);

再次请求,可以看到返回了html页面,如下

【qiao-z】

封装了一个npm包,欢迎使用, https://qiao-z.vincentqiao.com/#/

安装

npm i qiao-z

启动服务

// app
const app = require('qiao-z')();

// listen
app.listen(5277);

注册路由

建议使用如下文件结构

|--server
    |--controller
        |--IndexController.js
    |--config.json
|--views
    |--index.html
|--app.js
|--package.json

会自动将Controller.js结尾的文件注册,例如

// index controller
// 注册get请求
// /*代码所有访问都会分配到这个路由下
module.exports = function (app) {
  // index
  app.get('/*', function (req, res) {
    res.render('./views/index.html');
  });
};

模板渲染

支持模板渲染,更多: https://qiao-z.vincentqiao.com/#/guides/pages

const data = {
  user: {
    name: 'jack',
  },
};
res.render('./views/index.html', data);

静态文件

支持访问静态文件,优先级高于其他请求

app.static('/static', './static');

get请求

支持get请求,/*通配,解析query,解析params

// all get request
app.get('/*', function (req, res) {
  res.render('./views/index.html');
});

// url/:code
app.get('/url/:code', function (req, res) {
  // req.params
  console.log(req.params.code);

  // render
  res.render('./views/index.html');
});

// url?code=1
app.get('/url', function (req, res) {
  // req.query
  console.log(req.query.code);

  // render
  res.render('./views/index.html');
});

post请求

支持post请求,解析body

// post
app.post('/url', function (req, res) {
  // req.body
  console.log(req.body);

  // render
  res.render('./views/index.html');
});

支持post请求,解析文件上传,需要引入qiao-z-upload,

// config
const config = require('./server/config.json');

// qz
const qz = require('qiao-z');

// options
const options = {
  // upload,处理文件上传请求,会将文件信息返回到req.body
  upload: require('qiao-z-upload'),
};

// app
const app = qz(options);

// listen
app.listen(config.port);

跨域请求

支持跨域请求,如下

// config
const config = require('./server/config.json');

// qz
const qz = require('qiao-z');

// options
const options = {
  // cros,是否开启跨域请求
  // cros: true等价于
  // cros: {
  //     'Access-Control-Allow-Origin': '*',
  //     'Access-Control-Allow-Methods': '*',
  //     'Access-Control-Allow-Headers': '*',
  // }
  cros: true,

  // cros,自定义
  cros: {
    'Access-Control-Allow-Origin': 'xx',
    'Access-Control-Allow-Methods': 'xx',
    'Access-Control-Allow-Headers': 'xx',
  },
};

// app
const app = qz(options);

// listen
app.listen(config.port);

req解析

解析request,

1.支持解析url,req.url

2.支持解析headers,req.headers

3.支持解析cookies,req.cookies

4.支持解析useragent,req.useragent

res响应

1.支持重定向,res.redirect

2.支持返回文本,res.send

3.支持返回json,res.json

4.支持模板渲染,res.render

【总结】

1.http模块介绍

2.启动nodejs web server

3.解析url

4.解析query

5.解析headers

6.解析cookies

7.解析useragent

8.解析body

9.解析文件上传时的body

10.跨域请求

11.请求重定向

12.响应文本

13.响应json

14.渲染html

15.极简nodejs web server框架:qiao-z, https://qiao-z.vincentqiao.com/#/

© 2026 vincentqiao.com . 保留所有权利。