基于Node的PetShop,RESTful API以及认证

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介:

前篇 - 基本认证,用户名密码
后篇 - OAuth2 认证

由于宠物店的业务发展需要,我们需要一种更加便捷的方式来管理日益增多的宠物和客户。最好的方法就是开发一个APP,我可以用这个APP来添加、更新和删除宠物。同时,业务要给宠物店的会员用户有限查看某些宠物。

我们在开发中会用到NodeJs以及基于NodeJs的开发框架,如:Express,Mongoose(用来管理MongoDB的数据),Passport(认证)等工具。项目代码在这里

开始

我们这个项目的结构大概是这样的:

petshot/            //服务端和客户端(android)
    server/         //服务端
        models/     //实体类
            pet.js
            user.js
        node_modules/    //npm安装的包,无需手动修改
        package.json     //project定义和依赖声明 
        server.js        //服务端的一切从这里开始

注意node_modules这个目录你不需要创建,在执行npm安装命令之后这个目录会自动生成,并且npm命令会自动管理这个目录。

定义项目和依赖的包

这里假设你已经跳转到petshop/server/目录下了。之后在Terminal里执行命令:

npm init

按照提示,依次在Terminal里输入相应的信息。最后npm命令生成package.json文件。文件是这样的:

{
  "name": "petshop-server",
  "version": "0.1.0",
  "description": "petshop nodejs server",
  "main": "server.js",
  "dependencies": {
  }
}

内容很多,这里是一部分。

安装所需要的包

为什么NodeJs能让那么多的开发者青睐有加,npm命令绝对也是一个数得上的原因了。下面就体会一下,npm命令。

首先,使用npm命令安装Express:

npm install --save express

npm命令会选择合适的Express版本下载安装在本地。其他的包也是这么安装的。非常简单。

运行Server

有了Express,Server就已经可以运行起来了。如果你还没有创建server.js,请创建。在server.js中添加如下的代码:

// 引入我们需要的包express
var express = require('express');
// 创建一个express的server
var app = express();
// server运行的端口号
var port = process.env.PORT || '3090';
// 使用express的路由器
var router = express.Router();
// 访问http://localhost:3090/api的时候,
// 返回一个json
router.get('/', function (req, res) {
    res.json({'message': '欢迎来到宠物商店'});
});

// 给路由设定根路径为/api
app.use('/api', router);
// 运行server,并监听指定的端口
app.listen(port, function () {
    console.log('server is running at http://localhost:3090');
});

通过require得到的express,就可以初始化出一个express的server。之后指定路由,并指定路由的相对根路径。在app上调用listen方法,并指定端口。这样express的server就运行起来了。

在Terminal中输入命令:

node server.js

测试一下我们的第一个server是否可以运行。在浏览器中输入地址:*http://localhost:3090/api*就可以看到运行结果了

几个工具

为了开发的更加方便,仅仅以上介绍的内容是不够的。比如,修改代码之后,使用命令node server.js来重启server。设置断点,单步调试等。我们来看看这几个工具:

1. Visual Code

微软改邪归正依赖的第一个靠谱的工具。正好这个工具非常好的支持了NodeJs的开发。Visual Code还默认继承了Git代码管理工具。这让我非常愿意多安利几句。

并且使用visual code可以非常方便的调试。比如,设置断点、单步调试,step in、step out等都可以。还可以鼠标悬浮查看变量值等。

2. Postman

postman有chrome浏览器版本的应用,这样无论你在什么平台上开发只要安装了Chrome浏览器就可以装一个postman。这个工具是用来检查RESTful API的。直接使用浏览器得出来的Json字符串有的时候没有格式化,或者格式化不充分,非常难阅读。

并且直接使用浏览器没法模拟Http post请求。而Postman很好的解决了以上问题。所以,开发必备神器之一postman,你值得拥有。

MongoDB

业界著名的非关系数据库MongoDB。我们在petshot的server端使用该库来存储数据。请按照官网说明下载安装(其实就是把编译好的二进制文件放到一个指定目录)。

数据库安装好之后,就需要连接数据库的框架了。这就需要用到mongoose。Mongoose是处理MongoDB的一个ORM框架。

npm install --save mongoose

安装好之后在代码中引入(require)。

var express = require('express');
var mongoose = require('mongoose');

连接到数据库:

// 连接数据库
mongoose.connect('mongodb://localhost:27017/petshot');

创建model

MVC的开发模式这里就不再科普了。凡是MVC模式下开发,就一定会有Model,MVC的M。一般每一个model都和数据库中的一个“表”对应,在mongodb里“表”正式名称为“Collection”。我们这里只使用英文,专有名词没必要翻译。而“表”里的每一条“记录”又叫做“Document”。

我们首先在petshop/server/models/目录下新建一个文件pet.js。之后添加如下代码:

