js_脚本之家金沙8331网址

前言

那篇小说算是对Building APIs with
Node.js那本书的三个总计。用Node.js写接口对自个儿的话是很有用的,举例在档次始于阶段,能够高速的比葫芦画瓢网络要求。正因为它用js写的,跟iOS直接的关系也比其他语言写的后台越发雷同。

那本书写的极好,小编编码的思绪最为清晰,整本书虽说是用西班牙语写的,但相当轻巧读懂。同不经常间,它完整的营造了RESTful
API的一站式逻辑。

自家进一步中意写一些函数响应式的程序,把函数当作数据或参数举办传递对自己具备莫斯中国科学技术大学学的重力。

从程序的搭建,到规划不当捕获机制,再到程序的测量试验任务,那是三个一体化的长河。那边作品将会不长,作者会把各类大旨概念的代码都黏贴上来。

条件搭建

下载并设置Node.js

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

npm install

上边命令会下载项目所需的插件,然后运转项目

npm start

寻访接口文书档案

次第入口

Express
那几个框架我们应该都理解,他提供了很充分的效率,笔者在此就不做表达了,先看该品种中的代码:

import express from "express"import consign from "consign"const app = express();/// 在使用include或者then的时候,是有顺序的,如果传入的参数是一个文件夹/// 那么他会按照文件夹中文件的顺序进行加载consign .include .then .then .then("libs/middlewares.js") .then .then .into;module.exports = app;

无论是models,views依然routers都会由此 Express
的加工和安顿。在该类型中并未使用到views的地点。 Express
通过app对一切项指标效果拓宽布置,但大家不可能把富有的参数和措施都写到那叁个文件之中,不然当项目超大的时候将困难维护。

本人使用Node.js的经验是相当少的,但上边的代码给本人的以为正是无比简单,思路最为清晰,通过
consign 那么些模块导入其余模块在那地就让代码显得非常高贵。

@note:导入的次第很要紧。

在那处,app的施用很像三个全局变量,那么些我们会在底下的剧情中显得出来,按序导入后,大家就足以因此如此的秘技访谈模块的原委了:

app.dbapp.authapp.libs....

模型设计

以小编之见,在起先做任何类型前,须要解析是最关键的,经过需要解析后,我们会有三个有关代码设计的大的定义。

编码的精气神儿是何许?作者感觉正是数码的仓库储存和传递,同一时候还亟需考虑品质和安全的主题材料

故此大家第二部的任务正是设计数据模型,同不经常候能够反应出咱们要求深入分析的结晶。在该项目中有五个模型,
User 和 Task ,每多个 task 对应四个 user ,三个 user 能够有四个 task

