基于Node的PetShop,RESTful API以及認(rèn)證

前篇 - 用戶(hù)名密碼基本認(rèn)證
后篇 - OAuth2認(rèn)證

由于寵物店的業(yè)務(wù)發(fā)展需要岩遗,我們需要一種更加便捷的方式來(lái)管理日益增多的寵物和客戶(hù)扇商。最好的方法就是開(kāi)發(fā)一個(gè)APP,我可以用這個(gè)APP來(lái)添加宿礁、更新和刪除寵物钳吟。同時(shí),業(yè)務(wù)要給寵物店的會(huì)員用戶(hù)有限查看某些寵物窘拯。

我們?cè)陂_(kāi)發(fā)中會(huì)用到NodeJs以及基于NodeJs的開(kāi)發(fā)框架红且,如:Express,Mongoose(用來(lái)管理MongoDB的數(shù)據(jù))涤姊,Passport(認(rèn)證)等工具暇番。

代碼在這里

開(kāi)始

我們這個(gè)項(xiàng)目的結(jié)構(gòu)大概是這樣的:

petshot/            //服務(wù)端和客戶(hù)端(android)
    server/         //服務(wù)端
        models/     //實(shí)體類(lèi)
            pet.js
            user.js
        node_modules/    //npm安裝的包思喊,無(wú)需手動(dòng)修改
        package.json     //project定義和依賴(lài)聲明 
        server.js        //服務(wù)端的一切從這里開(kāi)始

注意node_modules這個(gè)目錄你不需要?jiǎng)?chuàng)建壁酬,在執(zhí)行npm安裝命令之后這個(gè)目錄會(huì)自動(dòng)生成,并且npm命令會(huì)自動(dòng)管理這個(gè)目錄恨课。

定義項(xiàng)目和依賴(lài)的包

這里假設(shè)你已經(jīng)跳轉(zhuǎn)到petshop/server/目錄下了舆乔。之后在Terminal里執(zhí)行命令:

npm init

按照提示,依次在Terminal里輸入相應(yīng)的信息剂公。最后npm命令生成package.json文件希俩。文件是這樣的:

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

內(nèi)容很多,這里是一部分纲辽。

安裝所需要的包

為什么NodeJs能讓那么多的開(kāi)發(fā)者青睞有加颜武,npm命令絕對(duì)也是一個(gè)數(shù)得上的原因了璃搜。下面就體會(huì)一下,npm命令鳞上。

首先这吻,使用npm命令安裝Express:

npm install --save express

npm命令會(huì)選擇合適的Express版本下載安裝在本地。其他的包也是這么安裝的篙议。非常簡(jiǎn)單唾糯。

運(yùn)行Server

有了Express,Server就已經(jīng)可以運(yùn)行起來(lái)了鬼贱。如果你還沒(méi)有創(chuàng)建server.js移怯,請(qǐng)創(chuàng)建。在server.js中添加如下的代碼:

// 引入我們需要的包express
var express = require('express');
// 創(chuàng)建一個(gè)express的server
var app = express();
// server運(yùn)行的端口號(hào)
var port = process.env.PORT || '3090';
// 使用express的路由器
var router = express.Router();
// 訪(fǎng)問(wèn)http://localhost:3090/api的時(shí)候吩愧,
// 返回一個(gè)json
router.get('/', function (req, res) {
    res.json({'message': '歡迎來(lái)到寵物商店'});
});

// 給路由設(shè)定根路徑為/api
app.use('/api', router);
// 運(yùn)行server,并監(jiān)聽(tīng)指定的端口
app.listen(port, function () {
    console.log('server is running at http://localhost:3090');
});

通過(guò)require得到的express增显,就可以初始化出一個(gè)express的server雁佳。之后指定路由,并指定路由的相對(duì)根路徑同云。在app上調(diào)用listen方法糖权,并指定端口。這樣express的server就運(yùn)行起來(lái)了炸站。

