欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

實(shí)現(xiàn)一個(gè)完整的Node.js RESTful API的示例

 更新時(shí)間:2017年09月29日 10:04:33   作者:馬在路上  
本篇文章主要介紹了實(shí)現(xiàn)一個(gè)完整的Node.js RESTful API的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

前言

這篇文章算是對(duì)Building APIs with Node.js這本書(shū)的一個(gè)總結(jié)。用Node.js寫(xiě)接口對(duì)我來(lái)說(shuō)是很有用的,比如在項(xiàng)目初始階段,可以快速的模擬網(wǎng)絡(luò)請(qǐng)求。正因?yàn)樗胘s寫(xiě)的,跟iOS直接的聯(lián)系也比其他語(yǔ)言寫(xiě)的后臺(tái)更加接近。

這本書(shū)寫(xiě)的極好,作者編碼的思路極其清晰,整本書(shū)雖說(shuō)是用英文寫(xiě)的,但很容易讀懂。同時(shí),它完整的構(gòu)建了RESTful API的一整套邏輯。

我更加喜歡寫(xiě)一些函數(shù)響應(yīng)式的程序,把函數(shù)當(dāng)做數(shù)據(jù)或參數(shù)進(jìn)行傳遞對(duì)我有著莫大的吸引力。

從程序的搭建,到設(shè)計(jì)錯(cuò)誤捕獲機(jī)制,再到程序的測(cè)試任務(wù),這是一個(gè)完整的過(guò)程。這邊文章將會(huì)很長(zhǎng),我會(huì)把每個(gè)核心概念的代碼都黏貼上來(lái)。

環(huán)境搭建

下載并安裝Node.js https://nodejs.org/en/

安裝npm

下載演示項(xiàng)目

git clone https://github.com/agelessman/ntask-api

進(jìn)入項(xiàng)目文件夾后運(yùn)行

npm install

上邊命令會(huì)下載項(xiàng)目所需的插件,然后啟動(dòng)項(xiàng)目

npm start

訪問(wèn)接口文檔 http://localhost:3000/apidoc

程序入口

Express 這個(gè)框架大家應(yīng)該都知道,他提供了很豐富的功能,我在這就不做解釋了,先看該項(xiàng)目中的代碼:

import express from "express"
import consign from "consign"

const app = express();

/// 在使用include或者then的時(shí)候,是有順序的,如果傳入的參數(shù)是一個(gè)文件夾
/// 那么他會(huì)按照文件夾中文件的順序進(jìn)行加載
consign({verbose: false})
 .include("libs/config.js")
 .then("db.js")
 .then("auth.js")
 .then("libs/middlewares.js")
 .then("routers")
 .then("libs/boot.js")
 .into(app);

module.exports = app;

不管是models,views還是routers都會(huì)經(jīng)過(guò) Express 的加工和配置。在該項(xiàng)目中并沒(méi)有使用到views的地方。 Express 通過(guò)app對(duì)整個(gè)項(xiàng)目的功能進(jìn)行配置,但我們不能把所有的參數(shù)和方法都寫(xiě)到這一個(gè)文件之中,否則當(dāng)項(xiàng)目很大的時(shí)候?qū)⒓彪y維護(hù)。

我使用Node.js的經(jīng)驗(yàn)是很少的,但上面的代碼給我的感覺(jué)就是極其簡(jiǎn)潔,思路極其清晰,通過(guò) consign 這個(gè)模塊導(dǎo)入其他模塊在這里就讓代碼顯得很優(yōu)雅。

@note:導(dǎo)入的順序很重要。

在這里,app的使用很像一個(gè)全局變量,這個(gè)我們會(huì)在下邊的內(nèi)容中展示出來(lái),按序?qū)牒螅覀兙涂梢酝ㄟ^(guò)這樣的方式訪問(wèn)模塊的內(nèi)容了:

app.db
app.auth
app.libs....

模型設(shè)計(jì)

在我看來(lái),在開(kāi)始做任何項(xiàng)目前,需求分析是最重要的,經(jīng)過(guò)需求分析后,我們會(huì)有一個(gè)關(guān)于代碼設(shè)計(jì)的大的概念。