// 1. 引入mongoose
var mongoose = require('mongoose');

var Schema = mongoose.Schema;
// 2. 定义了Pet的Schema
var petSchema = new Schema({
    name: {type: String, required: true},
    type: {type: String, required: true},
    quantity: Number
});
// 3. 定义并export了一个Model
module.exports = mongoose.Model('pet', petSchema);

相关解释:

  1. 引入mongoose库。
  2. 定义了一个mongoose的Schema,这个Schema和一个mongodb的collection的定义相对应。这个schema有两个字符串和一个数字类型的属性。
  3. 把mongoose的Model export出去给server端的其他代码使用。哪里使用哪里就require这个model。

准备接收HTTP数据

首先,需要一个包(package)来解析http发送过来的数据。那么安装之:

npm install --save body-parser

在代码中引入:

var express = require('express');
var mongoose = require('mongoose');
// 稍后处理数据使用
var Pet = require('./models/pet');
// 解析http数据
var bodyParser = require('body-parser');

下面还需要设置body-parser:

var app = express();

app.use(bodyParser.urlencoded({
    extended: true
}));

添加些许宠物

我们的server终于迎来可以使用的一个功能了:给宠物商店添加宠物。

router.get('/', function (req, res) {
    res.json({'message': '欢迎来到宠物商店'});
});

var petRouter = router.route('/pets');

petRouter.post(function (req, res) {
    var pet = new Pet();
    pet.name = req.body.name;
    pet.type = req.body.type;
    pet.quantity = req.body.quantity;

    pet.save(function (err) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: pet});
    });
});

我们增加了一个/pets的相对路径来处理POST请求,并在请求中获取信息,把用这个信息来给初始化的pet的model赋值,并调用这个model的save方法保存数据到数据库。

打开postman,各个设置如图:

这样就可以往数据库添加pet数据了。

重构代码

现在的代码虽然已经可以添加宠物宝宝了。但是,后面如果我们还要添加其他功能,比如:查找,更新和删除等的时候,server.js文件势必会不断增加。这样给以后代码的维护带来困扰。所以我们要重构代码。

petshop/server/目录下添加controllers目录。根据MVC的模式开发,把处理业务方面的代码都存放在某个controller里。新建pet.js文件。这个文件就作为pet的controller。并将宠物的增删改查都放在这个文件中处理:

var Pet = require('../models/pet');

var postPets = function(req, res) {
    var pet = new Pet();
    pet.name = req.body.name;
    pet.type = req.body.type;
    pet.quantity = req.body.quantity;

    pet.save(function (err) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: pet});
    });
};

var getPets = function(req, res) {
    Pet.find(function (err, pets) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: pets});
    });
};

var getPet = function(req, res) {
    Pet.findById(req.params.pet_id, function (err, pet) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }
        res.json({message: 'done', data: pet});
    });
};

var updatePet = function(req, res) {
    Pet.findById(req.params.pet_id, function(err, pet) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        pet.quantity = req.params.quantity;

        pet.save(function (err) {
            if (err) {
                res.json({message: 'error', data: err});
                return;
            }

            res.json({message: 'done', data: pet});
        });
    });
};

var deletePet = function(req, res) {
    Pet.findByIdAndRemove(req.params.pet_id, function(err) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: {}});
    });
}

module.exports = {
    postPets: postPets,
    getPets: getPets,
    getPet: getPet,
    updatePet: updatePet,
    deletePet: deletePet
};

原来的server.js也需要重构:

var Pet = require('../models/pet');

var postPets = function(req, res) {
    var pet = new Pet();
    pet.name = req.body.name;
    pet.type = req.body.type;
    pet.quantity = req.body.quantity;

    pet.save(function (err) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: pet});
    });
};

var getPets = function(req, res) {
    Pet.find(function (err, pets) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: pets});
    });
};

var getPet = function(req, res) {
    Pet.findById(req.params.pet_id, function (err, pet) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }
        res.json({message: 'done', data: pet});
    });
};

var updatePet = function(req, res) {
    Pet.findById(req.params.pet_id, function(err, pet) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        pet.quantity = req.params.quantity;

        pet.save(function (err) {
            if (err) {
                res.json({message: 'error', data: err});
                return;
            }

            res.json({message: 'done', data: pet});
        });
    });
};

var deletePet = function(req, res) {
    Pet.findByIdAndRemove(req.params.pet_id, function(err) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: {}});
    });
}

module.exports = {
    postPets: postPets,
    getPets: getPets,
    getPet: getPet,
    updatePet: updatePet,
    deletePet: deletePet
};

认证

我们当让不行让谁都可以添加宠物宝宝了。查看是可以的,添加需要控制。

passport

Passport就是给基于Express开发的web应用的,专注于认证中间件。也有和body-parser相类似的使用方法。passport的功能非常丰富,不过我们先使用最简单的一种认证策略

