Node.js課程知識講解大全(六)

4. 增加郵箱驗證功能

剛剛我們只是實現(xiàn)了登錄的流程观游,不過里面用的驗證碼是假的旱爆,我們考慮下實現(xiàn)真正的郵箱驗證碼發(fā)送功能缰犁。

第三方包nodemailer為我們封裝了郵件自動發(fā)送功能刑枝,它可以自己搭設一個郵箱服務器進行簡單配置鬼佣,即可自動發(fā)送郵件。官方英文文檔參考這里额获。

安裝nodemailer

$ npm install --save nodemailer

創(chuàng)建一個helpers目錄下的mailer.js文件够庙,這個文件封裝發(fā)送郵件相關(guān)的邏輯,代碼參考官方的Demo進行配置即可:

const nodemailer = require('nodemailer')

// node_modules/nodemailer/lib/well-known/services中提供了所有主流郵箱的配置列表

let transporter

const createMailServer = async function () {

? // 創(chuàng)建一個可以測試用的郵件服務器抄邀,生產(chǎn)環(huán)境不要使用測試的郵件服務器首启,一定要部署公司的郵件服務器

? let testAccount = await nodemailer.createTestAccount();

? // 配置郵件服務器

? // 這里是拿網(wǎng)易163賬號做測試,你可以手動去網(wǎng)易注冊一個163賬號撤摸。

? // 一定一定要注意,讓你的郵箱開啟SMTP服務才可以發(fā)送郵件。

? return nodemailer.createTransport({

? ? "host": "smtp.163.com",

? ? "port": 465,

? ? "secure": true,

? ? auth: {

? ? ? user: '你的用戶名', // 你的注冊郵箱賬號准夷,不需要加郵箱后綴@163.com

? ? ? pass: 'R8qUs23VW4co' // 郵箱密碼

? ? }

? });

}

// 基于隨機值钥飞,生成動態(tài)的驗證碼

var generateVerifyCode = function (length) {

? if (length < 4) {

? ? throw new Error('驗證碼至少是4位')

? }

? var code = ''

? for (var i = 0; i < length; i++) {

? ? const r = Math.random() * 10 + ''

? ? const values = r.split('.')

? ? code = code.concat(values[0])

? }

? return code

}

// 發(fā)送郵件驗證碼,返回Promise的驗證碼結(jié)果

async function sendMail (to) {

? if (!transporter) {

? ? transporter = await createMailServer()

? }

? var verifyCode = generateVerifyCode(4)

? // 發(fā)送郵件

? var info = await transporter.sendMail({

? ? from: '"扣扣音樂" <mingming_teacher@163.com>', // sender address

? ? to: to, // list of receivers

? ? subject: '驗證碼', // Subject line

? ? text: '', // plain text body

? ? html: '<p>您正在登錄扣扣曲庫管理衫嵌,驗證碼是:<b>' + verifyCode + '</b></p>' // html body

? });

? if (info && info.accepted && info.accepted.includes(to)) {

? ? // 說明發(fā)送成功了

? ? return verifyCode

? } else {

? ? throw new Error('驗證碼發(fā)送失敗')

? }

}

module.exports = {

? sendMail

}

可以先建一個test目錄读宙,其中編寫測試代碼測試一下接口好不好用,測試完成之后楔绞,修改routes/api.js的驗證碼驗證邏輯结闸。

var express = require('express');

var mailer = require('../helpers/mailer')

var router = express.Router();

var verifyCodeMap = new Map()

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

? const body = req.body

? const email = body.email

? if (email) {

? ? mailer.sendMail(email).then((verifyCode) => {

? ? ? verifyCodeMap.set(email, verifyCode)

? ? ? console.log('郵件發(fā)送成功' + verifyCode)

? ? ? res.send('驗證碼已經(jīng)發(fā)送到您的郵箱,請注意查收')

? ? }).catch((e) => {

? ? ? console.log('郵件發(fā)送失敗' + e.message)

? ? ? res.status(500).send(e.message)

? ? })

? } else {

? ? res.status(400).send('缺少email參數(shù)')

? }

})

/* 登錄頁面 */

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

? const body = req.body

