從零開始搭建一個(gè)Express應(yīng)用

業(yè)務(wù)前景

其實(shí)許多技術(shù)還是要應(yīng)用到業(yè)務(wù)中去做暇榴,才會(huì)有不一樣的挑戰(zhàn)和收獲橱赠,公司有自己內(nèi)部管理系統(tǒng)尤仍,主要是用于客戶維護(hù),和核算成本狭姨≡桌玻基于這樣的情況,上方?jīng)Q定前端自己來建立和維護(hù)這樣的系統(tǒng)饼拍,前兩天重新搭建了一遍赡模,現(xiàn)在打算整理出來,來一起討論這個(gè)搭建過程师抄。

一漓柑,寫一個(gè)hello world

1,新建項(xiàng)目文件夾

mkdir express-2020 && cd express-2020

2,安裝express

yarn add express --save 或 npm install express --save

3,新建server.js文件

const express = require("express");
const app = express();
app.get("/",(req,res)=>{res.send("hello world")});
app.listen("3000",()=>{    console.log("run at 3000")});

終端執(zhí)行

node server

我們可以看到,服務(wù)已經(jīng)運(yùn)行了

image

打開瀏覽器叨吮,輸入地址 http://localhost:3000/辆布,可以看到我們?cè)L問成功,hello world

image

到此為止我們已經(jīng)實(shí)現(xiàn)了所有語言初始化的第一步挤安,hello, world谚殊。

二,訪問靜態(tài)文件

1,使用express的static中間件函數(shù)

const path = require("path");app.use(express.static(__dirname + '/static'))
app.get('/*', function (req, res){    
    res.sendFile(path.resolve(__dirname, 'static', 'index.html'))
})

訪問根路徑之下任何路由返回的是絕對(duì)路徑+“/static”下的index.html文件蛤铜,接下來我們實(shí)驗(yàn)一下

server.js同級(jí)下新增文件夾static,里面創(chuàng)建一個(gè)index.html文件嫩絮,文件結(jié)構(gòu)如下

image

重啟服務(wù)

node server

效果如圖所示

image

現(xiàn)在我們成功運(yùn)行了一個(gè)本地服務(wù),可以通過我們本地ip地址<u style="box-sizing: border-box;">localhost:3000</u>围肥,訪問到static文件下的靜態(tài)資源剿干,默認(rèn)是index.html,如果是example.html則直接訪問<u style="box-sizing: border-box;">localhost:3000/example.html,其實(shí)這時(shí)候</u>我們可以通過本地啟動(dòng)一個(gè)服務(wù),來讓同一局域網(wǎng)下的計(jì)算機(jī)訪問我們的靜態(tài)網(wǎng)頁穆刻。

三置尔,寫一個(gè)接口出來

1,server.js同級(jí)目錄下新增一個(gè)app文件夾氢伟,文件夾下新增index.js榜轿,文件目錄此時(shí)如下

image

分成這樣的項(xiàng)目結(jié)構(gòu),主要是為了server.js朵锣,做總的中間件的控制谬盐,在index.js中做路由的請(qǐng)求分發(fā)。

代碼如下:

const express = require("express");
const app = express();// 處理異常
app.use((err,req,res,next)=>{    
    next(err);
})
export {app as serverIndex};

通過app.use來捕獲異常诚些,如果沒有next(err)飞傀,這個(gè)異常會(huì)被掛起皇型,不會(huì)被垃圾回收機(jī)制所回收,所有的中間件通過next()方法才會(huì)向下執(zhí)行砸烦。

將index.js引入到server.js中

import {serverIndex} from "./app"; app.use(serverIndex)弃鸦;

執(zhí)行 node server,這時(shí)發(fā)現(xiàn),報(bào)錯(cuò)了幢痘。

import {serverIndex} from "./app"; 
^^^^^^

SyntaxError: Unexpected token import
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:617:28)
    at Object.Module._extensions..js (module.js:664:10)
    at Module.load (module.js:566:32)
    at tryModuleLoad (module.js:506:12)
    at Function.Module._load (module.js:498:3)
    at Function.Module.runMain (module.js:694:10)
    at startup (bootstrap_node.js:204:16)
    at bootstrap_node.js:625:3

報(bào)錯(cuò)的原因是import是es6語法中引入方式唬格,此時(shí)我們項(xiàng)目不支持es6,咋辦呢雪隧?