編碼的實(shí)質(zhì)是什么?我認(rèn)為就是數(shù)據(jù)的存儲(chǔ)和傳遞,同時(shí)還需要考慮性能和安全的問(wèn)題

因此我們第二部的任務(wù)就是設(shè)計(jì)數(shù)據(jù)模型,同時(shí)可以反應(yīng)出我們需求分析的成果。在該項(xiàng)目中有兩個(gè)模型, User 和 Task ,每一個(gè) task 對(duì)應(yīng)一個(gè) user ,一個(gè) user 可以有多個(gè) task

用戶模型:

import bcrypt from "bcrypt"

module.exports = (sequelize, DataType) => {
 "use strict";
 const Users = sequelize.define("Users", {
  id: {
   type: DataType.INTEGER,
   primaryKey: true,
   autoIncrement: true
  },
  name: {
   type: DataType.STRING,
   allowNull: false,
   validate: {
    notEmpty: true
   }
  },
  password: {
   type: DataType.STRING,
   allowNull: false,
   validate: {
    notEmpty: true
   }
  },
  email: {
   type: DataType.STRING,
   unique: true,
   allowNull: false,
   validate: {
    notEmpty: true
   }
  }
 }, {
  hooks: {
   beforeCreate: user => {
    const salt = bcrypt.genSaltSync();
    user.password = bcrypt.hashSync(user.password, salt);
   }
  }
 });
 Users.associate = (models) => {
  Users.hasMany(models.Tasks);
 };
 Users.isPassword = (encodedPassword, password) => {
  return bcrypt.compareSync(password, encodedPassword);
 };

 return Users;
};

任務(wù)模型:

module.exports = (sequelize, DataType) => {
 "use strict";
 const Tasks = sequelize.define("Tasks", {
  id: {
   type: DataType.INTEGER,
   primaryKey: true,
   autoIncrement: true
  },
  title: {
   type: DataType.STRING,
   allowNull: false,
   validate: {
    notEmpty: true
   }
  },
  done: {
   type: DataType.BOOLEAN,
   allowNull: false,
   defaultValue: false
  }
 });
 Tasks.associate = (models) => {
  Tasks.belongsTo(models.Users);
 };
 return Tasks;
};

該項(xiàng)目中使用了系統(tǒng)自帶的 sqlite 作為數(shù)據(jù)庫(kù),當(dāng)然也可以使用其他的數(shù)據(jù)庫(kù),這里不限制是關(guān)系型的還是非關(guān)系型的。為了更好的管理數(shù)據(jù),我們使用 sequelize 這個(gè)模塊來(lái)管理數(shù)據(jù)庫(kù)。

為了節(jié)省篇幅,這些模塊我就都不介紹了,在google上一搜就出來(lái)了。在我看的Node.js的開(kāi)發(fā)中,這種ORM的管理模塊有很多,比如說(shuō)對(duì) MongoDB 進(jìn)行管理的 mongoose 。很多很多,他們主要的思想就是Scheme。

在上邊的代碼中,我們定義了模型的輸出和輸入模板,同時(shí)對(duì)某些特定的字段進(jìn)行了驗(yàn)證,因此在使用的過(guò)程中就有可能會(huì)產(chǎn)生來(lái)自數(shù)據(jù)庫(kù)的錯(cuò)誤,這些錯(cuò)誤我們會(huì)在下邊講解到。

Tasks.associate = (models) => {
  Tasks.belongsTo(models.Users);
};

Users.associate = (models) => {
 Users.hasMany(models.Tasks);
};
Users.isPassword = (encodedPassword, password) => {
 return bcrypt.compareSync(password, encodedPassword);
};

hasMany 和 belongsTo 表示一種關(guān)聯(lián)屬性, Users.isPassword 算是一個(gè)類(lèi)方法。 bcrypt 模塊可以對(duì)密碼進(jìn)行加密編碼。

數(shù)據(jù)庫(kù)