import bcrypt from "bcrypt"module.exports =  => { "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; } } }); Users.associate =  => { Users.hasMany; }; Users.isPassword = (encodedPassword, password) => { return bcrypt.compareSync(password, encodedPassword); }; return Users;};

module.exports =  => { "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 =  => { Tasks.belongsTo; }; return Tasks;};

该品种中央银行使了系统自带的 sqlite
作为数据库,当然也能够动用别的的数据库,这里不限量是关系型的还是非关系型的。为了更加好的管理数据,我们利用
sequelize 那些模块来保管数据库。

为了省去篇幅,那么些模块小编就都不介绍了,在google上一搜就出来了。在本身看的Node.js的费用中,这种ORM的管住模块有不计其数,比如说对
MongoDB 进行拘留的 mongoose 。超多广大,他们第一的研究就是Scheme。

在上面的代码中,大家定义了模型的输出和输入模板,相同的时间对有个别特定的字段进行了认证,由此在选用的进程中就有极大概率会爆发根源数据库的失实,这么些不当大家会在底下疏解到。

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

金沙8331网址,hasMany 和 belongsTo 表示一种关系属性, Users.isPassword
算是二个类形式。 bcrypt 模块能够对密码实行加密编码。

数据库

在上面大家曾经知晓了,大家接受 sequelize
模块来保管数据库。其实,在最简便易行的范畴来说,数据库只要求给我们数据模型就能够了,大家取得这么些模型后,就能够依据区别的需要,去做到有滋有味标CRUD操作。

import fs from "fs"import path from "path"import Sequelize from "sequelize"let db = null;module.exports = app => { "use strict"; if  { 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; fs.readdirSync.forEach(file => { const modelDir = path.join; const model = sequelize.import; db.models[model.name] = model; }); Object.keys.forEach(key => { db.models[key].associate; } return db;};

上边的代码相当轻巧,db是叁个指标,他存款和储蓄了富有的模型,在那处是 User 和
Task 。通过 sequelize.import
获取模型,然后又调用了事情发生前写好的associate方法。

上边的函数调用之后呢,再次回到db,db中有大家要求的模子,到此停止,大家就创建了数据库的维系,作为对前面代码的多个支撑。

CRUD

CRUD在router中,大家先看看 router/tasks.js 的代码:

module.exports = app => { "use strict"; const Tasks = app.db.models.Tasks; app.route .all(app.auth.authenticate => { console.log(`req.body: ${req.body}`); Tasks.findAll({where: {user_id: req.user.id} }) .then(result => res.json .catch(error => { res.status.json; }); }) .post => { req.body.user_id = req.user.id; Tasks.create .then(result => res.json .catch(error => { res.status.json; }); }); app.route .all(app.auth.authenticate => { Tasks.findOne({where: { id: req.params.id, user_id: req.user.id }}) .then(result => { if  { res.json; } else { res.sendStatus .catch(error => { res.status.json; }); }) .put => { Tasks.update(req.body, {where: { id: req.params.id, user_id: req.user.id }}) .then(result => res.sendStatus .catch(error => { res.status.json; }); }) .delete => { Tasks.destroy({where: { id: req.params.id, user_id: req.user.id }}) .then(result => res.sendStatus .catch(error => { res.status.json; }); });};

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

module.exports = app => { "use strict"; const Users = app.db.models.Users; app.route .all(app.auth.authenticate => { Users.findById(req.user.id, { attributes: ["id", "name", "email"] }) .then(result => res.json .catch(error => { res.status.json; }); }) .delete => { console.log(`delete..........${req.user.id}`); Users.destroy({where: {id: req.user.id}}) .then(result => { console.log; return res.sendStatus .catch(error => { console.log; res.status.json; }); }); app.post => { Users.create .then(result => res.json .catch(error => { res.status.json; }); });};

这么些路由写起来比较轻松,上面的代码中,基本观念就是基于模型操作CRUD,包罗捕获分外。然而额外的功能是做了authenticate,也正是授权操作。

这一块相仿没什么好说的,基本上都以原则性套路。

授权

在互连网蒙受中,不能够老是传递顾客名和密码。那时候就必要一些授权机制,该品种中运用的是JWT授权,有意思味的同学能够去探听下这几个授权,它也是依据一定的规行矩步生成token。

为此对于授权来讲,最大旨的有的便是哪些生成token。

import jwt from "jwt-simple"module.exports = app => { "use strict"; const cfg = app.libs.config; const Users = app.db.models.Users; app.post => { const email = req.body.email; const password = req.body.password; if  { 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 .catch(error => res.sendStatus; } else { res.sendStatus;};

上边代码中,在获取邮箱和密码后,再选用 jwt-simple 模块生成三个token。

JWT在此也十分少说了,它由三有个别组成,那几个在它的官方网址中解释的很详细。

本人认为老外写东西三个最大的亮点就是文书档案很详细。要想弄领会全数组件如何选拔,最佳的秘技正是去他们的官方网站看文书档案,当然那供给英语水准还足以。

生成token 验证token

假定在那以前端传递七个token过来,大家怎么剖析那些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; opts.secretOrKey = cfg.jwtSecret; const strategy = new Strategy => { Users.findById .then { return done(null, { id: user.id, email: user.email }); } return done .catch(error => done; passport.use; return { initialize: () => { return passport.initialize(); }, authenticate: () => { return passport.authenticate("jwt", cfg.jwtSession); } };};

那就用到了 passport 和 passport-jwt 那八个模块。 passport
援助很两种授权。不管是iOS还是Node中,验证都亟需钦赐一个计谋,那个核心是最灵敏的一层。

授权须要在品种中提前行行构造,也正是早先化, app.useState of Qatar; 。

假定大家想对有个别接口进行授权验证,那么只须求像下面这么用就能够了:

.all(app.auth.authenticate => { console.log(`req.body: ${req.body}`); Tasks.findAll({where: {user_id: req.user.id} }) .then(result => res.json .catch(error => { res.status.json; });})

配置

Node.js中一个很有用的探究正是middleware,我们能够使用那么些手法做过多神乎其神的事情:

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; app.set; console.log(`err ${JSON.stringify; app.use; app.use); app.use; app.use; app.use(morgan("common", { stream: { write:  => { logger.info; app.use(cors({ origin: ["http://localhost:3001"], methods: ["GET", "POST", "PUT", "DELETE"], allowedHeaders: ["Content-Type", "Authorization"] })); app.use => { // console.log(`header: ${JSON.stringify; if (req.body && req.body.id) { delete req.body.id; } next; app.use(express.static;};

上边的代码中隐含了多数新的模块,app.set表示举办安装,app.use表示使用middleware。

测试

写测量试验代码是自身平日十分轻易忽略的地点,说真话,这么重大的局地不该被忽略。

import jwt from "jwt-simple"describe => { "use strict"; const Users = app.db.models.Users; const jwtSecret = app.libs.config.jwtSecret; let token; beforeEach(done => { Users .destroy => { return Users.create({ name: "Bond", email: "Bond@mc.com", password: "123456" }); }) .then(user => { token = jwt.encode({id: user.id}, jwtSecret); done; describe => { describe => { it("returns an authenticated user", done => { request.get .set("Authorization", `JWT ${token}`) .expect => { expect.to.eql; expect.to.eql; done; describe => { describe => { it("deletes an authenticated user", done => { request.delete .set("Authorization", `JWT ${token}`) .expect => { console.log; done; describe => { describe => { it("creates a new user", done => { request.post .send({ name: "machao", email: "machao@mc.com", password: "123456" }) .expect => { expect.to.eql; expect.to.eql; done;});

测验主要依附上边的那多少个模块:

import supertest from "supertest"import chai from "chai"import app from "../index"global.app = app;global.request = supertest;global.expect = chai.expect;

当中 supertest 用来发央求的, chai 用来决断是不是中标。

行使 mocha 测量试验框架来拓宽测验:

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

转移接口文书档案

接口文书档案也是很关键的八个环节,该项目接收的是 ApiDoc.js
。那一个没什么好说的,直接上代码:

/** * @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 */

粗粗就贴近与上方的楷模,不只能够做注明用,又足以自动生成文书档案,一矢双穿,小编就不上海体育场地了。

预备宣布

到了那边,就只剩下发表前的一些操作了,

一对时候,处于安全方面包车型地铁杜撰,我们的API只怕只同意一些域名的拜访,因而在那处引进七个强盛的模块
cors
,介绍它的小说,网络有无数,我们能够直接寻觅,在该项目中是这么使用的:

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

其一设置在本文的最后的现身说法网址中,会起功效。

打字与印刷央求日志相通是一个很要紧的职务,因而引入了 winston
模块。下面是对他的配置:

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

打印的结果大概是如此的:

{"level":"info","message":"::1 - - [26/Sep/2017:11:16:23 +0000] "GET /tasks HTTP/1.1" 200 616n","timestamp":"2017-09-26T11:16:23.089Z"}{"level":"info","message":"::1 - - [26/Sep/2017:11:16:43 +0000] "OPTIONS /user HTTP/1.1" 204 0n","timestamp":"2017-09-26T11:16:43.583Z"}{"level":"info","message":"Tue Sep 26 2017 19:16:43 GMT+0800  Executing : 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  Executing : 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 73n","timestamp":"2017-09-26T11:16:43.599Z"}{"level":"info","message":"::1 - - [26/Sep/2017:11:16:49 +0000] "OPTIONS /user HTTP/1.1" 204 0n","timestamp":"2017-09-26T11:16:49.658Z"}{"level":"info","message":"Tue Sep 26 2017 19:16:49 GMT+0800  Executing : 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  Executing : 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 0n","timestamp":"2017-09-26T11:17:04.905Z"}{"level":"info","message":"Tue Sep 26 2017 19:17:04 GMT+0800  Executing : 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 12n","timestamp":"2017-09-26T11:17:04.916Z"}

性格上,我们接纳Node.js自带的cluster来利用机械的多核,代码如下:

import cluster from "cluster"import os from "os"const CPUS = os.cpus();if  { // Fork CPUS.forEach; // 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;}

在数额传输上,我们应用 compression
模块对数码举行了gzip压缩,那么些应用起来比较轻松:

app.use;

末尾,让我们支撑https访谈,https的显要就在于注脚,使用授权部门的证件是最棒的,但该类型中,我们选用

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 => { https.createServer .listen => { console.log(`NTask API - Port ${app.get; }};

当然,处于安全着想,制止攻击,我们应用了 helmet 模块:

app.use;

后边二个程序

为了更加好的现身说法该API,小编把前段的代码也上传到了那几个库房

API的代码连接

总结

自己以为那本书写的不胜好,小编获得良多。它固然并不复杂,不过该有的皆有了,由此作者得以自由的往外延伸。同期也学到了小编精晓代码的力量。

作者认为自家还达不到把所学所会的事物讲掌握。有怎么着错误的地点,还请予以指正。

上述正是本文的全部内容,希望对我们的学习抱有利于,也冀望我们多多点拨脚本之家。

发表评论

电子邮件地址不会被公开。 必填项已用*标注