express+monogo实现ToDo Restful Api

Begin

REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。
表述性状态转移是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful。需要注意的是,REST是设计风格而不是标准。REST通常基于使用HTTP,URI,和XML(标准通用标记语言下的一个子集)以及HTML(标准通用标记语言下的一个应用)这些现有的广泛流行的协议和标准。REST 通常使用 JSON 数据格式。

本文主要阐述使用node.js的express 框架实现一个ToDo List(任务列表)的Restful 接口。基本的功能包括用户的登录注册、添加任务、完成任务、查看任务等。 最终所有的Api可以在Postman中进行测试。

完整的项目地址: https://github.com/superzhan/ToDo

开发环境

mac, webstom, monogoDB , node.js 6.10.1 ,express 4.01

安装、创建express项目还挺简单的,官方还提供一个项目生成器。

npm install express-generator -g

执行命令就可以全局安装一个express的项目生成器。新建项目的时候执行命令

express todo -e

新建一个名称为 todo 的express 项目,使用ejs 模板。

执行 npm install ,安装相应的模块。

执行 npm start , 运行工程。可以通过 http://localhost:3000 访问。

基本的Restful 接口示例

新建的工程中会有一些基本的代码。express 中实现Restful 接口是一件相当简单的事情。

添加一个简单的api接口 testAPI. 在 routes/index.js 添加代码

router.get('/testApi',function(req, res, next) {

    res.json({result:'hi ,this is api result'});
});

重新启动工程后,可以在浏览器通过http://localhost:3000/testApi 访问这个接口。这个接口使用了GET方法。

在添加一个Post 方法的接口。

router.post('/testApi',function(req, res, next) {

    res.json({result:'hi ,this is api result'});
});

这个接口可以再Postman中,通过POST 方法来访问。

Screen Shot 2017-08-05 at 23.24.20.png

简单添加和获取任务

添加一个addItem 的接口,把客户端发送过来的数据处理之后,插入到数据库中。

router.post('/addItem',function(req,res,next){

  var item = {};
  item.note =req.body.note;
  item.completed=false;
  item.updated_at =timeTool.getCurDate();   
  todoSchema.create(item,function(err,post){
    if(err)
    {
        next(err);
    }else
    {
       res.redirect('/');
    }
  });

});

添加一个finishItem 接口,返回已经完成的任务。接口的实现也是简单的数据库查询。

router.post('/finishItem',function(req,res,next){

  var item = {};
  item._id =req.body._id;
  item.completed=true;
  item.updated_at =timeTool.getCurDate();   
  todoSchema.findByIdAndUpdate(item._id,item,function(err,post){
    if(err)
    {
        next(err);
    }else
    {
       res.redirect('/');
    }
  });

});

数据库mongoose的使用

这个工程使用monogoDb 作为数据库,相对地使用monogoose作为数据库连接模块。monogoose的使用也相当简单。monogoose Api 文档

数据库连接

新建一个数据库连接模块mongoDB.js ,管理数据库连接。数据库的配置文件是根目录下的 mongoConfig.json, 在这个文件中配置相应的数据库地址和端口号。

具体的可以参考github上的项目https://github.com/superzhan/ToDo

var config = require('../../mongoConfig.json');

var connectStr = '';
if(config.isAuth)
{
 connectStr='mongodb://'+config.username+':'+config.password+'@'+config.host$
}else
{
 connectStr='mongodb://'+config.host+':'+config.port+'/'+config.database;
}
console.log(connectStr);

var mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect(connectStr)
   .then(function () {console.log('connection succesful')})
   .catch(function (err) {console.error(err)});

module.exports = mongoose;

数据模型

这个工程用到的数据模型也比较简单,只有任务数据模型和用户数据模型。通过monogoose 预先定义好这两个数据模型。

任务数据模型

var mongoose = require('mongoose');
var timeTool = require('./timeTool');

var ToDoSchema = new mongoose.Schema({
  completed: Boolean,
  note: String,
  userId : mongoose.Schema.Types.ObjectId, //表明任务所属的用户
  updated_at: { type: Date, default: timeTool.getCurDate()},
});
module.exports = mongoose.model('Todo', ToDoSchema);

用户数据模型

var mongoose = require('mongoose');
var timeTool = require('./timeTool');

var UserSchema = new mongoose.Schema({

    name : String,
    password :String,
    updated_at: { type: Date, default: timeTool.getCurDate()}
});
module.exports = mongoose.model('User', UserSchema);

用户注册和登录

注册

