NodeJs 简单的基于 http、url、querystring 模块的服务器项目示例(纯 NodeJs 库,无依赖第三方扩展包,支持跨域)

不依赖第三方扩展包,支持路由,支持简单的 GET、POST、PUT、DELETE 请求类型。
只有一个文件:server.js,源码如下:

// 文件路径:server.js
// 使用方法:node server.js,浏览器打开:http://127.0.0.1:3000

/**
 * NodeJs 简单的基于 http、url、querystring 模块的服务器项目示例(纯 NodeJs 库,无依赖第三方扩展包)
 */
const NmHttp = require('http');
const NmUrl = require('url');
const NmQueryString = require('querystring');
// 引入 MD5 加密模块
const NmCrypto = require('crypto');

// 配置信息
const host = {
  ip: '127.0.0.1',
  port: 3000
}

// 简易的路由类
class Route {
  routes = {};
  getKey(method, url) {
    return NmCrypto.createHash('md5').update(method + url).digest('hex').substring(8, 16);
  }
  hasRoute(method = '', url = '') {
    return typeof (this.routes[this.getKey(method.toUpperCase(), url)]) != 'undefined';
  }
  getRoute(method = '', url = '') {
    return this.routes[this.getKey(method.toUpperCase(), url)];
  }
  getRouteCallBack(method = '', url = '') {
    return this.routes[this.getKey(method.toUpperCase(), url)].cb;
  }
  addRoute(method = '', url = '', cb = null) {
    method = method.toUpperCase();
    this.routes[this.getKey(method, url)] = {
      method: method,
      url: url,
      cb: cb
    };
  }
  get(url, cb) {
    this.addRoute('GET', url, cb);
  }
  post(url, cb) {
    this.addRoute('POST', url, cb);
  }
  put(url, cb) {
    this.addRoute('PUT', url, cb);
  }
  delete(url, cb) {
    this.addRoute('DELETE', url, cb);
  }
  header(url, cb) {
    this.addRoute('HEADER', url, cb);
  }
}
let routers = getRouters();

// ===== 核心功能 =====

// 创建服务实例
const server = NmHttp.createServer((req, res) => {
  // 处理输入请求参数
  // 解析地址。req.url=/,是相对路径,所以要加 baseUrl 参数
  let urlObj = new NmUrl.URL(req.url, 'http://' + host.ip);
  let urlPathName = urlObj.pathname; // 路由路径(?前面的相对路径)
  // 获取 URL 参数对象
  let urlParams = urlObj.searchParams;
  // 获取表单提交的内容
  let reqBody = '';
  req.on('data', function (data) {
    reqBody += data;
  })
  // 用于数据接收完成后再获取
  req.on('end', function () {
    let formParams = NmQueryString.parse(reqBody);
    // 获取请求类型(GET,POST,DELETE...)
    let reqMethod = req.method;
    // 伪装请求类型处理,可以在表单中带 _method 文本域,值可为 GET,POST,PUT,DELETE。
    if (urlParams.has('_method')) {
      reqMethod = urlParams.get('_method');
    }
    if (typeof (formParams['_method']) != 'undefined') {
      reqMethod = formParams['_method'];
    }
    // 返回内容
    let content = {
      status: 0,
      msg: '路由错误 ',
      data: {
        method: reqMethod,
        path: urlPathName
      },
      code: 404
    };
    // 简易路由处理
    if (routers.hasRoute(reqMethod, urlPathName)) {
      content = routers.getRouteCallBack(reqMethod, urlPathName)(urlParams, formParams);
    }
	// 处理跨域
	res.setHeader("Access-Control-Allow-Origin", "*");
	res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
	res.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
	res.setHeader("X-Powered-By",' 3.2.1')
    // 处理输出
    if (content.status == 0 && content.code == 404) {
      res.writeHead(404);
      res.end(JSON.stringify(content));
      return;
    }
    res.statusCode = 200;
    switch (typeof (content)) {
      case 'object':
        content = JSON.stringify(content);
        res.setHeader('Content-Type', 'application/json');
        res.setHeader('Content-Length', Buffer.byteLength(content))
        res.end(content)
        break;
      default:
        if (content.indexOf('<') > -1 && content.indexOf('>') > -1) {
          res.setHeader('Content-Type', 'text/html');
        } else {
          res.setHeader('Content-Type', 'text/plain');
        }
        res.setHeader('Content-Length', Buffer.byteLength(content))
        res.end(content);
        break;
    }
  })
})