在上邊我們已經(jīng)知道了,我們使用 sequelize 模塊來(lái)管理數(shù)據(jù)庫(kù)。其實(shí),在最簡(jiǎn)單的層面而言,數(shù)據(jù)庫(kù)只需要給我們數(shù)據(jù)模型就行了,我們拿到這些模型后,就能夠根據(jù)不同的需求,去完成各種各樣的CRUD操作。

import fs from "fs"
import path from "path"
import Sequelize from "sequelize"

let db = null;


module.exports = app => {
 "use strict";
 if (!db) {
  const config = app.libs.config;
  const sequelize = new Sequelize(
   config.database,
   config.username,
   config.password,
   config.params
  );

  db = {
   sequelize,
   Sequelize,
   models: {}
  };

  const dir = path.join(__dirname, "models");

  fs.readdirSync(dir).forEach(file => {
   const modelDir = path.join(dir, file);
   const model = sequelize.import(modelDir);
   db.models[model.name] = model;
  });

  Object.keys(db.models).forEach(key => {
   db.models[key].associate(db.models);
  });
 }
 return db;
};

上邊的代碼很簡(jiǎn)單,db是一個(gè)對(duì)象,他存儲(chǔ)了所有的模型,在這里是 User 和 Task 。通過(guò) sequelize.import 獲取模型,然后又調(diào)用了之前寫(xiě)好的associate方法。

上邊的函數(shù)調(diào)用之后呢,返回db,db中有我們需要的模型,到此為止,我們就建立了數(shù)據(jù)庫(kù)的聯(lián)系,作為對(duì)后邊代碼的一個(gè)支撐。

CRUD

CRUD在router中,我們先看看 router/tasks.js 的代碼:

module.exports = app => {
 "use strict";
 const Tasks = app.db.models.Tasks;

 app.route("/tasks")
  .all(app.auth.authenticate())

  .get((req, res) => {
   console.log(`req.body: ${req.body}`);
   Tasks.findAll({where: {user_id: req.user.id} })
    .then(result => res.json(result))
    .catch(error => {
     res.status(412).json({msg: error.message});
    });
  })

  .post((req, res) => {
   req.body.user_id = req.user.id;
   Tasks.create(req.body)
    .then(result => res.json(result))
    .catch(error => {
     res.status(412).json({msg: error.message});
    });
  });

 app.route("/tasks/:id")
  .all(app.auth.authenticate())

  .get((req, res) => {
   Tasks.findOne({where: {
    id: req.params.id,
    user_id: req.user.id
   }})
    .then(result => {
     if (result) {
      res.json(result);
     } else {
      res.sendStatus(412);
     }
    })
    .catch(error => {
     res.status(412).json({msg: error.message});
    });
  })

  .put((req, res) => {
   Tasks.update(req.body, {where: {
    id: req.params.id,
    user_id: req.user.id
   }})
    .then(result => res.sendStatus(204))
    .catch(error => {
     res.status(412).json({msg: error.message});
    });
  })

  .delete((req, res) => {
   Tasks.destroy({where: {
    id: req.params.id,
    user_id: req.user.id
   }})
    .then(result => res.sendStatus(204))
    .catch(error => {
     res.status(412).json({msg: error.message});
    });
  });
};

再看看 router/users.js 的代碼:

module.exports = app => {
 "use strict";
 const Users = app.db.models.Users;

 app.route("/user")
  .all(app.auth.authenticate())

 .get((req, res) => {
   Users.findById(req.user.id, {
    attributes: ["id", "name", "email"]
   })
    .then(result => res.json(result))
    .catch(error => {
     res.status(412).json({msg: error.message});
    });
  })

  .delete((req, res) => {
  console.log(`delete..........${req.user.id}`);
   Users.destroy({where: {id: req.user.id}})
    .then(result => {
     console.log(`result: ${result}`);
     return res.sendStatus(204);
    })
    .catch(error => {
     console.log(`resultfsaddfsf`);
     res.status(412).json({msg: error.message});
    });
  });

 app.post("/users", (req, res) => {
  Users.create(req.body)
   .then(result => res.json(result))
   .catch(error => {
    res.status(412).json({msg: error.message});
   });
 });
};

這些路由寫(xiě)起來(lái)比較簡(jiǎn)單,上邊的代碼中,基本思想就是根據(jù)模型操作CRUD,包括捕獲異常。但是額外的功能是做了authenticate,也就是授權(quán)操作。