辦法總比困難多西轩,編譯一下就完了。(:

2脑沿,通過babel將es6轉(zhuǎn)為es5,安裝babel

npm i -D babel-cli babel-preset-es2015 babel-preset-stage-2

然后在根目錄下藕畔,新增.babelrc文件,代碼如下:

{     
    "presets": ["es2015", "stage-2"]
}

在package.json中新增如下代碼

"scripts": {        
    "start": "babel-node server.js --presets es2015,stage-2"
}

執(zhí)行命令

npm run start

這時(shí)候發(fā)現(xiàn)運(yùn)行起來了庄拇。

3,新建路由文件login.js注服,和index.js同級(jí)

async function getAsync(req,res){
    res.json(Object.assign({},{msg:"成功",code:0},{data:null}))
}
const wrap = fn => (...args) => fn(...args).catch(e=>{console.log(e)})
let get = wrap(getAsync);

通過wrap函數(shù)包裹住路由接口函數(shù),可以及時(shí)捕獲到異步錯(cuò)誤措近。

在index.js中溶弟,引入login.js中的login函數(shù),這時(shí)候這是一個(gè)get請(qǐng)求,我們用postman試一下

import * as user from "./login";
app.get("/get",user.get);

返回結(jié)果

{
    "msg": "成功",
    "code": 0,
    "data": null
}

我們已經(jīng)完成一個(gè)了一個(gè)簡(jiǎn)單的get請(qǐng)求。

4蚀腿,接下來我們來整一個(gè)post請(qǐng)求温鸽。

首先我們先安裝一個(gè)中間件body-parser辐怕,將post請(qǐng)求攜帶的參數(shù)解析之后放到req.body中

npm i body-parser

在server.js中引入

import bodyParser from 'body-parser';
app.use(bodyParser.json({limit: '100mb'}));// 解析文本格式
app.use(bodyParser.urlencoded({limit: '100mb', extended: true}));

這里只是做了參數(shù)大小限制,更多api用法訪問https://github.com/expressjs/body-parser

繼續(xù)在 login.js中新增一個(gè)login函數(shù),為了方便我們對(duì)code和msg進(jìn)行管理,我們和app文件夾同級(jí)新增一個(gè)config文件夾碳抄,文件夾下新增constants.js文件,里面放我們一些配置信息场绿。

文件目錄如圖所示

image

constants.js

export const Success = {code:0,msg:"成功"};
export const ErrorParam = {code:10001,msg:"參數(shù)錯(cuò)誤"};
export const ErrorAuthentication = {code:10002,msg:"無權(quán)限"};
export const ErrorToken = {code:10003,msg:"token失效"};

login.js

import * as constants from "../config/constants";
async function loginAsync(req,res){    
    let username = req.body.username;    
    let password = req.body.password;    
    if(!username||!password){        
         return res.json(Object.assign({},constants.ErrorParam,{data:null}));
    }    
    if(username=="123" && password=="1"){
        return res.json(Object.assign({},constants.Success,{data:null}));    
    }else{        
        return res.json(Object.assign({},constants.ErrorAuthentication,{data:null}));
    }
}
let login = wrap(loginAsync);
export {login}

接下來在index.js中新增路由

app.post("/login",user.login);

重啟服務(wù)

npm run start

訪問結(jié)果如圖所示

image

現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了常用的兩種請(qǐng)求剖效,get,post。

四焰盗,為請(qǐng)求添加log日志

1,引入express中間件morgan(獲取所有的請(qǐng)求)和winston

npm install --save winston morgan

server.js同級(jí)新建util文件夾璧尸,文件夾下新增logger.js,目錄如下:

image

logger.js

import fs from "fs";import {createLogger,format,transports} from "winston";fs.exists( __dirname + '/../../logs/all.log', function(exists) {    console.log(exists ? "已存在" : "創(chuàng)建成功");  });let logger = createLogger({    level: 'http',    handleExceptions: true,    json: true,    transports: [        // 可以定義多個(gè)文件,主要輸出的info里面的文件        new transports.File({            level: 'http',            filename: __dirname + '/../../logs/all.log',            maxsize: 52428800,            maxFiles: 50,            tailable: true,            format:format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' })            }),        new transports.Console({            level: 'debug',            prettyPrint: true,            colorize: true        })    ],});logger.stream = {    write: function(message, encoding){        logger.http(message);    }};export {logger};

logger文件主要是記錄http日志到all.log文件中,日志文件不存在則創(chuàng)建文件熬拒。

詳細(xì)用法請(qǐng)查看:https://github.com/bithavoc/express-winston

2爷光,server.js中引入logger日志功能,<u style="box-sizing: border-box;">切記logger放在路由之前才會(huì)輸出日志梦湘。</u>

server.js

import morgan from 'morgan';
import {logger} from './utils/logger';
app.use(morgan(":date[iso] :remote-addr :method :url :status :user-agent",{stream:logger.stream}))

morgan輸出日志信息可以配置瞎颗,morgan(format,option),可參考https://github.com/expressjs/morgan

3捌议,重啟服務(wù)哼拔,請(qǐng)求/login接口,而且文件目錄下新增了log/all.log文件瓣颅,控制臺(tái)效果如下:

{"message":"2020-01-19T11:58:31.385Z ::ffff:192.168.1.169 POST /api/login?username=123&password=1 200 PostmanRuntime/7.15.0\n","level":"http"}

現(xiàn)在我們的請(qǐng)求日志就加好了倦逐。

五,連接mysql數(shù)據(jù)庫

1宫补,安裝數(shù)據(jù)庫檬姥,執(zhí)行sql,看這個(gè)mysql菜鳥教程https://www.runoob.com/mysql/mysql-install.html

新建數(shù)據(jù)庫db_user并執(zhí)行以下sql

CREATE TABLE `user` (  
`id` int(11) NOT NULL AUTO_INCREMENT, 
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
`password` varchar(128) NOT NULL,  
`realname` varchar(64) DEFAULT NULL,  
`email` varchar(32) DEFAULT NULL,  
`is_link` tinyint(1) DEFAULT '1',  
PRIMARY KEY (`id`),  
UNIQUE KEY `email` (`email`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

現(xiàn)在我們創(chuàng)建了一個(gè)user表粉怕,表結(jié)構(gòu)如下

image

user表現(xiàn)在為空表健民,我們首先寫一個(gè)接口為表中新增數(shù)據(jù),user表里面有用戶密碼信息贫贝,所以我們?cè)俳酉聛淼拇a中會(huì)引入node的<u style="box-sizing: border-box;">crypto</u>模塊進(jìn)行密碼加密秉犹。

2,這個(gè)時(shí)候我們需要安裝mysql2/promise

npm i mysql2/promise

安裝完成之后我們可以用async/await來操作數(shù)據(jù)庫稚晚,相對(duì)于之前的mysql崇堵,mysql2/promise好處是,操作數(shù)據(jù)庫完成之后不需要手動(dòng)釋放客燕,可自行釋放連接池鸳劳,減少占用進(jìn)程。

3也搓,在config文件夾下新建db.js, 為了對(duì)數(shù)據(jù)庫連接的統(tǒng)一管理赏廓,在constants.js中配置數(shù)據(jù)庫連接

export const MysqlUser = "mysql://root:123456@192.168.1.169:3306/db_user";

db.js

import mysql from "mysql2/promise";
import {MysqlUser} from "./constants";
const db_user = mysql.createPool(MysqlUser);
export {db_user}

4,login.js中引入<u style="box-sizing: border-box;">db_user</u>數(shù)據(jù)庫連接池还绘,新增addUser函數(shù)楚昭。

import crypto from "crypto";
async function addUserAsync(req,res){    
    let realname = req.body.realname;    
    let email = req.body.email;    
    let password = req.body.password;    
    if(!realname||!email||!password){        
        return res.json(Object.assign({},constants.ErrorParam,{data:null}));        
    }    
    let pass = await makePassword(password,'~9MnqsfOH@',1000,32,'sha256');    
    if(pass){        
        pass = 'pbkdf2_sha256$'+1000+"$~9MnqsfOH@$"+pass;   
    }    
    await db_user.execute(`INSERT INTO user (realname,password,email,is_link) VALUES(?,?,?,?)`,[realname,pass,email,1]);    
    res.json(Object.assign({},constants.Success,{data:null}))
}
function makePassword(password, salt, iterations, keylen, digest) {    
    return new Promise(function(resolve, reject) {      
        crypto.pbkdf2(password, salt, iterations, keylen, digest, (err, key) => {        
        if (err) {          
            reject(err);        
        } else {          
            resolve(key.toString('base64'));        
        }      
})})}
let addUser = wrap(addUserAsync);
export {addUser}

上面代碼crypto.pbkdf2加密,對(duì)應(yīng)的參數(shù)依次為拍顷,密碼抚太,加鹽,次數(shù)昔案,長(zhǎng)度尿贫,加密方式

index.js

app.post("/user/add",user.addUser);

postman請(qǐng)求/user/add接口

image

然后我們通過mysql客戶端,navicat查詢一下我們剛才新插入的數(shù)據(jù)

執(zhí)行sql

SELECT * from user WHERE realname = "多啦A夢(mèng)"
image

到這一步我們實(shí)現(xiàn)了向數(shù)據(jù)庫里添加用戶踏揣。

六庆亡,查詢數(shù)據(jù)庫

剛才我們?cè)跀?shù)據(jù)新增了一條數(shù)據(jù),現(xiàn)在我們新增一個(gè)查詢接口捞稿,參數(shù)取自req.query

login.js

async function userListAsync(req,res){    
    let realname = req.query.realname;    
    if(!realname){        
        res.json(Object.assign({},constants.ErrorParam,{data:null}));        
        return     
    }    
    let [rows,d] = await db_user.execute(`SELECT * FROM user WHERE realname = ?`,[realname]);    
    res.json(Object.assign({},constants.Success,{data:rows[0]}))
};
let userList = wrap(userListAsync);
export {userList}

index.js

app.get("/user/query",user.userList);

記得重啟服務(wù)又谋,請(qǐng)求看一下效果:

image

七拼缝,JWT(json web token)登錄

大多數(shù)網(wǎng)站登錄之后返回一個(gè)token字符串,每次請(qǐng)求放在header中彰亥,后臺(tái)根據(jù)解析token中的信息來返回相應(yīng)的數(shù)據(jù)咧七。

安裝jwt

npm i jsonwebtoken

生成token

寫一個(gè)login登錄接口,通過正確的用戶名密碼換取jwt生成的token任斋。

了解更多jwt https://github.com/auth0/node-jsonwebtoken

<u style="box-sizing: border-box;">登錄生成token思路為</u>:

將當(dāng)前請(qǐng)求的用戶名在數(shù)據(jù)庫中進(jìn)行查詢继阻,查詢到數(shù)據(jù)之后取出密碼,并將當(dāng)前的密碼按照插入數(shù)據(jù)庫的邏輯加密废酷,將加密的字符串和取出的密碼進(jìn)行比對(duì)瘟檩,若相同則認(rèn)為是密碼正確,生成包含email的token返回澈蟆。

jwt生成token需要密鑰墨辛,此時(shí)我們將密鑰字符串存儲(chǔ)在了contants.js中,token失效期10h。

constants.js

export const JwtSecret = "test1~@!^";

login.js

import jwt from "jsonwebtoken";
async function loginAsync(req,res){    
    let email = req.body.username;    
    let password = req.body.password;    
    if(!email||!password){        
        return res.json(Object.assign({},constants.ErrorParam,{data:null}))   
     }    
    let [result,d] = await db_user.execute(`select password from user where email = ?`,[email]);    
    let [algorithm, iterations, salt, hash] = result[0].password.split('$', 4);    
    let valid = await comparePassword(password, salt, parseInt(iterations, 10), 32, 'sha256', hash);    
    if(valid){       
             // 返回token        
             const token = jwt.sign({user:req.body.username},constants.JwtSecret,{expiresIn:"10h"});       
            res.json(Object.assign({},constants.Success,{data:{token:token}}))    
    }else{        
            res.json(Object.assign({},constants.ErrorPassword,{data:null}))    
}};
function comparePassword(password, salt, iterations, keylen, digest, hash) {    
return new Promise(function(resolve, reject) {        
crypto.pbkdf2(password, salt, iterations, keylen, digest, (err, key) => {           
 if (err) {               
     reject(err);           
 } else {               
     resolve(key.toString('base64') === hash);          
  }})})
};
let login = wrap(loginAsync);
export {login}

index.js

app.post("/login",user.login);

重啟服務(wù)之后趴俘,請(qǐng)求拿到token

image

瀏覽器請(qǐng)求

請(qǐng)求相比之前參數(shù)攜帶沒什么區(qū)別背蟆,只是在header請(qǐng)求頭中給Authorization賦值:Bearer+“ ”+上面請(qǐng)求返回的token。

如圖所示

新增token校驗(yàn)中間件

為了每次校驗(yàn)token,我們?cè)谶M(jìn)入邏輯之前先解析token

index.js

import jwtFnc from "jsonwebtoken";
import {db_user} from "../config/db";// 中間件哮幢,處理tokenasync 
function checkToken(req,res,next){    
let jwt = req.get('Authorization');    
    if(!jwt){        
        return res.json(constants.ErrorAuthentication);    
    }    
    // 解析 jwt.verify    
    let jwtArr = jwt.split(" ");    
    if(jwtArr.length !== 2 || jwtArr[0] !== 'Bearer'){        
        return res.json(constants.ErrorAuthentication)    
    }    
    try{        
        // 解析的時(shí)候可以知道token是否過期        
        let userData = jwtFnc.verify(jwtArr[1],constants.JwtSecret);        
        // 校驗(yàn)用戶是否存在        
        let [rows,d] = await db_user.execute(`SELECT id FROM user WHERE email = ?`, [userData.user]);        
        if(rows.length>0){            
            req.jwtUsername = userData.user;        
        }else{           
             return res.json(constants.ErrorAuthentication)        
        }       
     }catch(e){        
        return res.json(constants.ErrorToken);   
     }    
        next();
    };
// 那個(gè)接口使用带膀,就在路由后邊加上這個(gè)中間件,校驗(yàn)通過執(zhí)行next(),才會(huì)往下執(zhí)行
app.get("/user/query",checkToken,user.userList);

我們給剛才的/user/query加上了token校驗(yàn)現(xiàn)在橙垢,不加token請(qǐng)求一下

image

我們?cè)趆eader加上token試一下

image

此時(shí)我們只是校驗(yàn)了token的格式和有效期垛叨,還有客戶信息,我們可以看到解析完成之后我們將信息拼在了body中柜某,可以在login函數(shù)中進(jìn)一步去校驗(yàn)權(quán)限之類的東西.......

八嗽元,解析excel文件

解析前端傳過來的文件,首先我們需要一個(gè)可以接收文件的中間件connect-multiparty喂击,他可以把前端傳過來的文件轉(zhuǎn)到req.body.files在接收剂癌。

安裝connect-multiparty

npm i connect-multiparty

要解析excel文件,需要安裝node-xlsx

npm i node-xlsx

login.js新增解析文件方法getFileDataAsync

import xlsx from "node-xlsx";
async function getFileDataAsync(req,res){    
    const filePath = req.files.file.path;    
    // 讀取xlsx文件    
    const data = xlsx.parse(req.files.file.path);    
    onsole.log(data)    
    res.json(Object.assign({},constants.Success,{data:{token:null}}))
}

index.js

import  multipart from 'connect-multiparty';
const multipartMiddleware = multipart();
app.post("/upload",checkToken,multipartMiddleware,user.getFileData);

我們新建一個(gè)excel翰绊,

image

請(qǐng)求下佩谷,我們?cè)诳刂婆_(tái)看下輸出:

image

九,根據(jù)不同場(chǎng)景區(qū)分不同的路由

我們有時(shí)候可能對(duì)于user模塊期望訪問的是/user/, 對(duì)于list期望請(qǐng)求/list/监嗜。這時(shí)候我們用到express的router模塊谐檀。

index.js

//創(chuàng)建實(shí)例
let usersRouter = express.Router();
let listRouter = express.Router();
app.use("/user",usersRouter);
app.use("/order",listRouter);
userRouter.get("/list",func) // 相當(dāng)于請(qǐng)求 “/user/list”
listRouter.get("/get",func1) //相當(dāng)于請(qǐng)求 “/list/get”

十,定時(shí)任務(wù)

如果有定時(shí)任務(wù)需要用到node-schedule模塊

可以參考https://github.com/node-schedule/node-schedule.git

安裝node-schedule

npm i node-schedule

index.js

import schedule from 'node-schedule';
//定時(shí)任務(wù),可以根據(jù)rule配置不同時(shí)間間隔
//每五分鐘執(zhí)行一次
let rule = new schedule.RecurrenceRule();
rule.minute = [1, 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56];
let count = 0;
schedule.scheduleJob(rule, async function () {   
     console.log(++count);
});

十一裁奇,解決跨域

本地調(diào)試過程中可能會(huì)出現(xiàn)跨域問題桐猬,我們可以通過如下設(shè)置來解決