安装:

npm install --save passport-http

认证以前首先要有用户数据。

同时还有一个包需要安装:

npm install --save bcrypt-nodejs

这个包是用来给密码hash用的。

用户model

所有关于用户的数据都放在MongoDB的user colleciton里,并有user model与之对应。在models目录下新建user.js文件。

var mongoose = require('mongoose'),
    bcrypt = require('bcrypt-nodejs');

var Schema = mongoose.Schema;

var userSchema = new Schema({
    username: {type: String, unique: true, required: true},
    password: {type: String, required: true}
});

// * called before 'save' method.
userSchema.pre('save', function (next) {
    var self = this;

    if (!self.isModified('password')) {
        return next();
    }

    bcrypt.genSalt(5, function (err, salt) {
        if (err) {
            return next(err);
        }

        bcrypt.hash(self.password, salt, null, function (err, hash) {
            if (err) {
                return next(err);
            }

            self.password = hash;
            next();
        });
        
    });
});

module.exports = mongoose.model('User', userSchema);

使用userSchema.pre('save', function(next){})给model添加了一个在save方法调用之前先执行的方法。在这个方法里首先检查用户的密码是否有修改,如果有则使用包bcrypt-nodejs来hash用户的密码。

User Controller

有了model,就需要对应的controller来处理。在controllers目录下新建一个user.js文件作为user controller。注意:实际开发的时候你肯定是不会把全部用户的信息都发到客户端的,里面包含了hash的用户密码

var User = require('../models/user');

var postUsers = function (req, res) {
    var user = new User({
        username: req.body.username,
        password: req.body.password
    });

    user.save(function (err) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: user});
    });
};

var getUsers = function (req, res) {
    User.find(function (err, users) {
        if (err) {
            res.json({message: 'error', data: err});
            return;
        }

        res.json({message: 'done', data: users});
    });
};

module.exports = {
    postUsers: postUsers,
    getUsers: getUsers
};

定义user controller的路由:

...

var petController = require('./controllers/pet')
    userController = require('./controllers/user');

...

// path: /users, for users
router.route('/users')
    .post(userController.postUsers)
    .get(userController.getUsers);

你已经可以在这个路径:*http://localhost:3090/api/users*下POST添加用户,GET获取全部用户

认证

在开始以前首先确保你已经安装了认证需要的包:

npm install --save passport
npm install --save passport-http

之后给在user model里添加一个方法验证password:

userSchema.methods.verifyPassword = function (password, callback) {
    bcrypt.compare(password, this.password, function (err, match) {
        if (err) {
            return callback(err);
        }

        callback(null, match);
    });
};

接下来,在controllers目录下添加auth.js文件。

var passport =          require('passport'),
    BasicStrategy =     require('passport-http').BasicStrategy,
    User =              require('../models/user');

passport.use(new BasicStrategy(
    function(username, password, done) {
        User.findOne({username: username}, function(err, user) {
            if (err) {
                return done(err);
            }

            // 用户不存在
            if (!user) {
                return done(null, false);
            }

            // 检查用户的密码
            user.verifyPassword(passowrd, function(err, match) {
                // 密码不匹配
                if (!match) {
                    return done(null, false);
                }

                // 成功
                return done(null, user);
            });
        });
    }
));

module.exports.isAuthenticated = passport.authenticate('basic', {session: false});

我们使用包passport-httpBasicStrategy来处理http的用户认证。首先,我们通过用户名查找用户。如果用户存在,接着验证用户的密码是否与数据库的数据一致。如果以上两步通过验证则用户认证成功,否则不成功。

最后一句就是告知passport使用BasicStrategy来认证用户。session为false,是告诉passport不存储用户的session。用户每一次的http请求都需要提供用户名和密码。

相应的更新server.js:

// 引入我们需要的包express
var express             = require('express'),
    mongoose            = require('mongoose'),
    bodyParser          = require('body-parser'),
    passport            = require('passport'),

    petController       = require('./controllers/pet'),
    userController      = require('./controllers/user'),
    authController      = require('./controllers/auth');

// 创建一个express的server
var app = express();

app.use(bodyParser.urlencoded({
    extended: true
}));

// 连接数据库
mongoose.connect('mongodb://localhost:27017/petshot');

...

router.route('/pets')
    .post(authController.isAuthenticated, petController.postPets)
    .get(authController.isAuthenticated, petController.getPets);

router.route('/pets/:pet_id')
    .get(authController.isAuthenticated, petController.getPet)
    .put(authController.isAuthenticated, petController.updatePet)
    .delete(authController.isAuthenticated, petController.deletePet);

// path: /users, for users
router.route('/users')
    .post(userController.postUsers)
    .get(authController.isAuthenticated, userController.getUsers);

...