這一塊好像沒(méi)什么好說(shuō)的,基本上都是固定套路。

授權(quán)

在網(wǎng)絡(luò)環(huán)境中,不能老是傳遞用戶名和密碼。這時(shí)候就需要一些授權(quán)機(jī)制,該項(xiàng)目中采用的是JWT授權(quán)(JSON Wbb Toknes),有興趣的同學(xué)可以去了解下這個(gè)授權(quán),它也是按照一定的規(guī)則生成token。

因此對(duì)于授權(quán)而言,最核心的部分就是如何生成token。

import jwt from "jwt-simple"

module.exports = app => {
 "use strict";
 const cfg = app.libs.config;
 const Users = app.db.models.Users;

 app.post("/token", (req, res) => {
  const email = req.body.email;
  const password = req.body.password;
  if (email && password) {
   Users.findOne({where: {email: email}})
    .then(user => {
     if (Users.isPassword(user.password, password)) {
      const payload = {id: user.id};
      res.json({
       token: jwt.encode(payload, cfg.jwtSecret)
      });
     } else {
      res.sendStatus(401);
     }
    })
    .catch(error => res.sendStatus(401));
  } else {
   res.sendStatus(401);
  }
 });
};

上邊代碼中,在得到郵箱和密碼后,再使用 jwt-simple 模塊生成一個(gè)token。

JWT在這也不多說(shuō)了,它由三部分組成,這個(gè)在它的官網(wǎng)中解釋的很詳細(xì)。

我覺(jué)得老外寫(xiě)東西一個(gè)最大的優(yōu)點(diǎn)就是文檔很詳細(xì)。要想弄明白所有組件如何使用,最好的方法就是去他們的官網(wǎng)看文檔,當(dāng)然這要求英文水平還可以。

授權(quán)一般分兩步:

  • 生成token
  • 驗(yàn)證token

如果從前端傳遞一個(gè)token過(guò)來(lái),我們?cè)趺唇馕鲞@個(gè)token,然后獲取到token里邊的用戶信息呢?

import passport from "passport";
import {Strategy, ExtractJwt} from "passport-jwt";

module.exports = app => {
 const Users = app.db.models.Users;
 const cfg = app.libs.config;
 const params = {
  secretOrKey: cfg.jwtSecret,
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
 };
 var opts = {};
 opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme("JWT");
 opts.secretOrKey = cfg.jwtSecret;

 const strategy = new Strategy(opts, (payload, done) => {
  Users.findById(payload.id)
   .then(user => {
    if (user) {
     return done(null, {
      id: user.id,
      email: user.email
     });
    }
    return done(null, false);
   })
   .catch(error => done(error, null));
 });
 passport.use(strategy);

 return {
  initialize: () => {
   return passport.initialize();
  },
  authenticate: () => {
   return passport.authenticate("jwt", cfg.jwtSession);
  }
 };
};

這就用到了 passport 和 passport-jwt 這兩個(gè)模塊。 passport 支持很多種授權(quán)。不管是iOS還是Node中,驗(yàn)證都需要指定一個(gè)策略,這個(gè)策略是最靈活的一層。

授權(quán)需要在項(xiàng)目中提前進(jìn)行配置,也就是初始化, app.use(app.auth.initialize()); 。

如果我們想對(duì)某個(gè)接口進(jìn)行授權(quán)驗(yàn)證,那么只需要像下邊這么用就可以了:

.all(app.auth.authenticate())

.get((req, res) => {
 console.log(`req.body: ${req.body}`);
 Tasks.findAll({where: {user_id: req.user.id} })
  .then(result => res.json(result))
  .catch(error => {
   res.status(412).json({msg: error.message});
  });
})

配置

Node.js中一個(gè)很有用的思想就是middleware,我們可以利用這個(gè)手段做很多有意思的事情:

import bodyParser from "body-parser"
import express from "express"
import cors from "cors"
import morgan from "morgan"
import logger from "./logger"
import compression from "compression"
import helmet from "helmet"