server.js

if (app.get('env') === 'development') {    
    app.use(function (req, res, next) {       
        res.setHeader('Access-Control-Allow-Origin', req.get('Origin') || '');        
        res.setHeader('Access-Control-Allow-Credentials', 'true');       
        res.setHeader('Access-Control-Allow-Headers', 'Authorization,x-requested-with');       
        res.setHeader('Access-Control-Allow-Methods', 'POST, GET');        
     if (req.method == 'OPTIONS') {           
         res.send(200);       
     }else{        
        next();        
    }})
}

十二,安全最佳實(shí)踐

關(guān)于最佳實(shí)踐刽肠,了解更多點(diǎn)擊http://expressjs.com/zh-cn/advanced/best-practice-security.html

安裝helmet設(shè)置請(qǐng)求頭

npm install --save helmet

server.js

import helmet from 'helmet';
app.use(helmet());
app.disable('x-powered-by')

十三溃肪,打包文件

到這一步呢免胃,我們已經(jīng)實(shí)現(xiàn)了express的簡(jiǎn)單搭建,但是要把代碼部署到服務(wù)器上惫撰,還需要我們進(jìn)行進(jìn)一步打包杜秸。

這里呢,我們使用babel進(jìn)行打包润绎,需要把我們所有文件打進(jìn)一個(gè)文件夾中。

