Skip to content
一、开发接口
1.nodejs 处理 http 请求
  • http 请求概述

    • DNS 解析,建立 TCP 连接,发送 http 请求
    • server 接受到 http 请求,处理,并返回
    • 客户端接受到返回数据,处理数据(如渲染页面,执行js)
  • nodejs 如何处理

    • get 请求 和 querystring

      • get 请求,即客户端要向 server 端获取数据,如查询博客列表
      • 通过 querystring 来传递数据,如a.html?a=100&b=200
      • 浏览器直接访问,就发送 get 请求
        js
        const http = require('http');
        const querystring = require('querystring');
        
        const server = http.createServer((req, res) => {
          console.log(req.method);
          const url = req.url;
          req.query = querystring.parse(url.split('?')[1]); // 解析 querystring
          res.end(JSON.stringify(req.query)); // 将 querystring 返回
        })
        
        server.listen(8000);
        
        // 然后浏览器访问 http://localhost:8000/
    • post 请求 和 postdata

      • post 请求,即客户端要像服务端传递数据,如新建博客
      • 通过 post data 传递数据
      • 浏览器无法直接模拟,需要手写js,或者使用 postman等第三方工具
        js
        const http = require('http');
        
        const server = http.createServer((req, res) => {
          if (req.method === 'POST') {
            // 数据格式
            console.log('content-type', req.headers['content-type']);
            let postData = "";
            req.on('data', chunk => {
              postData += chunk.toString()
            })
            req.on('end', () => {
              console.log(postData);
              res.end('hello world'); // 在这里返回,因为异步
            })
          }
        })
        server.listen(8000);
        
        // 然后浏览器访问 http://localhost:8000/
    • 路由

    • 简单示例

      js
      const http = require('http');
      
      const server = http.createServer((req, res) => {
        res.end('hello world');
      })
      
      server.listen(8000);
      
      // 然后浏览器访问 http://localhost:8000/
    • 综合示例

      js
      const http = require('http');
      const querystring = require('querystring');
      
      const server = http.createServer((req, res) => {
        const method = req.method;
        const url = req.url;
        const path = url.split('?')[0];
        const query = querystring.parse(url.split('?')[1]);
        // 设置返回格式 JSON
        res.setHeader('Content-type', 'application/json');
        // 返回的数据
        const resData = {
          method,
          url,
          path,
          query
        }
        // 返回
        if (method === 'GET') {
          res.end(JSON.stringify(resData))
        }
        if (method === 'POST') {
          let postData = '';
          res.on('data', chunk => {
            postData += chunk.toString()
          })
          res.on('end', () => {
            resData.postData = postData;
            // 返回
            res.end(JSON.stringify(resData))
          })
        }
      })
      
      server.listen(8000);
      
      // 然后浏览器访问 http://localhost:8000/
2.搭建开发环境
  • 从0开始搭建,不使用任何框架
  • 使用 nodemon 监测文件变化,自动重启 node
  • 使用 cross-env 设置环境变量,兼容 mac linux 和 windows
  • 搭建bolg1
    • 相关命令
      shell
      mkdir blog1
      cd blog1
      npm init -y
      touch bin/www.js
      touch app.js
      npm i nodemon -S
      npm i cross-env -S
    • app.js
      js
      const serverhandler = (req, res) => {
        // 设置返回格式 JSON
        res.setHeader('Content-type', 'application/json');
      
        const resData = {
          name: '张三',
          site: 'baidu',
          env: process.env.NODE_ENV
        }
        res.end(JSON.stringify(resData))
      }
      
      module.exports = serverhandler
    • www.js
      js
      const http = require('http');
      const PORT = 8000;
      
      const serverhandler = require('../app');
      const server = http.createServer(serverhandler);
      server.listen(PORT)
    • package.json
      json
      {
        "name": "blog1",
        "version": "1.0.0",
        "description": "",
        "main": "app.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          "dev": "cross-env NODE_ENV=dev nodemon ./bin/www.js",
          "pro": "cross-env NODE_ENV=production nodemon ./bin/www.js"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
          "cross-env": "^7.0.3",
          "nodemon": "^2.0.15"
        }
      }
    • 启动命令
      shell
      npm run dev