module.exports = app => {
 "use strict";
 app.set("port", 3000);
 app.set("json spaces", 4);
 console.log(`err ${JSON.stringify(app.auth)}`);
 app.use(bodyParser.json());
 app.use(app.auth.initialize());
 app.use(compression());
 app.use(helmet());
 app.use(morgan("common", {
  stream: {
   write: (message) => {
    logger.info(message);
   }
  }
 }));
 app.use(cors({
  origin: ["http://localhost:3001"],
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"]
 }));
 app.use((req, res, next) => {
  // console.log(`header: ${JSON.stringify(req.headers)}`);
  if (req.body && req.body.id) {
   delete req.body.id;
  }
  next();
 });

 app.use(express.static("public"));
};

上邊的代碼中包含了很多新的模塊,app.set表示進(jìn)行設(shè)置,app.use表示使用middleware。

測(cè)試

寫(xiě)測(cè)試代碼是我平時(shí)很容易疏忽的地方,說(shuō)實(shí)話,這么重要的部分不應(yīng)該被忽視。

import jwt from "jwt-simple"

describe("Routes: Users", () => {
 "use strict";
 const Users = app.db.models.Users;
 const jwtSecret = app.libs.config.jwtSecret;
 let token;

 beforeEach(done => {
  Users
   .destroy({where: {}})
   .then(() => {
    return Users.create({
     name: "Bond",
     email: "Bond@mc.com",
     password: "123456"
    });
   })
   .then(user => {
    token = jwt.encode({id: user.id}, jwtSecret);
    done();
   });
 });

 describe("GET /user", () => {
  describe("status 200", () => {
   it("returns an authenticated user", done => {
    request.get("/user")
     .set("Authorization", `JWT ${token}`)
     .expect(200)
     .end((err, res) => {
      expect(res.body.name).to.eql("Bond");
      expect(res.body.email).to.eql("Bond@mc.com");
      done(err);
     });
   });
  });
 });

 describe("DELETE /user", () => {
  describe("status 204", () => {
   it("deletes an authenticated user", done => {
    request.delete("/user")
     .set("Authorization", `JWT ${token}`)
     .expect(204)
     .end((err, res) => {
      console.log(`err: ${err}`);
      done(err);
     });
   });
  });
 });

 describe("POST /users", () => {
  describe("status 200", () => {
   it("creates a new user", done => {
    request.post("/users")
     .send({
      name: "machao",
      email: "machao@mc.com",
      password: "123456"
     })
     .expect(200)
     .end((err, res) => {
      expect(res.body.name).to.eql("machao");
      expect(res.body.email).to.eql("machao@mc.com");
      done(err);
     });
   });
  });
 });
});

測(cè)試主要依賴下邊的這幾個(gè)模塊:

import supertest from "supertest"
import chai from "chai"
import app from "../index"

global.app = app;
global.request = supertest(app);
global.expect = chai.expect;

其中 supertest 用來(lái)發(fā)請(qǐng)求的, chai 用來(lái)判斷是否成功。

使用 mocha 測(cè)試框架來(lái)進(jìn)行測(cè)試:

"test": "NODE_ENV=test mocha test/**/*.js",

生成接口文檔

接口文檔也是很重要的一個(gè)環(huán)節(jié),該項(xiàng)目使用的是 ApiDoc.js 。這個(gè)沒(méi)什么好說(shuō)的,直接上代碼:

/**
 * @api {get} /tasks List the user's tasks
 * @apiGroup Tasks
 * @apiHeader {String} Authorization Token of authenticated user
 * @apiHeaderExample {json} Header
 * {
 *  "Authorization": "xyz.abc.123.hgf"
 * }
 * @apiSuccess {Object[]} tasks Task list
 * @apiSuccess {Number} tasks.id Task id
 * @apiSuccess {String} tasks.title Task title
 * @apiSuccess {Boolean} tasks.done Task is done?
 * @apiSuccess {Date} tasks.updated_at Update's date
 * @apiSuccess {Date} tasks.created_at Register's date
 * @apiSuccess {Number} tasks.user_id The id for the user's
 * @apiSuccessExample {json} Success
 * HTTP/1.1 200 OK
 * [{
 *  "id": 1,
 *  "title": "Study",
 *  "done": false,
 *  "updated_at": "2016-02-10T15:46:51.778Z",
 *  "created_at": "2016-02-10T15:46:51.778Z",
 *  "user_id": 1
 * }]
 * @apiErrorExample {json} List error
 * HTTP/1.1 412 Precondition Failed
 */
 
 /**
 * @api {post} /users Register a new user
 * @apiGroup User
 * @apiParam {String} name User name
 * @apiParam {String} email User email
 * @apiParam {String} password User password
 * @apiParamExample {json} Input
 * {
 *  "name": "James",
 *  "email": "James@mc.com",
 *  "password": "123456"
 * }
 * @apiSuccess {Number} id User id
 * @apiSuccess {String} name User name
 * @apiSuccess {String} email User email
 * @apiSuccess {String} password User encrypted password
 * @apiSuccess {Date} update_at Update's date
 * @apiSuccess {Date} create_at Rigister's date
 * @apiSuccessExample {json} Success
 * {
 *  "id": 1,
 *  "name": "James",
 *  "email": "James@mc.com",
 *  "updated_at": "2016-02-10T15:20:11.700Z",
 *  "created_at": "2016-02-10T15:29:11.700Z"
 * }
 * @apiErrorExample {json} Rergister error
 * HTTP/1.1 412 Precondition Failed
 */

大概就類(lèi)似與上邊的樣子,既可以做注釋用,又可以自動(dòng)生成文檔,一石二鳥(niǎo),我就不上圖了。

準(zhǔn)備發(fā)布

到了這里,就只剩下發(fā)布前的一些操作了,

有的時(shí)候,處于安全方面的考慮,我們的API可能只允許某些域名的訪問(wèn),因此在這里引入一個(gè)強(qiáng)大的模塊 cors ,介紹它的文章,網(wǎng)上有很多,大家可以直接搜索,在該項(xiàng)目中是這么使用的:

app.use(cors({
 origin: ["http://localhost:3001"],
 methods: ["GET", "POST", "PUT", "DELETE"],
 allowedHeaders: ["Content-Type", "Authorization"]
}));

這個(gè)設(shè)置在本文的最后的演示網(wǎng)站中,會(huì)起作用。

打印請(qǐng)求日志同樣是一個(gè)很重要的任務(wù),因此引進(jìn)了 winston 模塊。下邊是對(duì)他的配置:

import fs from "fs"
import winston from "winston"

if (!fs.existsSync("logs")) {
 fs.mkdirSync("logs");
}

module.exports = new winston.Logger({
 transports: [
  new winston.transports.File({
   level: "info",
   filename: "logs/app.log",
   maxsize: 1048576,
   maxFiles: 10,
   colorize: false
  })
 ]
});

打印的結(jié)果大概是這樣的:

{"level":"info","message":"::1 - - [26/Sep/2017:11:16:23 +0000] \"GET /tasks HTTP/1.1\" 200 616\n","timestamp":"2017-09-26T11:16:23.089Z"}
{"level":"info","message":"::1 - - [26/Sep/2017:11:16:43 +0000] \"OPTIONS /user HTTP/1.1\" 204 0\n","timestamp":"2017-09-26T11:16:43.583Z"}
{"level":"info","message":"Tue Sep 26 2017 19:16:43 GMT+0800 (CST) Executing (default): SELECT `id`, `name`, `password`, `email`, `created_at`, `updated_at` FROM `Users` AS `Users` WHERE `Users`.`id` = 342;","timestamp":"2017-09-26T11:16:43.592Z"}
{"level":"info","message":"Tue Sep 26 2017 19:16:43 GMT+0800 (CST) Executing (default): SELECT `id`, `name`, `email` FROM `Users` AS `Users` WHERE `Users`.`id` = 342;","timestamp":"2017-09-26T11:16:43.596Z"}
{"level":"info","message":"::1 - - [26/Sep/2017:11:16:43 +0000] \"GET /user HTTP/1.1\" 200 73\n","timestamp":"2017-09-26T11:16:43.599Z"}
{"level":"info","message":"::1 - - [26/Sep/2017:11:16:49 +0000] \"OPTIONS /user HTTP/1.1\" 204 0\n","timestamp":"2017-09-26T11:16:49.658Z"}
{"level":"info","message":"Tue Sep 26 2017 19:16:49 GMT+0800 (CST) Executing (default): SELECT `id`, `name`, `password`, `email`, `created_at`, `updated_at` FROM `Users` AS `Users` WHERE `Users`.`id` = 342;","timestamp":"2017-09-26T11:16:49.664Z"}
{"level":"info","message":"Tue Sep 26 2017 19:16:49 GMT+0800 (CST) Executing (default): DELETE FROM `Users` WHERE `id` = 342","timestamp":"2017-09-26T11:16:49.669Z"}
{"level":"info","message":"::1 - - [26/Sep/2017:11:16:49 +0000] \"DELETE /user HTTP/1.1\" 204 -\n","timestamp":"2017-09-26T11:16:49.714Z"}
{"level":"info","message":"::1 - - [26/Sep/2017:11:17:04 +0000] \"OPTIONS /token HTTP/1.1\" 204 0\n","timestamp":"2017-09-26T11:17:04.905Z"}
{"level":"info","message":"Tue Sep 26 2017 19:17:04 GMT+0800 (CST) Executing (default): SELECT `id`, `name`, `password`, `email`, `created_at`, `updated_at` FROM `Users` AS `Users` WHERE `Users`.`email` = 'xiaoxiao@mc.com' LIMIT 1;","timestamp":"2017-09-26T11:17:04.911Z"}
{"level":"info","message":"::1 - - [26/Sep/2017:11:17:04 +0000] \"POST /token HTTP/1.1\" 401 12\n","timestamp":"2017-09-26T11:17:04.916Z"}

性能上,我們使用Node.js自帶的cluster來(lái)利用機(jī)器的多核,代碼如下:

import cluster from "cluster"
import os from "os"

const CPUS = os.cpus();

if (cluster.isMaster) {
 // Fork
 CPUS.forEach(() => cluster.fork());

 // Listening connection event
 cluster.on("listening", work => {
  "use strict";
  console.log(`Cluster ${work.process.pid} connected`);
 });

 // Disconnect
 cluster.on("disconnect", work => {
  "use strict";
  console.log(`Cluster ${work.process.pid} disconnected`);
 });

 // Exit
 cluster.on("exit", worker => {
  "use strict";
  console.log(`Cluster ${worker.process.pid} is dead`);
  cluster.fork();
 });

} else {
 require("./index");
}

在數(shù)據(jù)傳輸上,我們使用 compression 模塊對(duì)數(shù)據(jù)進(jìn)行了gzip壓縮,這個(gè)使用起來(lái)比較簡(jiǎn)單:

app.use(compression());

最后,讓我們支持https訪問(wèn),https的關(guān)鍵就在于證書(shū),使用授權(quán)機(jī)構(gòu)的證書(shū)是最好的,但該項(xiàng)目中,我們使用http://www.selfsignedcertificate.com這個(gè)網(wǎng)站自動(dòng)生成了一組證書(shū),然后啟用https的服務(wù):

import https from "https"
import fs from "fs"

module.exports = app => {
 "use strict";
 if (process.env.NODE_ENV !== "test") {

  const credentials = {
   key: fs.readFileSync("44885970_www.localhost.com.key", "utf8"),
   cert: fs.readFileSync("44885970_www.localhost.com.cert", "utf8")
  };

  app.db.sequelize.sync().done(() => {

   https.createServer(credentials, app)
    .listen(app.get("port"), () => {
    console.log(`NTask API - Port ${app.get("port")}`);
   });
  });
 }
};

當(dāng)然,處于安全考慮,防止攻擊,我們使用了 helmet 模塊:

app.use(helmet());

前端程序

為了更好的演示該API,我把前段的代碼也上傳到了這個(gè)倉(cāng)庫(kù)https://github.com/agelessman/ntaskWeb,直接下載后,運(yùn)行就行了。

API的代碼連接https://github.com/agelessman/ntask-api

總結(jié)