// 开始监听
server.listen(host.port, host.ip, () => {
  console.log(`服务器运行在 http://${host.ip}:${host.port}`);
})


// ===== 业务功能 =====

// 获取路由配置
function getRouters() {
  let route = new Route();
  // 测试 http://127.0.0.1:3000/test
  route.get('/test',function(urlParams){
	return {status:1,msg:'测试成功',data:{ts:new Date().getTime()},code:1}  
  })
  // 默认首页
  route.get('/', function (urlParams) {
    return `<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>演示</title>
    </head>
    <body>
        <h2>演示</h2>
        <form target="_blank">
            编号ID:<input type="number" name="id" value="1" /><br />
            用户名:<input type="text" name="user_name" value="user-001" /><br />
            密码:<input type="password" name="password" value="123456" /><br />
            性别:<input type="radio" name="sex" value="1" checked /> 男 <input type="radio" name="sex" value="2" /> 女<br />
            <hr>
            路由地址:
            <select id="routePicker">
                <option value="">==请选择==</option>
                <option value="/user/read" data-method="GET">获取用户信息</option>
                <option value="/user/save" data-method="POST">新增用户</option>
                <option value="/user/update" data-method="PUT">更新用户</option>
                <option value="/user/delete" data-method="DELETE">删除用户</option>
            </select><br />
            <div id="routeInfo">路由信息:</div>
            <hr>
            <input type="hidden" name="_method">
            <button type="submit">提交表单</button> <button type="reset">重置表单</button>
        </form>
        <script>
            let elRouteInfo = document.querySelector('#routeInfo');
            let elRoutePicker = document.querySelector('#routePicker')
            let elForm = document.querySelector('form');
            let elMethod = document.querySelector('input[name="_method"]');
            elRoutePicker.addEventListener('change', function (evt) {
                let elOption = this.options[this.selectedIndex];
                elRouteInfo.innerHTML = '方式:' + elOption.dataset.method + ',路径:' + elOption.value;
                elForm.setAttribute('method', elOption.dataset.method == 'GET' ? 'GET' : 'POST');
                elForm.setAttribute('action', elOption.value);
                elMethod.value = elOption.dataset.method;
            })
            elForm.addEventListener('submit', function (evt) {
                evt.preventDefault();
                if (!elForm.hasAttribute('method') || !elForm.hasAttribute('action')) {
                    alert('未选择路由地址');
                    return false;
                }
                elForm.submit();
            })
        </script>
    </body>
    </html>`;
  })

  route.get('/user/read', function (urlParams) {
    let id = urlParams.has('id') ? parseInt(urlParams.get('id')) : 0;
    return {
      id: id,
      user_name: 'user-' + id,
      sex: 0
    };
  })

  route.post('/user/save', function (urlParams, formParams) {
    return {
      status: 1,
      msg: '保存成功',
      data: formParams,
      code: 0
    }
  })

  route.put('/user/update', function (urlParams, formParams) {
    let id = urlParams.has('id') ? parseInt(urlParams.get('id')) : 0;
    return {
      status: 1,
      msg: '更新成功',
      data: formParams,
      code: 0
    }
  })

  route.delete('/user/delete', function (urlParams, formParams) {
    let id = urlParams.has('id') ? urlParams.get('id') : null;
    if (id < 1 && typeof (formParams.id) != 'undefined') {
      id = formParams.id;
    }
    if (!id) {
      return {
        status: 0,
        msg: '编号无效',
        data: null,
        code: 0
      }
    }
    return {
      status: 1,
      msg: '删除成功',
      data: {
        id: id
      },
      code: 0
    }
  })

  return route;
}

运行:

node server.js

在浏览器打开如下URL

http://127.0.0.1:3000

界面效果:
默认首页界面

posted on 2021-01-19 17:10  sochishun  阅读(119)  评论(0编辑  收藏  举报