在Terminal中輸入命令:

node server.js

測(cè)試一下我們的第一個(gè)server是否可以運(yùn)行星澳。在瀏覽器中輸入地址:http://localhost:3090/api就可以看到運(yùn)行結(jié)果了。

幾個(gè)工具

為了開(kāi)發(fā)的更加方便旱易,僅僅以上介紹的內(nèi)容是不夠的禁偎。比如,修改代碼之后阀坏,使用命令node server.js來(lái)重啟server如暖。設(shè)置斷點(diǎn),單步調(diào)試等忌堂。我們來(lái)看看這幾個(gè)工具:

1. Visual Code

微軟改邪歸正依賴(lài)的第一個(gè)靠譜的工具盒至。正好這個(gè)工具非常好的支持了NodeJs的開(kāi)發(fā)。Visual Code還默認(rèn)繼承了Git代碼管理工具士修。這讓我非常愿意多安利幾句枷遂。

并且使用visual code可以非常方便的調(diào)試。比如棋嘲,設(shè)置斷點(diǎn)酒唉、單步調(diào)試,step in沸移、step out等都可以黔州。還可以鼠標(biāo)懸浮查看變量值等耍鬓。

2. Postman

postman有chrome瀏覽器版本的應(yīng)用,這樣無(wú)論你在什么平臺(tái)上開(kāi)發(fā)只要安裝了Chrome瀏覽器就可以裝一個(gè)postman流妻。這個(gè)工具是用來(lái)檢查RESTful API的牲蜀。直接使用瀏覽器得出來(lái)的Json字符串有的時(shí)候沒(méi)有格式化,或者格式化不充分绅这,非常難閱讀涣达。

并且直接使用瀏覽器沒(méi)法模擬Http post請(qǐng)求。而Postman很好的解決了以上問(wèn)題证薇。所以度苔,開(kāi)發(fā)必備神器之一postman,你值得擁有浑度。

MongoDB

業(yè)界著名的非關(guān)系數(shù)據(jù)庫(kù)MongoDB寇窑。我們?cè)趐etshot的server端使用該庫(kù)來(lái)存儲(chǔ)數(shù)據(jù)。請(qǐng)按照官網(wǎng)說(shuō)明下載安裝(其實(shí)就是把編譯好的二進(jìn)制文件放到一個(gè)指定目錄)箩张。

數(shù)據(jù)庫(kù)安裝好之后甩骏,就需要連接數(shù)據(jù)庫(kù)的框架了。這就需要用到mongoose先慷。Mongoose是處理MongoDB的一個(gè)ORM框架饮笛。

npm install --save mongoose

安裝好之后在代碼中引入(require)。

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

連接到數(shù)據(jù)庫(kù):

// 連接數(shù)據(jù)庫(kù)
mongoose.connect('mongodb://localhost:27017/petshot');

創(chuàng)建model

MVC的開(kāi)發(fā)模式這里就不再科普了论熙。凡是MVC模式下開(kāi)發(fā)福青,就一定會(huì)有Model,MVC的M脓诡。一般每一個(gè)model都和數(shù)據(jù)庫(kù)中的一個(gè)“表”對(duì)應(yīng)无午,在mongodb里“表”正式名稱(chēng)為“Collection”。我們這里只使用英文祝谚,專(zhuān)有名詞沒(méi)必要翻譯指厌。而“表”里的每一條“記錄”又叫做“Document”。

我們首先在petshop/server/models/目錄下新建一個(gè)文件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了一個(gè)Model
module.exports = mongoose.Model('pet', petSchema);

相關(guān)解釋?zhuān)?/p>

  1. 引入mongoose庫(kù)踩验。
  2. 定義了一個(gè)mongoose的Schema,這個(gè)Schema和一個(gè)mongodb的collection的定義相對(duì)應(yīng)商玫。這個(gè)schema有兩個(gè)字符串和一個(gè)數(shù)字類(lèi)型的屬性箕憾。
  3. 把mongoose的Model export出去給server端的其他代碼使用。哪里使用哪里就require這個(gè)model拳昌。