如果还没有数据的话,首先使用POST方法添加几个用户。之后GET用户测试一下。如果用户名、密码都对的话就会获得数据了。

使用passport包认证还有一个好处,你可以直接从req获取user数据。如:req.user._id获得用户的_id。有些数据需要记录更新数据的用户,这样就非常方便了。

最后

下文使用更加安全的oauth2认证。

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!
























本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/5634907.html ,如需转载请自行联系原作者
相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
2天前
|
前端开发 JavaScript NoSQL
使用 Node.js、Express 和 React 构建强大的 API
本文详细介绍如何使用 Node.js、Express 和 React 构建强大且动态的 API。从开发环境搭建到集成 React 前端,再到利用 APIPost 高效测试 API,适合各水平开发者。内容涵盖 Node.js 运行时、Express 框架与 React 库的基础知识及协同工作方式,还涉及数据库连接和前后端数据交互。通过实际代码示例,助你快速上手并优化应用性能。
|
16天前
|
安全 Java API
利用 AWS Signature:REST API 认证的安全指南
本文探讨了 AWS Signature 在保护 REST API 访问中的重要性,详解其工作原理,并提供 Java 和 Go 的实现示例。AWS Signature 通过加密技术确保请求安全,具备增强安全性、保障请求完整性、防范重放攻击及与 AWS 兼容等优势。文章还介绍了测试工具如 APIPost、Postman 和 cURL 的使用方法,帮助开发者验证实现效果。总结指出,采用 AWS Signature 可有效提升 API 安全性,增强用户信任并保护敏感数据。
|
15天前
|
安全 Java API
为什么要为 REST API 添加认证
在现代Web服务中,REST API的通信安全至关重要。认证机制可验证用户身份、控制资源访问、保护数据并监控使用情况。Basic Auth(基本认证)是一种简单有效的方法,通过HTTP头部发送Base64编码的用户名和密码实现安全保护,但建议搭配HTTPS使用以避免漏洞。本文展示了如何用Java和Go语言实现Basic Auth,并介绍了APIPost、Curl和Postman等工具进行测试。开发者可通过这些方法确保API功能强大且安全可靠。
|
1月前
|
Kubernetes API 网络安全
当node节点kubectl 命令无法连接到 Kubernetes API 服务器
当Node节点上的 `kubectl`无法连接到Kubernetes API服务器时,可以通过以上步骤逐步排查和解决问题。首先确保网络连接正常,验证 `kubeconfig`文件配置正确,检查API服务器和Node节点的状态,最后排除防火墙或网络策略的干扰,并通过重启服务恢复正常连接。通过这些措施,可以有效解决与Kubernetes API服务器通信的常见问题,从而保障集群的正常运行。
58 17
|
1月前
|
XML JSON API
Understanding RESTful API and Web Services: Key Differences and Use Cases
在现代软件开发中,RESTful API和Web服务均用于实现系统间通信,但各有特点。RESTful API遵循REST原则,主要使用HTTP/HTTPS协议,数据格式多为JSON或XML,适用于无状态通信;而Web服务包括SOAP和REST,常用于基于网络的API,采用标准化方法如WSDL或OpenAPI。理解两者区别有助于选择适合应用需求的解决方案,构建高效、可扩展的应用程序。
|
1月前
|
安全 API Go
如何实现和调试REST API中的摘要认证(Digest Authentication)
本文介绍如何实现和调试REST API中的摘要认证(Digest Authentication),涵盖其原理、优势及Java和Go语言的实现示例。摘要认证通过哈希算法处理密码,避免明文传输风险,并使用nonce防止重放攻击,确保数据完整性。文中还提供了Postman、cURL和Insomnia等工具的测试方法,帮助开发者轻松验证API的安全性。总结指出,摘要认证相比基本认证更安全,适合需要高安全性的API应用。
|
1月前
|
机器学习/深度学习 设计模式 API
Python 高级编程与实战:构建 RESTful API
本文深入探讨了使用 Python 构建 RESTful API 的方法,涵盖 Flask、Django REST Framework 和 FastAPI 三个主流框架。通过实战项目示例,详细讲解了如何处理 GET、POST 请求,并返回相应数据。学习这些技术将帮助你掌握构建高效、可靠的 Web API。
|
17天前
|
存储 Cloud Native 安全
API 安全之认证鉴权
API 作为企业的重要数字资源,在给企业带来巨大便利的同时也带来了新的安全问题,一旦被攻击可能导致数据泄漏重大安全问题,从而给企业的业务发展带来极大的安全风险。
|
4月前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
95 12
|
JSON API PHP
CI中如何保护RESTful API
步骤5 保护RESTful API   为了保护RESTful API,可以在application/config/rest.php中设置安全保护级别,如下所示: $config['rest_auth'] = 'basic';    其中保护级别有如下设置:   None:任何人都...
984 0

热门文章

最新文章