1诞挨,我們需要新建src文件夾莉撇,此時(shí)的代碼結(jié)構(gòu)如下

--src
    --app
    --config
    --util
    --static
server.js

package.json 新增打包script

"build": "babel src -d lib"

執(zhí)行命令

npm run build

我們發(fā)現(xiàn)src同級(jí)目錄下新增了lib文件夾

這時(shí)候我們可以直接啟動(dòng)lib/server.js,所以我在script分了三步

"scripts": {        
           "start": "babel-node src/server.js --presets es2015,stage-2",        
            "build": "babel src -d lib",        
            "dev": "babel-node lib/server.js"    
},

最后我們的項(xiàng)目結(jié)構(gòu)為

image

感興趣的同學(xué)還可以了解下pm2,這就不做展開了惶傻。

補(bǔ)充:

寫的不好還請(qǐng)諒解棍郎,以上也有許多疏漏的地方,有些地方畢竟做的不是很嚴(yán)謹(jǐn)银室,歡迎指正涂佃。

github地址:https://github.com/songtao1/express-2020

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蜈敢,隨后出現(xiàn)的幾起案子辜荠,更是在濱河造成了極大的恐慌,老刑警劉巖抓狭,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伯病,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡否过,警方通過查閱死者的電腦和手機(jī)午笛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苗桂,“玉大人药磺,你說我怎么就攤上這事∶何埃” “怎么了癌佩?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)便锨。 經(jīng)常有香客問我驼卖,道長(zhǎng),這世上最難降的妖魔是什么鸿秆? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任酌畜,我火速辦了婚禮,結(jié)果婚禮上卿叽,老公的妹妹穿的比我還像新娘桥胞。我一直安慰自己恳守,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布贩虾。 她就那樣靜靜地躺著催烘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缎罢。 梳的紋絲不亂的頭發(fā)上伊群,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音策精,去河邊找鬼舰始。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咽袜,可吹牛的內(nèi)容都是我干的丸卷。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼询刹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼谜嫉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起凹联,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤沐兰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蔽挠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體僧鲁,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年象泵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寞秃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡偶惠,死狀恐怖春寿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情忽孽,我是刑警寧澤绑改,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站兄一,受9級(jí)特大地震影響厘线,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜出革,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一造壮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦耳璧、人聲如沸成箫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹬昌。三九已至,卻和暖如春攀隔,著一層夾襖步出監(jiān)牢的瞬間皂贩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工昆汹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留明刷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓筹煮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親居夹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子败潦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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