? if (body.username === '小明' && body.password === '123456') {

? ? const email = body.email && body.email.toLowerCase()

? ? const verifyCode = body.verifyCode && body.verifyCode.toLowerCase()

? ? if (verifyCodeMap.get(email) === verifyCode) {

? ? ? req.session.isLogin = true

? ? ? if (body.online === 'online') {

? ? ? ? // 以后處理

? ? ? }

? ? ? res.send({

? ? ? ? username: '小明',

? ? ? ? age: 34,

? ? ? ? school: '清華大學'

? ? ? });

? ? } else {

? ? ? res.status(400).send('驗證碼驗證錯誤')

? ? }

? } else {

? ? res.status(401).send('登錄失敗')

? }

});

修改前端頁面login.ejs的獲取驗證碼邏輯:

function postEmailVerifyCode () {

? var value = document.getElementById('email').value

? if (value) {

? ? // 正則表達式記得去網(wǎng)站 https://regex101.com/ 驗證一下再測試代碼:

? ? if (/^\S+@\S+\.\S+$/.test(value)) {

? ? ? $.post('/api/v1/email/verifyCode', {email: value}, function (data) {

? ? ? ? console.log('發(fā)送驗證碼成功')

? ? ? ? alert(data)

? ? ? })

? ? } else {

? ? ? alert('郵箱格式不正確')

? ? }

? } else {

? ? alert('請?zhí)顚戉]箱')

? }

}

之后測試結(jié)果酒朵,如果測試成功桦锄,恭喜你和老師一樣成功的完成了郵箱驗證碼功能。

5. 使用jwt改進session

Session解決了HTTP無狀態(tài)的問題蔫耽,它可以通過每次請求的Session id來判斷用戶身份结耀。但是,Session有一個比較明顯的問題匙铡,就是如果服務器是分布式架構(gòu)(集群服務器)图甜,會導致多個服務器之間的Session無法共享。因為Session是保存在服務器上的鳖眼,因此解決這個問題非常困難黑毅。這時,JWT就應運而生钦讳。

什么JWT

JWT(全名JSON Web Token)摒棄了Session保存在服務器的而無法共享的問題矿瘦,它把用戶數(shù)據(jù)通過加密的方式直接存儲在本地客戶端,這樣每次請求都把用戶數(shù)據(jù)再回傳回去蜂厅,相當于客戶端替代了服務器保存Session的工作匪凡。

JWT 的原理

服務器認證以后,生成一個用戶信息的 JSON 對象掘猿,以后病游,客戶端與服務端通信的時候,都要發(fā)回這個 JSON 對象稠通。服務器完全只靠這個對象認定用戶身份衬衬。為了防止用戶篡改數(shù)據(jù),服務器在生成這個對象的時候改橘,會加上簽名滋尉。

這樣,服務器就不保存任何 Session 數(shù)據(jù)了飞主,也就是說狮惜,服務器變成無狀態(tài)了高诺,從而比較容易實現(xiàn)擴展。

JWT 由三個部分組成:

Header(頭部):一個 JSON 對象碾篡,描述 JWT 的元數(shù)據(jù)虱而;

Payload(負載):一個 JSON 對象,用來存放實際需要傳遞的數(shù)據(jù)开泽;

JWT 規(guī)定了7個官方字段牡拇,供選用:

iss (issuer):簽發(fā)人

exp (expiration time):過期時間

sub (subject):主題

aud (audience):受眾

nbf (Not Before):生效時間

iat (Issued At):簽發(fā)時間

jti (JWT ID):編號

除了官方字段,你還可以在這個部分定義私有字段

Signature(簽名):對前兩部分的簽名穆律,防止數(shù)據(jù)篡改惠呼。

JWT保存的格式是:Header.Payload.Signature固以,例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

它實際的數(shù)據(jù)時:

Header:{

? "alg": "HS256",

? "typ": "JWT"

}易迹,

Payload:{

? "sub": "1234567890",

? "name": "John Doe",

? "iat": 1516239022

},

HMACSHA256(

? base64UrlEncode(header) + "." +

? base64UrlEncode(payload),

your-256-bit-secret

)