用户注册的接口实现需要验证用户的密码是否相同,然后对密码进行哈希加密,把密文存放在数据中。

而且还需要查找是否有相同名称的用户,若有相同名称的用户,返回注册失败的结果。

router.post('/register',function (req, res, next) {

    var name = req.body.name;
    var password = req.body.password;
    var comfirmPassword = req.body.comfirmPassword;

    if( password != comfirmPassword)
    {
        res.json({code:500,msg:'password is not same'});
        return;
    }

    UserSchema.findOne({'name':name} , function (err, data) {
        if(err)
        {
            res.json({code:500,msg:'check error'});
            return;
        }

        if(data != null)
        {
            console.log(data);
            res.json({code:500,msg:'userName is exist'});
            return;
        }

        var hash = crypto.createHash('sha1');
        hash.update(password);
        password = hash.digest('hex');

        var userInfo={};
        userInfo.name = name;
        userInfo.password= password;

        UserSchema.create( userInfo,function (err,resData) {
            if(err)
            {
                res.json({code:500,msg:'data base error'});
            }else
            {
                res.json({code:200,msg:'success'});
            }
        });

    });

});

登录

用户登录时需要把请求的明文密码用哈希算法加密后,再进行数据库查询。

router.post('/login',function (req, res, next) {

    var name = req.body.name;
    var password = req.body.password;

    UserSchema.findOne({'name':name} , function (err, data) {
        if(err)
        {
            res.json({code:500,msg:'data base error'});
            return;
        }

        if(data == null)
        {
            res.json({code:500,msg:'user not exist'});
            return;
        }

        var hash = crypto.createHash('sha1');
        hash.update(password);
        password = hash.digest('hex');
        UserSchema.findOne({'name':name ,'password':password},function (err, data) {
            if(err)
            {
                res.json({code:500,msg:'data base error'});
                return;
            }
            if(data==null)
            {
                res.json({code:500,msg:'password not right'});
                return;
            }


            UserSchema.findOneAndUpdate({name:name ,password:password},
                {updated_at:timeTool.getCurDate()},
                function (err, data) {
                    if(err)
                    {
                        res.json({code:500,msg:'data base error'});
                        return;
                    }
                    res.json({code:200,msg:'success',_id:data._id});
                }
            );

        });

    });

});

API验证 Basic Auth

这些API有一个安全问题,API没有安全验证,任何一台设备都可以通过url进行访问。这个时候就需要对API的访问者进行身份认证。

这里使用最基本的 http Basic Auth 认证,所有的访问请求都需要携带用户的帐号密码信息。

Screen Shot 2017-08-06 at 21.01.34.png

这里使用passport 模块,http://passportjs.org/ 。passport 是一个node.js的Http验证模块,可以快速开发http验证功能。 这里使用简单的basic auth 验证。

/*导入包*/
var passport = require('passport');
var Strategy = require('passport-http').BasicStrategy;

/*设置验证函数 验证帐号密码*/
passport.use(new Strategy(
    function(username, password, cb) {

        var hash = crypto.createHash('sha1');
        hash.update(password);
        var cryPassword = hash.digest('hex');

        UserSchema.findOne({name:username,password:cryPassword}, function(err, user) {
            if (err) { return cb(err); }
            if (!user) { return cb(null, false); }
            return cb(null, user);
        });
    }));

var authenti=passport.authenticate('basic', { session: false });

最后在路由上添加 authenti 进行API验证。

router.post('/getItem', authenti,function(req, res, next) {});

完整的任务接口

这个工程总共有8个任务接口,包括查看任务、添加任务、更新任务和删除任务。具体代码可以参考github 代码仓库。 https://github.com/superzhan/ToDo

router.post('/getItem', authenti,function(req, res, next) {

    TodoSchema.findOne({"_id":req.body._id},function(err, data){
        if(err){
            next(err);
        }else
        {
            res.json(data);
        }
    });

});

/*返回未完成的Item*/
router.post('/getUnDoItem', authenti,function(req, res, next) {

    if(req.body.userId==null)
    {
        res.json({code:500,msg:"request error"});
        return;
    }

    var userId =mongoose.Types.ObjectId(req.body.userId);
    TodoSchema.find({"completed":false ,"userId":userId},function(err, data){
    if(err){
      next(err);
    }else
    {
      res.json(data);
    }
  });

});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,563评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,713评论 6 342
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,266评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • 很多时候,我们想的,并不是时间去哪了,而是心去哪了。 一年365天,一天24小时,一小时60分钟,一分钟60秒,这...
    唐思尧阅读 387评论 0 0