我覺(jué)得這本書(shū)寫(xiě)的非常好,我收獲很多。它雖然并不復(fù)雜,但是該有的都有了,因此我可以自由的往外延伸。同時(shí)也學(xué)到了作者駕馭代碼的能力。

我覺(jué)得我還達(dá)不到把所學(xué)所會(huì)的東西講明白。有什么錯(cuò)誤的地方,還請(qǐng)給予指正。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • node.js快速部署vue代碼詳細(xì)步驟

    node.js快速部署vue代碼詳細(xì)步驟

    眾所周知Vue是現(xiàn)在前端最流行的框架之一,作為前端開(kāi)發(fā)人員應(yīng)該要熟練的掌握它,下面這篇文章主要給大家介紹了關(guān)于node.js快速部署vue代碼的詳細(xì)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • ubuntu下安裝nodejs以及升級(jí)的辦法

    ubuntu下安裝nodejs以及升級(jí)的辦法

    本文介紹了ubuntu 12.04服務(wù)器安裝nodejs以及升級(jí)的方法,ubuntu安裝nodejs以及升級(jí)的實(shí)例教程,需要的朋友參考下。
    2015-05-05
  • Express URL跳轉(zhuǎn)(重定向)的實(shí)現(xiàn)方法

    Express URL跳轉(zhuǎn)(重定向)的實(shí)現(xiàn)方法

    Express是一個(gè)基于Node.js實(shí)現(xiàn)的Web框架,其響應(yīng)HTTP請(qǐng)求的response對(duì)象中有兩個(gè)用于URL跳轉(zhuǎn)方法res.location()和res.redirect(),使用它們可以實(shí)現(xiàn)URL的301或302重定向。
    2017-04-04
  • Node.js(安裝,啟動(dòng),測(cè)試)

    Node.js(安裝,啟動(dòng),測(cè)試)

    這里主要介紹基于windows平臺(tái)上最簡(jiǎn)單方便的安裝方式,啟動(dòng)以及簡(jiǎn)單測(cè)試
    2014-06-06
  • NODE.JS加密模塊CRYPTO常用方法介紹

    NODE.JS加密模塊CRYPTO常用方法介紹

    這篇文章主要介紹了NODE.JS加密模塊CRYPTO常用方法介紹,需要的朋友可以參考下
    2014-06-06
  • node文件上傳功能簡(jiǎn)易實(shí)現(xiàn)代碼

    node文件上傳功能簡(jiǎn)易實(shí)現(xiàn)代碼

    本篇文章主要介紹了node文件上傳功能簡(jiǎn)易實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • Node.js中的模塊機(jī)制學(xué)習(xí)筆記

    Node.js中的模塊機(jī)制學(xué)習(xí)筆記

    這篇文章主要介紹了Node.js中的模塊機(jī)制學(xué)習(xí)筆記,本文講解了CommonJS模塊規(guī)范、Node模塊實(shí)現(xiàn)過(guò)程、模塊調(diào)用棧、包與NPM等內(nèi)容,需要的朋友可以參考下
    2014-11-11
  • 利用C/C++編寫(xiě)node.js原生模塊的方法教程

    利用C/C++編寫(xiě)node.js原生模塊的方法教程

    這篇文章主要給大家介紹了關(guān)于利用C/C++編寫(xiě)node.js原生模塊的相關(guān)資料,文中將實(shí)現(xiàn)的步驟一步步的介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起看看吧。
    2017-07-07
  • node.js中的fs.readFileSync方法使用說(shuō)明

    node.js中的fs.readFileSync方法使用說(shuō)明

    這篇文章主要介紹了node.js中的fs.readFileSync方法使用說(shuō)明,本文介紹了fs.readFileSync的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • node?NPM庫(kù)qs?iconv-lite字符串編碼轉(zhuǎn)換及解析URL查詢學(xué)習(xí)

    node?NPM庫(kù)qs?iconv-lite字符串編碼轉(zhuǎn)換及解析URL查詢學(xué)習(xí)

    這篇文章主要為大家介紹了node?NPM庫(kù)之qs解析URL查詢字符串及iconv-lite字符串編碼轉(zhuǎn)換學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07

最新評(píng)論