JWT 默認是不加密的大磺,任何人都可以讀到贡歧,所以不要把秘密信息放在這個部分滩租。

JWT在加密使用時,需要使用非對稱加密的方式利朵,提供一個私鑰和公鑰律想。關(guān)于公鑰私鑰的概念,可以參考這里绍弟。

JWT的傳輸方式

客戶端進行登錄驗證

服務器收到請求后驗證用戶身份技即,驗證失敗則返回失敗結(jié)果,驗證成功樟遣,把用戶非敏感數(shù)據(jù)應用JWT的規(guī)則進行簽名加密而叼。

服務器把加密的JWT數(shù)據(jù)發(fā)送給客戶端

客戶端接收到之后存儲在本地Cookie或者localstorage中。

客戶端以后的每次請求都攜帶JWT數(shù)據(jù)豹悬,你可以把它放在 Cookie 里面自動發(fā)送葵陵,但是這樣不能跨域,所以更好的做法是放在 HTTP 請求的頭信息Authorization字段里面瞻佛。當然脱篙,也可以放到post請求的body里面。

服務器端驗證Token是否合法伤柄,合法才會響應請求返回數(shù)據(jù)绊困。

JWT有哪些缺點

JWT 的最大缺點是,由于服務器不保存 session 狀態(tài)适刀,一旦 JWT 簽發(fā)了秤朗,在到期之前就會始終有效。

JWT 本身包含了認證信息笔喉,一旦泄露取视,任何人都可以獲得該令牌的所有權(quán)限硝皂。因此,JWT 的有效期應該設置得比較短贫途。

為了減少盜用吧彪,JWT 不應該使用 HTTP 協(xié)議明碼傳輸,要使用 HTTPS 協(xié)議傳輸丢早。

JWT的適用場景

保持用戶登錄狀態(tài)

多個網(wǎng)站共享用戶狀態(tài)(第三方登錄)

JWT和Session可以一起使用,Session用來保持用戶狀態(tài)秧倾,JWT用來驗證用戶身份怨酝。

接下來我們把Session機制改造成JWT的形式來處理用戶登錄。

安裝

npm install --save jsonwebtoken

jsonwebtoken是Node.js上實現(xiàn)JWT的工具那先,中文參考這里农猬。

使用

因為jwt是把token保存在客戶端,因為不存在服務器端的清理機制售淡,所以可以去掉logout接口斤葱。

改造登錄接口,再添加一個驗證接口用于驗證token揖闸。

var express = require('express');

var mailer = require('../helpers/mailer')

var jwt = require('jsonwebtoken')

var router = express.Router();

var verifyCodeMap = new Map()

// 簽名或私鑰

var secret = 'fdsfdsfkljdskfjdsfkjdfkl'

/* 登錄頁面 */

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

? const body = req.body

? if (body.username === '小明' && body.password === '123456') {

? ? const email = body.email && body.email.toLowerCase()

? ? const verifyCode = body.verifyCode && body.verifyCode.toLowerCase()

? ? if (verifyCodeMap.get(email) === verifyCode) {

? ? ? // req.session.isLogin = true

? ? ? // if (body.online === 'online') {

? ? ? //? // 以后處理

? ? ? // }

? ? ? // res.send({

? ? ? //? username: '小明',

? ? ? //? age: 34,

? ? ? //? school: '清華大學'

? ? ? // });

? ? ? jwt.sign({isLogin: true, username: body.username, email: email}, secret, function (err, token) {

? ? ? ? if (err) {

? ? ? ? ? res.status(500).send('用戶加密報錯')

? ? ? ? } else {

? ? ? ? ? if (body.online === 'online') {

? ? ? ? ? ? // 以后處理

? ? ? ? ? }

? ? ? ? ? res.send({

? ? ? ? ? ? token,

? ? ? ? ? ? username: '小明',

? ? ? ? ? ? age: 34,

? ? ? ? ? ? school: '清華大學'

? ? ? ? ? });

? ? ? ? }

? ? ? });

? ? } else {

? ? ? res.status(400).send('驗證碼驗證錯誤')

? ? }

? } else {

? ? res.status(401).send('登錄失敗')

? }

});

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