準(zhǔn)備接收HTTP數(shù)據(jù)

首先袭异,需要一個(gè)包(package)來(lái)解析http發(fā)送過(guò)來(lái)的數(shù)據(jù)。那么安裝之:

npm install --save body-parser

在代碼中引入:

var express = require('express');
var mongoose = require('mongoose');
// 稍后處理數(shù)據(jù)使用
var Pet = require('./models/pet');
// 解析http數(shù)據(jù)
var bodyParser = require('body-parser');

下面還需要設(shè)置body-parser:

var app = express();

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

添加些許寵物

我們的server終于迎來(lái)可以使用的一個(gè)功能了:給寵物商店添加寵物炬藤。

router.get('/', function (req, res) {
    res.json({'message': '歡迎來(lái)到寵物商店'});
});

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});
    });
});

我們?cè)黾恿艘粋€(gè)/pets的相對(duì)路徑來(lái)處理POST請(qǐng)求御铃,并在請(qǐng)求中獲取信息碴里,把用這個(gè)信息來(lái)給初始化的pet的model賦值,并調(diào)用這個(gè)model的save方法保存數(shù)據(jù)到數(shù)據(jù)庫(kù)上真。

打開(kāi)postman咬腋,各個(gè)設(shè)置如圖:


這樣就可以往數(shù)據(jù)庫(kù)添加pet數(shù)據(jù)了。

重構(gòu)代碼

現(xiàn)在的代碼雖然已經(jīng)可以添加寵物寶寶了睡互。但是根竿,后面如果我們還要添加其他功能,比如:查找就珠,更新和刪除等的時(shí)候寇壳,server.js文件勢(shì)必會(huì)不斷增加。這樣給以后代碼的維護(hù)帶來(lái)困擾妻怎。所以我們要重構(gòu)代碼壳炎。

petshop/server/目錄下添加controllers目錄。根據(jù)MVC的模式開(kāi)發(fā)逼侦,把處理業(yè)務(wù)方面的代碼都存放在某個(gè)controller里匿辩。新建pet.js文件。這個(gè)文件就作為pet的controller偿洁。并將寵物的增刪改查都放在這個(gè)文件中處理:

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
};

原來(lái)的server.js也需要重構(gòu):

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
};

認(rèn)證

我們當(dāng)讓不行讓誰(shuí)都可以添加寵物寶寶了撒汉。查看是可以的沟优,添加需要控制涕滋。

passport

Passport就是給基于Express開(kāi)發(fā)的web應(yīng)用的,專(zhuān)注于認(rèn)證中間件挠阁。也有和body-parser相類(lèi)似的使用方法宾肺。passport的功能非常豐富,不過(guò)我們先使用最簡(jiǎn)單的一種認(rèn)證策略侵俗。

安裝:

npm install --save passport-http

認(rèn)證以前首先要有用戶(hù)數(shù)據(jù)锨用。

同時(shí)還有一個(gè)包需要安裝:

npm install --save bcrypt-nodejs

這個(gè)包是用來(lái)給密碼hash用的。

用戶(hù)model

所有關(guān)于用戶(hù)的數(shù)據(jù)都放在MongoDB的user colleciton里隘谣,并有user model與之對(duì)應(yīng)增拥。在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添加了一個(gè)在save方法調(diào)用之前先執(zhí)行的方法寻歧。在這個(gè)方法里首先檢查用戶(hù)的密碼是否有修改掌栅,如果有則使用包bcrypt-nodejs來(lái)hash用戶(hù)的密碼。

User Controller

有了model码泛,就需要對(duì)應(yīng)的controller來(lái)處理猾封。在controllers目錄下新建一個(gè)user.js文件作為user controller。注意:實(shí)際開(kāi)發(fā)的時(shí)候你肯定是不會(huì)把全部用戶(hù)的信息都發(fā)到客戶(hù)端的噪珊,里面包含了hash的用戶(hù)密碼晌缘。

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);