3.开发接口(暂不连接数据库,暂不考虑登录)
  • 初始化路由:根据之前技术方案的设计,做出路由
  • 返回假数据:将路由和数据处理分离,以符合设计原则
  • 接口定义
    • src/router/blog.js
      js
      const handlerBlogRouter = (req, res) => {
        const {method, path } = req
        // 获取博客列表
        if (method === 'GET' && path === '/api/blog/list') {
          return {
            msg: '这是获取博客列表的接口'
          }
        }
        // 获取博客详情
        if (method === 'GET' && path === '/api/blog/detail') {
          return {
            msg: '这是获取博客详情的接口'
          }
        }
        // 新建一篇博客
        if (method === 'POST' && path === '/api/blog/new') {
          return {
            msg: '这是新建博客的接口'
          }
        }
        // 更新一篇博客
        if (method === 'POST' && path === '/api/blog/update') {
          return {
            msg: '这是更新博客的接口'
          }
        }
        // 删除一篇博客
        if (method === 'POST' && path === '/api/blog/delete') {
          return {
            msg: '这是删除博客的接口'
          }
        }
      }
      module.exports = handlerBlogRouter
    • src/router/user.js
      js
      const handlerUserRouter = (req, res) => {
        const { method, path } = req
        // 登录
        if (method === 'POST' && path === '/api/user/login') {
          return {
            msg: '这是登录接口'
          }
        }
      }
      
      module.exports = handlerUserRouter
  • 开发路由
    • 定义统一返回数据格式
      js
      class BaseModel {
        constructor(data, message) {
          if (typeof data === 'string') {
            this.message = data;
            data = null;
            message = null;
          }
      
          if (data) {
            this.data = data;
          }
      
          if (message) {
            this.message = message
          }
        }
      }
      
      class SuccessModel extends BaseModel {
        constructor(data, message) {
          super(data, message);
          this.errno = 0
        }
      }
      
      class ErrorModel extends BaseModel {
        constructor(data, message) {
          super(data, message);
          this.errno = -1
        }
      }
      
      module.exports = {
        SuccessModel,
        ErrorModel
      }
    • 数据适配 [controller/blog.js]
      js
      const getList = (author, keyword) => {
        // 先返回假数据
        return [
          {
            id: 1,
            title: '标题A',
            content: '内容A',
            createTime: 1649147318532,
            author: '张三'
          },
          {
            id: 2,
            title: '标题B',
            content: '内容B',
            createTime: 1649147318533,
            author: '李四'
          }
        ]
      }
      
      module.exports = {
        getList
      }
    • 路由应用[router/blog.js]
      js
      const { getList } = require('../controller/blog');
      const { SuccessModel } = require('../model/resModel');
      
      const handlerBlogRouter = (req, res) => {
        const {method, path } = req
        // 获取博客列表
        if (method === 'GET' && path === '/api/blog/list') {
          const author = req.query.author || '';
          const keyword = req.query.keyword || '';
          const listData = getList(author, keyword);
      
          return new SuccessModel(listData);
        }
      }
      module.exports = handlerBlogRouter
    • post接口处理 getPostData /api/blog/new
      js
      const querystring = require('querystring');
      const handlerBlogRouter = require('./src/router/blog');
      const handlerUserRouter = require('./src/router/user');
      
      // 用户处理 post data
      const getPostData = (req) => {
        const promise = new Promise((resolve, reject) => {
          if (req.method !== 'POST') {
            resolve({});
            return;
          }
          if (req.headers['content-type'] !== 'application/json') {
            resolve({});
            return;
          }
          let postData = '';
          req.on('data', chunk => {
            postData += chunk.toString();
          })
          req.on('end', () => {
            if (!postData) {
              resolve({})
              return
            }
            resolve(JSON.parse(postData))
          })
        })
        return promise;
      }
      
      
      const serverhandler = (req, res) => {
        // 设置返回格式 JSON
        res.setHeader('Content-type', 'application/json');
      
        // 获取path
        const { url } = req;
        req.path = url.split('?')[0];
      
        // 解析 query
        req.query = querystring.parse(url.split('?')[1]);
      
        // 处理 post data
        getPostData(req).then(postData => {
          req.body = postData;
          // 处理 blog 路由
          const blogData = handlerBlogRouter(req, res);
          if (blogData) {
            res.end(JSON.stringify(blogData));
            return
          }
          // 处理 user 路由
          const userData = handlerUserRouter(req,res);
          if (userData) {
            res.end(JSON.stringify(userData));
            return;
          }
          // 未命中路由,返回 404
          res.writeHead(404, {"Content-type": "text/plain"});
          res.write("404 Not Found\n");
          res.end()
        })
      }
      
      module.exports = serverhandler

Released under the MIT License.