? const body = req.body

? jwt.verify(body.token, secret, function(err, decoded) {

? ? if(err){

? ? ? res.status(401).send('用戶身份驗證失敗');

? ? }else{

? ? ? console.log(decoded)

? ? ? res.send(decoded)

? ? }

? });

})

module.exports = router;

6. 增刪查改實現(xiàn)

a. 項目解耦和模塊化

接下來我們要編寫業(yè)務邏輯部分揍堕,這塊是重頭戲,在編寫過程中汤纸,我們要更規(guī)范得去編寫代碼衩茸,因此需要對項目的目錄結(jié)構(gòu)進行一個合理劃分。再來看下我們一開始創(chuàng)建項目時的README.md文件:

#### 軟件架構(gòu)

├── README.md - 項目文檔<br/>

├── app.js - 初始化應用<br/>

├── bin - 命令腳本<br/>

├── database - 數(shù)據(jù)庫配置<br/>

├── controllers - 定義路由處理的實現(xiàn)邏輯<br/>

├── helpers - 可以被項目各部分所調(diào)用的功能函數(shù)和代碼<br/>

├── middlewares - Express 中間件贮泞,將要處理在進入路由之前的請求<br/>

├── models - 表示數(shù)據(jù)楞慈,實現(xiàn)業(yè)務邏輯和處理存儲<br/>

├── package.json - 項目配置及其依賴的包<br/>

├── public - 包含所有的靜態(tài)文件,像圖片啃擦、樣式和腳本<br/>

├── routes - 定義路由,這里的路由僅用于轉(zhuǎn)發(fā)<br/>

├── tests - 測試在其他文件夾的的代碼<br/>

└── views - 提供模板文件囊蓝,模板文件將會在你路由中進行渲染和使用<br/>

我們需要按照這個目錄結(jié)構(gòu)去補充幾個目錄,包括database令蛉、controllers聚霜、models和對routes內(nèi)部的代碼進行解耦。

database處理和數(shù)據(jù)庫操作有關(guān)的代碼言询,但不包括增刪改查的業(yè)務邏輯

routes只負責分發(fā)接口

controllers負責路由分發(fā)接口的數(shù)據(jù)預處理

models負責建立對象模型和增刪改查

解耦是編程中的一個很常用但很關(guān)鍵的技巧俯萎,它是一種編程思想,核心思維是把一個模塊分隔成幾個獨立的模塊运杭,這些模塊之間通過接口來交互夫啊,以便讓程序的代碼更清晰,易于維護和單元測試辆憔。

b. 安裝MongoDB和mongoose

MongoDB的安裝方式上文已經(jīng)學過撇眯,不再贅述报嵌,只需要記住需要通過mongod命令啟動一下數(shù)據(jù)庫。

安裝mongoose

$ npm install --save mongoose

啟動數(shù)據(jù)庫熊榛,這里一定要注意锚国,mongoose是會緩存查詢操作的,如果查詢老是報錯玄坦,回頭看一下數(shù)據(jù)庫是否通過命令啟動成功了

$ mongod

新建database/connect.js文件血筑,用來編寫數(shù)據(jù)庫啟動代碼:

const mongoose = require('mongoose')

module.exports = function (callback) {

? // 需要創(chuàng)建一個songs-manager用戶,密碼123456煎楣,設置可讀寫權(quán)限豺总,來源test

? mongoose.connect('mongodb://songs-manager:123456@127.0.0.1:27017/test', {

? ? useNewUrlParser: true,

? ? useUnifiedTopology: true,

? ? useFindAndModify: false

? });

? var db = mongoose.connection;

? db.on('error', function (e) {

? ? callback(e)

? });

? db.once('open', function () {

? ? console.log("數(shù)據(jù)庫連接成功")

? ? callback(null)

? });

}

修改bin/www代碼,讓數(shù)據(jù)庫啟動成功之后再啟動服務器:

#!/usr/bin/env node

// requires ....

var connectDatabase = require('../database/connect')

connectDatabase(function (e) {

? if (e) {

? ? return console.error(e)

? }

// 把啟動服務器的東西放到這里

})

c. 設計數(shù)據(jù)模型