你已經(jīng)可以在這個(gè)路徑:http://localhost:3090/api/users下POST添加用戶(hù)齐莲,GET獲取全部用戶(hù)。

認(rèn)證

在開(kāi)始以前首先確保你已經(jīng)安裝了認(rèn)證需要的包:

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

之后給在user model里添加一個(gè)方法驗(yàn)證password:

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

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

接下來(lái)磷箕,在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);
            }

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

            // 檢查用戶(hù)的密碼
            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來(lái)處理http的用戶(hù)認(rèn)證。首先搀捷,我們通過(guò)用戶(hù)名查找用戶(hù)星掰。如果用戶(hù)存在,接著驗(yàn)證用戶(hù)的密碼是否與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致嫩舟。如果以上兩步通過(guò)驗(yàn)證則用戶(hù)認(rèn)證成功氢烘,否則不成功。

最后一句就是告知passport使用BasicStrategy來(lái)認(rèn)證用戶(hù)家厌。session為false播玖,是告訴passport不存儲(chǔ)用戶(hù)的session。用戶(hù)每一次的http請(qǐng)求都需要提供用戶(hù)名和密碼饭于。

相應(yīng)的更新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');

// 創(chuàng)建一個(gè)express的server
var app = express();

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

// 連接數(shù)據(jù)庫(kù)
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);

...

如果還沒(méi)有數(shù)據(jù)的話(huà)蜀踏,首先使用POST方法添加幾個(gè)用戶(hù)。之后GET用戶(hù)測(cè)試一下掰吕。如果用戶(hù)名果覆、密碼都對(duì)的話(huà)就會(huì)獲得數(shù)據(jù)了。


使用passport包認(rèn)證還有一個(gè)好處殖熟,你可以直接從req獲取user數(shù)據(jù)局待。如:req.user._id獲得用戶(hù)的_id。有些數(shù)據(jù)需要記錄更新數(shù)據(jù)的用戶(hù)菱属,這樣就非常方便了钳榨。

最后

下文使用更加安全的oauth2認(rèn)證。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纽门,一起剝皮案震驚了整個(gè)濱河市薛耻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赏陵,老刑警劉巖饼齿,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蝙搔,居然都是意外死亡缕溉,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)杂瘸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)倒淫,“玉大人,你說(shuō)我怎么就攤上這事〉型粒” “怎么了镜硕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)返干。 經(jīng)常有香客問(wèn)我兴枯,道長(zhǎng),這世上最難降的妖魔是什么矩欠? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任财剖,我火速辦了婚禮,結(jié)果婚禮上癌淮,老公的妹妹穿的比我還像新娘躺坟。我一直安慰自己,他們只是感情好乳蓄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布咪橙。 她就那樣靜靜地躺著,像睡著了一般虚倒。 火紅的嫁衣襯著肌膚如雪美侦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天魂奥,我揣著相機(jī)與錄音菠剩,去河邊找鬼。 笑死耻煤,一個(gè)胖子當(dāng)著我的面吹牛具壮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播违霞,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嘴办,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瞬场!你這毒婦竟也來(lái)了买鸽?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贯被,失蹤者是張志新(化名)和其女友劉穎眼五,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體彤灶,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡看幼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幌陕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诵姜。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搏熄,靈堂內(nèi)的尸體忽然破棺而出棚唆,到底是詐尸還是另有隱情暇赤,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布宵凌,位于F島的核電站鞋囊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瞎惫。R本人自食惡果不足惜溜腐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓜喇。 院中可真熱鬧挺益,春花似錦、人聲如沸乘寒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)肃续。三九已至黍檩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間始锚,已是汗流浹背刽酱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瞧捌,地道東北人棵里。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像姐呐,于是被迫代替她去往敵國(guó)和親殿怜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容