服務器啟動成功择懂,我們就可以設計整個項目最核心的模塊數(shù)據(jù)模型和增刪查改了喻喳。

創(chuàng)建models/record.js文件,這里面的增刪查改很容易出錯困曙,在編寫完成代碼后表伦,一定要編寫js代碼測試下這個接口。測試之前記得保證已經(jīng)通過mongod命令啟動了數(shù)據(jù)庫慷丽。

var mongoose = require('mongoose')

// 模式設計

var schema = new mongoose.Schema({

? name: {type: String, required: true}, // 唱片名稱

? cover: {type: String, required: true}, //封面

? singer: {type: String, required: true}, //歌手

? publishTime: Date,// 發(fā)布時間

? songs: [String]//歌曲名

})

var record = mongoose.model('records', schema);

module.exports = {

? findRecords: function (filter, page, pageSize, callback) {

? ? if (filter && page && pageSize && callback) {

? ? ? record.find(filter, 'id name cover singer publishTime songs', {

? ? ? ? skip: page * pageSize,

? ? ? ? limit: parseInt(pageSize)

? ? ? }, function (err, docs) {

? ? ? ? callback(err, docs)

? ? ? })

? ? } else {

? ? ? throw new Error('缺少參數(shù)或參數(shù)格式錯誤')

? ? }

? },

? createRecord: function (data, callback) {

? ? record.findOne(data, function (err, doc) {

? ? ? if (err) {

? ? ? ? return callback(err)

? ? ? }

? ? ? if (doc) {

? ? ? ? return callback(new Error('同名專輯已存在'))

? ? ? }

? ? ? record.create(data, function (err, doc) {

? ? ? ? callback(null, doc)

? ? ? })

? ? })

? },

? updateRecord: function (id, data, callback) {

? ? record.findByIdAndUpdate(id, data, function (err, doc) {

? ? ? callback(err, doc)

? ? })

? },

? deleteRecord: function (id, callback) {

? ? record.findByIdAndRemove(id, function (err, doc) {

? ? ? callback(err, doc)

? ? })

? }

}

創(chuàng)建一個test/test.js文件蹦哼,測試上面接口node ./test/test.js

var record = require('../models/record')

var connect = require('../database/connect')

// 必須先連接數(shù)據(jù)庫才能測試,否則沒有任何回調(diào)信息打印出來盈魁。

connect(function () {

? // 新建翔怎,注意傳入的數(shù)據(jù)格式值是JSON.stringify之后的數(shù)據(jù)

? record.createRecord({

? ? cover: "https://upload.wikimedia.org/wikipedia/zh/thumb/8/80/S.H.E_Super_Star.jpg/220px-S.H.E_Super_Star.jpg",

? ? name: "Super Star",

? ? publishTime: "2003-10-12T16:00:00.000Z",

? ? singer: "S.H.E",

? ? songs: "\"['半糖主義']\""

? }, function (err, doc) {

? ? if (err) {

? ? ? throw err

? ? }

? ? if(!doc){

? ? ? throw new Error('新建失敗,數(shù)據(jù)庫已存在同名數(shù)據(jù)')

? ? }

? ? console.log('新建成功', doc)

? ? // 更新

? ? record.updateRecord(doc._id, {...doc.toJSON(), singer: 'TFboys'}, function (err, doc) {

? ? ? if (err) {

? ? ? ? throw err

? ? ? }

? ? ? if(!doc){

? ? ? ? throw new Error('更新失敗杨耙,未找到數(shù)據(jù)項')

? ? ? }

? ? ? console.log('修改成功', doc)

? ? ? // 查找

? ? ? record.findRecords({id: doc._id}, "0", "10", function (err, docs){

? ? ? ? if (err) {

? ? ? ? ? throw err

? ? ? ? }

? ? ? ? console.log('查詢成功', docs)

? ? ? ? record.deleteRecord(doc._id, function (err, doc) {

? ? ? ? ? if (err) {

? ? ? ? ? ? throw err

? ? ? ? ? }

? ? ? ? ? console.log('刪除成功', doc)

? ? ? ? ? process.exit(0)

? ? ? ? })

? ? ? })

? ? })

? })

})

d. 編寫路由規(guī)則

因為我們編寫的是增刪改查的接口赤套,所有所有路由都放在api/v1下面。

修改routes/api.js珊膜,這里加入了jwt驗證容握,請求的處理放到controllers/record.js中。

router.route('/records(/:id)?')

? .all(function (req, res, next) {

? ? next()

? ? // 測試接口時车柠,記得先刪除用戶校驗

? ? jwt.verify(body.token, secret, function (err, decoded) {

? ? ? if (err) {

? ? ? ? res.status(401).send('用戶身份驗證失敗');

? ? ? } else {

? ? ? ? // 注意一定要next剔氏,否則進入不了后續(xù)的請求處理中

? ? ? ? next()

? ? ? }

? ? });

? })

? .get(record.get)

? .post(record.post)

? .put(record.put)

? .delete(record.del)

創(chuàng)建controllers/record.js文件,編寫路由處理邏輯:

var {createRecord, updateRecord, deleteRecord, findRecords} = require('../models/record')

// 查詢

const get = function (req, res) {

? const {filter = {}, page, pageSize} = req.body

? findRecords(filter, page, pageSize, function (err, docs) {

? ? if (err) {

? ? ? return res.status(400).send(err.message)

? ? }

? ? res.send(docs)

? })

}

// 新建

const put = function (req, res) {

? createRecord(req.body, function (err, doc) {

? ? if (err) {

? ? ? return res.status(400).send(err.message)

? ? }

? ? res.send(doc)

? })

}

// 修改

const patch = function (req, res) {

? // 這一這里使用params的id數(shù)據(jù)竹祷,body只用來存儲傳值數(shù)據(jù)

? updateRecord(req.params.id, req.body, function (err, doc) {

? ? if (err) {

? ? ? return res.status(400).send(err.message)

? ? }

? ? if(doc){

? ? ? // doc是被替換之前的文檔谈跛,沒什么用

? ? ? res.send(doc)

? ? }else{

? ? ? res.status(400).send('未找到對應的數(shù)據(jù)')

? ? }

? })

}

// 刪除

const del = function (req, res) {

? // 這一這里使用params的id數(shù)據(jù),body只用來存儲傳值數(shù)據(jù)

? deleteRecord(req.params.id, function (err, doc) {

? ? if (err) {

? ? ? return res.status(400).send(err.message)

? ? }

? ? res.send(doc)

? })

}

module.exports = {

? get, put, patch, del

}

最后塑陵,通過PostMan測試下接口是否可行感憾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市令花,隨后出現(xiàn)的幾起案子阻桅,更是在濱河造成了極大的恐慌凉倚,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫂沉,死亡現(xiàn)場離奇詭異稽寒,居然都是意外死亡,警方通過查閱死者的電腦和手機趟章,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門杏糙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尤揣,你說我怎么就攤上這事搔啊。” “怎么了北戏?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長漫蛔。 經(jīng)常有香客問我嗜愈,道長,這世上最難降的妖魔是什么莽龟? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任蠕嫁,我火速辦了婚禮,結(jié)果婚禮上毯盈,老公的妹妹穿的比我還像新娘剃毒。我一直安慰自己,他們只是感情好搂赋,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布赘阀。 她就那樣靜靜地躺著,像睡著了一般脑奠。 火紅的嫁衣襯著肌膚如雪基公。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天宋欺,我揣著相機與錄音轰豆,去河邊找鬼。 笑死齿诞,一個胖子當著我的面吹牛酸休,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祷杈,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼斑司,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吠式?” 一聲冷哼從身側(cè)響起陡厘,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤抽米,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后糙置,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體云茸,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年谤饭,在試婚紗的時候發(fā)現(xiàn)自己被綠了标捺。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡揉抵,死狀恐怖亡容,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冤今,我是刑警寧澤闺兢,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站戏罢,受9級特大地震影響屋谭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜龟糕,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一桐磁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讲岁,春花似錦我擂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至郎任,卻和暖如春秧耗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舶治。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工分井, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人霉猛。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓尺锚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親惜浅。 傳聞我的和親對象是個殘疾皇子瘫辩,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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