從零開始打造 Mock 平臺(tái) - 核心篇

前言

最近一直在搗鼓畢設(shè),準(zhǔn)備做的是一個(gè)基于前后端開發(fā)的Mock平臺(tái)催首,前期花了很多時(shí)間完成了功能模塊的交互∩辈停現(xiàn)在進(jìn)度推到如何設(shè)計(jì)核心功能,也就是Mock數(shù)據(jù)的解析绞愚。

根據(jù)之前的需求設(shè)定加上一些思考叙甸,用戶可以像寫json一般輕松完成數(shù)據(jù)的mock,也可以通過在mock數(shù)據(jù)模型之上進(jìn)行構(gòu)建出復(fù)雜的數(shù)據(jù)模型并在項(xiàng)目中引用位衩。

這看似簡單的需求其實(shí)需要處理幾個(gè)不同的模塊功能以及交互設(shè)計(jì)裆蒸。該如何處理解析不同mock數(shù)據(jù)并進(jìn)行構(gòu)造?前端交互中模擬數(shù)據(jù)該如何處理糖驴?數(shù)據(jù)構(gòu)造時(shí)如何加載用戶設(shè)定的數(shù)據(jù)模型僚祷?錯(cuò)誤捕捉與處理?

這些都暫時(shí)沒有一個(gè)好的處理結(jié)果贮缕。因此想要完成核心功能我們需要明確需求辙谜,并且通過同類產(chǎn)品是如何處理的,通過閱讀它們的源碼來學(xué)習(xí)思想并加入感昼。

明確需求

在明確該功能模塊之前我們可以通過模擬流程來明確装哆。

用戶 -> 添加數(shù)據(jù)模型 - > 實(shí)時(shí)看到構(gòu)造結(jié)構(gòu)

用戶 -> 添加接口 -> 構(gòu)造json格式返回參數(shù) -> 預(yù)覽

構(gòu)造json格式返回參數(shù) 不僅包含返回的正文,同時(shí)也設(shè)定了 header 和 method定嗓。

閱讀源碼

符合大部分需求的開源項(xiàng)目有

mock.js

easy-mock

eolinker

YAPI

DOCCLEVER

MOCK.JS篇

首先我們需要明確現(xiàn)階段大部門的 Mock 平臺(tái)或多或少都是受到?Mock.js?的思想或者是其增強(qiáng)版蜕琴。

我們可以用下面簡單的 json 通過?Mock.js來構(gòu)造數(shù)據(jù):

example:

{

? ? "status|0-1": 0, //接口狀態(tài)

? ? "message": "成功", //消息提示

? ? "data": {

? ? ? ? "counts":"@integer", //統(tǒng)計(jì)數(shù)量

? ? ? ? "totalSubjectType|1-4": [ //4-10意味著可以隨機(jī)生成4-10組數(shù)據(jù)

? ? ? ? ? ? {

? ? ? ? ? ? ? "subjectName|regexp": "大數(shù)據(jù)|機(jī)器學(xué)習(xí)|工具", //主題名

? ? ? ? ? ? ? "subjectType|+1": 1 //類型

? ? ? ? ? ? }

? ? ? ? ],

? ? ? ? "data":[

? ? ? ? ? ? {

? ? ? ? ? ? ? ? "name": "@name", //用戶名

? ? ? ? ? ? ? ? "cname":"@cname",

? ? ? ? ? ? ? ? "email": "@email", //email

? ? ? ? ? ? ? ? "time": "@datetime" //時(shí)間

? ? ? ? ? ? }

? ? ? ? ]}

}

返回結(jié)果

{

? ? "status": 0,

? ? "message": "成功",

? ? "data": {

? ? ? ? "counts": 2216619884890228,

? ? ? ? "totalSubjectType": [

? ? ? ? ? ? {

? ? ? ? ? ? ? ? "subjectNameregexp": "大數(shù)據(jù)|機(jī)器學(xué)習(xí)|工具",

? ? ? ? ? ? ? ? "subjectType": 1

? ? ? ? ? ? },

? ? ? ? ? ? {

? ? ? ? ? ? ? ? "subjectNameregexp": "大數(shù)據(jù)|機(jī)器學(xué)習(xí)|工具",

? ? ? ? ? ? ? ? "subjectType": 2

? ? ? ? ? ? },

? ? ? ? ? ? {

? ? ? ? ? ? ? ? "subjectNameregexp": "大數(shù)據(jù)|機(jī)器學(xué)習(xí)|工具",

? ? ? ? ? ? ? ? "subjectType": 3

? ? ? ? ? ? },

? ? ? ? ? ? {

? ? ? ? ? ? ? ? "subjectNameregexp": "大數(shù)據(jù)|機(jī)器學(xué)習(xí)|工具",

? ? ? ? ? ? ? ? "subjectType": 4

? ? ? ? ? ? }

? ? ? ? ],

? ? ? ? "data": [

? ? ? ? ? ? {

? ? ? ? ? ? ? ? "name": "Ruth Thompson",

? ? ? ? ? ? ? ? "cname": "魯克",

? ? ? ? ? ? ? ? "email": "z.white@young.gov",

? ? ? ? ? ? ? ? "time": "1985-02-06 05:45:21"

? ? ? ? ? ? }

? ? ? ? ]

? ? }

}

而且可以通過其 Mock.Random.extend() 來擴(kuò)展自定義占位符.

example:

Random.extend({

? ? weekday: function(date) {

? ? ? ? var weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

? ? ? ? return this.pick(weekdays);

? ? },

? ? sex: function(date) {

? ? ? ? var sexes = ['男', '女', '中性', '未知'];

? ? ? ? return this.pick(sexes);

? ? }

});

console.log(Random.weekday());? // 結(jié)果: Saturday

console.log(Mock.mock('@weekday'));? // 結(jié)果: Tuesday

console.log(Random.sex());? // 結(jié)果: 男

console.log(Mock.mock('@sex'));? // 結(jié)果: 未知

來延伸所需進(jìn)的拓展。

這個(gè)可以將自定義數(shù)據(jù)模型先進(jìn)行解析宵溅,然后通過extend將其加入凌简。

easy-mock

easy-mock?是我參考的主要項(xiàng)目之一,它的UI交互非常符合我的設(shè)定恃逻,而且作為開源項(xiàng)目可以從它的源碼中學(xué)到很多雏搂。

直接來看它提供接口編輯的頁面

{

? data: {

? ? img: function({

? ? ? _req,

? ? ? Mock

? ? }) {

? ? ? return _req.body.fileName + '_' + Mock.mock('@image')

? ? }

? }

}

可以從上得之它既可以處理Mock數(shù)據(jù)模擬也可以處理函數(shù)藕施,而且它內(nèi)部有一套能處理req的內(nèi)容。

先是在源碼中找了一下畔派,找到幾個(gè)疑似點(diǎn)铅碍,但是不確定,還是在本地裝好環(huán)境,主要是需要按照redis.然后啟動(dòng)服務(wù)去打幾個(gè)斷點(diǎn)輸出线椰。

根據(jù)經(jīng)驗(yàn)先確定?controllers\mock.js?應(yīng)該是處理數(shù)據(jù)模擬的地方胞谈。通過瀏覽源碼并分析,最終定位于 297行處的代碼

? ? await redis.lpush('mock.count', api._id)

? ? if (jsonpCallback) {

? ? ? ctx.type = 'text/javascript'

? ? ? ctx.body = `${jsonpCallback}(${JSON.stringify(apiData, null, 2)})`

? ? ? ? .replace(/\u2028/g, '\\u2028')

? ? ? ? .replace(/\u2029/g, '\\u2029') // JSON parse vs eval fix. https://github.com/rack/rack-contrib/pull/37

? ? } else {

? ? ? ctx.body = apiData

? ? }

首先是看到最終返回的 apiData 憨愉。用過 koa 或者 express 都應(yīng)該清楚 ctx.body 的含義烦绳。然后我在上面寫了句?console.log(apiData)。

然后在瀏覽器端發(fā)送請求配紫【睹埽看下 node 端輸出和瀏覽器端拿到的數(shù)據(jù),基本可以肯定最終輸出就是這個(gè)躺孝。

然后我們往上翻享扔,可以看到這么一段代碼:

const vm = new VM({

? ? ? ? timeout: 1000,

? ? ? ? sandbox: {

? ? ? ? ? Mock: Mock,

? ? ? ? ? mode: api.mode,

? ? ? ? ? template: new Function(`return ${api.mode}`) // eslint-disable-line

? ? ? ? }

? ? ? })

? ? ? console.log('數(shù)據(jù)驗(yàn)證')

? ? ? console.log(mode)

? ? ? vm.run('Mock.mock(new Function("return " + mode)())') // 數(shù)據(jù)驗(yàn)證,檢測 setTimeout 等方法

? ? ? apiData = vm.run('Mock.mock(template())') // 解決正則表達(dá)式失效的問題

通過查詢了解到 VM 是一個(gè)沙盒植袍,可以運(yùn)行不受信任的代碼惧眠。

大概就能了解?easy-mock?通過 vm 沙盒模式運(yùn)行 mode 代碼解析后返回結(jié)果。

核心代碼就是?Mock.mock( template )?這么一句于个。根據(jù)數(shù)據(jù)模板生成模擬數(shù)據(jù)氛魁。

通過查文檔了解 template 是可以直接內(nèi)部寫函數(shù)然后執(zhí)行的。

這樣解析的難度大大下降厅篓,發(fā)現(xiàn)原來并沒有特別復(fù)雜的秀存,依舊是依賴了?Mock.js?的原生方法。

然后我們可以看到?easy-mock?另一的操作就是可以獲取?請求參數(shù)_req羽氮。也就是可以通過以下代碼來根據(jù)請求參數(shù)返回指定數(shù)據(jù)或链。

{

? success: true,

? data: {

? ? default: "hah",

? ? _req: function({

? ? ? _req

? ? }) {

? ? ? return _req

? ? },

? ? name: function({

? ? ? _req

? ? }) {

? ? ? return _req.query.name || this.default

? ? }

? }

}

_req 一看就是從請求參數(shù)中獲得的對象。

Mock.js是沒有這個(gè)對象的档押,我們來找找源碼中是哪里注入了這個(gè)對象株扛。

還是在?mock.js?這個(gè)文件中第234行處找到

? Mock.Handler.function = function (options) {

? ? ? const mockUrl = api.url.replace(/{/g, ':').replace(/}/g, '') // /api/{user}/{id} => /api/:user/:id

? ? ? options.Mock = Mock

? ? ? options._req = ctx.request

? ? ? options._req.params = util.params(mockUrl, mockURL)

? ? ? options._req.cookies = ctx.cookies.get.bind(ctx)

? ? ? return options.template.call(options.context.currentContext, options)

? ? }

通過閱讀?MockJS?的源碼,了解到?Handler是處理數(shù)據(jù)模板的地方汇荐,打個(gè)斷點(diǎn)再輸出一次可以發(fā)現(xiàn)其實(shí)是在?Mock.mock(new Function("return " + mode)())'?之后傳入的參數(shù)。

options._req = ctx.request?這句代碼告訴了我們所謂的?_req是從哪里來的盆繁。

因此這個(gè)技術(shù)點(diǎn)我們也了解了是怎么做的掀淘,那么剩下一個(gè)靈活的支持?restful?通過閱讀源碼發(fā)現(xiàn)其實(shí)也沒怎么處理,只是用?pathToRegexp?進(jìn)行了一次驗(yàn)證油昂。它先是在?middlewares/index.js?中 的?mockFilter?進(jìn)行了路徑正則革娄。

static mockFilter (ctx, next) {

? ? console.log(ctx.path)

? ? const pathNode = pathToRegexp('/mock/:projectId(.{24})/:mockURL*').exec(ctx.path)

? ? console.log(pathNode)

? ? if (!pathNode) ctx.throw(404)

? ? if (blackProjects.indexOf(pathNode[1]) !== -1) {

? ? ? ctx.body = ctx.util.refail('接口請求頻率太快倾贰,已被限制訪問')

? ? ? return

? ? }

? ? console.log('通過篩選')

? ? ctx.pathNode = {

? ? ? projectId: pathNode[1],

? ? ? mockURL: '/' + (pathNode[2] || '')

? ? }

? ? return next()

? }

然后通過存在?redis?里的接口內(nèi)容再進(jìn)行了驗(yàn)證匹配。

const { query, body } = ctx.request

? ? const method = ctx.method.toLowerCase()

? ? const jsonpCallback = query.jsonp_param_name && (query[query.jsonp_param_name] || 'callback')

? ? let { projectId, mockURL } = ctx.pathNode

? ? console.log('ctx.pathNode', ctx.pathNode)

? ? const redisKey = 'project:' + projectId

? ? let apiData, apis, api

? ? console.log('通過URL匹配檢驗(yàn)')

? ? apis = await redis.get(redisKey)

? ? console.log(apis)

? ? if (apis) {

? ? ? apis = JSON.parse(apis)

? ? ? console.log('pure apis', apis)

? ? } else {

? ? ? apis = await MockProxy.find({ project: projectId })

? ? ? console.log('find projectId', apis)

? ? ? if (apis[0]) await redis.set(redisKey, JSON.stringify(apis), 'EX', 60 * 30)

? ? }

? ? if (apis[0] && apis[0].project.url !== '/') {

? ? ? mockURL = mockURL.replace(apis[0].project.url, '') || '/'

? ? }

? ? api = apis.filter((item) => {

? ? ? const url = item.url.replace(/{/g, ':').replace(/}/g, '') // /api/{user}/{id} => /api/:user/:id

? ? ? return item.method === method && pathToRegexp(url).test(mockURL)

? ? })[0]

? ? console.log('api',api)

? ? if (!api) ctx.throw(404)

基本不匹配的路徑請求都是在?item.method === method && pathToRegexp(url).test(mockURL)?這句代碼里被攔截的拦惋。

非常優(yōu)秀的代碼匆浙。通讀下來,加上斷點(diǎn)對其思路邏輯學(xué)到了很多厕妖。

eolinker

它的后端代碼是 PHP 的首尼,這就略過不看了。

YAPI

它的核心后端處理代碼是在?mockServer.js?里

有了之前的閱讀經(jīng)驗(yàn)很快找到處理 Mock 數(shù)據(jù)的地方

? let res;

? ? ? ? res = interfaceData.res_body;

? ? ? ? try {

? ? ? ? ? ? if (interfaceData.res_body_type === 'json') {

? ? ? ? ? ? ? ? res = mockExtra(

? ? ? ? ? ? ? ? ? ? yapi.commons.json_parse(interfaceData.res_body),

? ? ? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ? ? query: ctx.request.query,

? ? ? ? ? ? ? ? ? ? ? ? body: ctx.request.body,

? ? ? ? ? ? ? ? ? ? ? ? params: Object.assign({}, ctx.request.query, ctx.request.body)? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? );

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? res = Mock.mock(res);

? ? ? ? ? ? ? ? } catch (e) {

? ? ? ? ? ? ? ? ? ? yapi.commons.log(e, 'error')

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }


非常簡單粗暴的處理方法言秸。软能。。

對增強(qiáng)功能比較好奇在, 于是在?common\mock-extra.js?里找到了?mock(mockJSON, context)方法举畸。根據(jù)參數(shù)其實(shí)就能了解綁定上下文然后做了一些動(dòng)作查排。這里就不展開詳細(xì)。等之后開發(fā)的時(shí)候用到再去細(xì)讀抄沮。因?yàn)檫@是做了其自己的增強(qiáng)的Mock功能,而暫時(shí)不需要這方面的考慮跋核。

DOClecer

這個(gè)項(xiàng)目是國內(nèi)一個(gè)創(chuàng)業(yè)團(tuán)隊(duì)做的,我也加入了其官方群。雖然還沒有用過。不過不妨礙閱讀其源碼了解思路昼窗。不過講道理這個(gè)代碼組織風(fēng)格是挺糟糕的肺然。。已骇。

而且源碼中不止一次出現(xiàn)了eval… 于是放棄參考。

寫個(gè)小模塊開心一下

通過閱讀以上項(xiàng)目的源碼,其實(shí)主要是前三個(gè)娃圆,感覺可以完成自己想要的需求了。那么先寫一個(gè)小的來作為基礎(chǔ)模塊蛾茉。

export const mock = async(ctx: any) => {

? console.log('mock')

? console.log(ctx)

? console.log(ctx.params)

? const method = ctx.request.method.toLowerCase()

? // let { projectId, mockURL } = ctx.pathNode

? // 獲取接口路徑內(nèi)容

? console.log('ctx.pathNode', ctx.pathNode)

? // 匹配內(nèi)容是否一致

? console.log('驗(yàn)證內(nèi)容中...')

? // 模擬數(shù)據(jù)

? Mock.Handler.function = function (options: any) {

? ? console.log('start Handle')

? ? options.Mock = Mock

? ? // 傳入 request cookies讼呢,方便使用

? ? options._req = ctx.request

? ? return options.template.call(options.context.currentContext, options)

? }

? console.log('Mock.Handler', Mock.Handler.function)

//? const testMode = `{

//? ? 'title': 'Syntax Demo',

//? ? 'string1|1-10': '★',

//? ? 'string2|3': 'value',

//? ? 'number1|+1': 100,

//? ? 'number2|1-100': 100,

//? ? 'number3|1-100.1-10': 1,

//? ? 'number4|123.1-10': 1,

//? ? 'number5|123.3': 1,

//? ? 'number6|123.10': 1.123,

//? ? 'boolean1|1': true,

//? ? 'boolean2|1-2': true,

//? ? 'object1|2-4': {

//? ? ? ? '110000': '北京市',

//? ? ? ? '120000': '天津市',

//? ? ? ? '130000': '河北省',

//? ? ? ? '140000': '山西省'

//? ? },

//? ? 'object2|2': {

//? ? ? ? '310000': '上海市',

//? ? ? ? '320000': '江蘇省',

//? ? ? ? '330000': '浙江省',

//? ? ? ? '340000': '安徽省'

//? ? },

//? ? 'array1|1': ['AMD', 'CMD', 'KMD', 'UMD'],

//? ? 'array2|1-10': ['Mock.js'],

//? ? 'array3|3': ['Mock.js'],

//? ? 'function': function() {

//? ? ? ? return this.title

//? ? }

// }`

const testMode = `{success :true, data: { default: "hah", _req: function({ _req }) { return _req }, name: function({ _req }) { return _req.query.name || this.default }}}`

? const vm = new VM({

? ? timeout: 1000,

? ? sandbox: {

? ? ? Mock: Mock,

? ? ? mode: testMode,

? ? ? template: new Function(`return ${testMode}`)

? ? }

? })

? vm.run('Mock.mock(new Function("return " + mode)())') // 數(shù)據(jù)驗(yàn)證,檢測 setTimeout 等方法, 順便將內(nèi)部的函數(shù)執(zhí)行了

? // console.log(Mock.Handler.function(new Function('return ' + testMode)()))

? const apiData = vm.run('Mock.mock(template())')

? console.log('apiData2333' , apiData)

? let result

? switch (method) {

? ? case 'get':

? ? ? result = success({'msg': '你調(diào)用了get方法'})

? ? ? break;

? ? case 'post':

? ? ? result = success({'msg': '你調(diào)用了post方法'})

? ? ? break;

? ? case 'put' :

? ? ? result = success({'msg': '你調(diào)用了put方法'})

? ? ? break;

? ? case 'patch' :

? ? ? result = success({'msg': '你調(diào)用了patch方法'})

? ? ? break;

? ? case 'delete' :

? ? ? result = success({'msg': '你調(diào)用了delete方法'})

? ? ? break;

? ? default:

? ? ? result = error()

? }

? // console.log(result)

? return ctx.body = result

}

這里調(diào)試的遇到一些問題谦炬,主要是一開始測試的時(shí)候發(fā)現(xiàn) Mock 只將規(guī)則的數(shù)據(jù)模擬出悦屏,發(fā)現(xiàn) function 類型的函數(shù)都沒執(zhí)行,一開始定位以為是Mock.Handler.function?在 ts 中未執(zhí)行键思。于是在里面寫了一個(gè)輸出础爬,發(fā)現(xiàn)的確沒有。經(jīng)過各種猜想和測試吼鳞,發(fā)現(xiàn)是模擬mode有問題看蚜。

一開始我是這么寫的

const testcode = {

? ? 'array1|1': ['AMD', 'CMD', 'KMD', 'UMD'],

? ? 'array2|1-10': ['Mock.js'],

? ? 'array3|3': ['Mock.js'],

? ? 'function': function() {

? ? ? ? return this.title

? ? }

}

事實(shí)上應(yīng)該這么寫

const testcode = `{

? ? 'array1|1': ['AMD', 'CMD', 'KMD', 'UMD'],

? ? 'array2|1-10': ['Mock.js'],

? ? 'array3|3': ['Mock.js'],

? ? 'function': function() {

? ? ? ? return this.title

? ? }

}`

參照?easy-mock?的思路可以實(shí)現(xiàn)一個(gè)基礎(chǔ)的 Mock數(shù)據(jù)解析器,而且可以根據(jù) koa 的特性同時(shí)支持 _req 的一些參數(shù)赔桌,這里先不加進(jìn)去供炎。

如何支持自定義的數(shù)據(jù)模型也有了基本的思路渴逻,在之前沒有考慮 redis 情況下還是用傳統(tǒng)的數(shù)據(jù)庫查詢。具體實(shí)現(xiàn)等后期再搗鼓出來再寫出來音诫。

結(jié)尾

通過這兩天的學(xué)習(xí)惨奕,總算把一個(gè)Mock的核心模塊該如何實(shí)現(xiàn)的思路給理順了。

其實(shí)無論你是用戶自定義數(shù)據(jù),比如

{

? 'user': User, // User是用戶自定義的數(shù)據(jù)類型

? 'string2|3': 'value',

? 'number1|+1': 100,

? ? _req: function({

? ? ? _req

? ? }) {

? ? ? return _req

? ? },

? ? name: function({

? ? ? _req

? ? }) {

? ? ? return _req.query.name || this.default

? ? }

}

還是 Mock.js 原生的語法竭钝,你最終轉(zhuǎn)換過來需要執(zhí)行的是一樣的內(nèi)容梨撞,無非是在其轉(zhuǎn)換前需要做一定的處理。只有搞懂了基本的數(shù)據(jù)模擬實(shí)現(xiàn)蜓氨,基本上你可以將各個(gè)參數(shù)都做定制化聋袋。比如有的平臺(tái)會(huì)將用戶自己編寫的函數(shù)一起和 json 拼接。其實(shí)用的最終核心思路還是一樣的穴吹。

參考資料

Mock.js使用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末幽勒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子港令,更是在濱河造成了極大的恐慌啥容,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顷霹,死亡現(xiàn)場離奇詭異咪惠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)淋淀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門遥昧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朵纷,你說我怎么就攤上這事炭臭。” “怎么了袍辞?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵鞋仍,是天一觀的道長。 經(jīng)常有香客問我搅吁,道長威创,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任谎懦,我火速辦了婚禮肚豺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘界拦。我一直安慰自己详炬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呛谜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪枪萄。 梳的紋絲不亂的頭發(fā)上隐岛,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音瓷翻,去河邊找鬼聚凹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛齐帚,可吹牛的內(nèi)容都是我干的妒牙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼对妄,長吁一口氣:“原來是場噩夢啊……” “哼湘今!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起剪菱,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤摩瞎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后孝常,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旗们,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年构灸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了上渴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喜颁,死狀恐怖稠氮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洛巢,我是刑警寧澤括袒,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站稿茉,受9級(jí)特大地震影響锹锰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜漓库,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一恃慧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渺蒿,春花似錦痢士、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽善延。三九已至,卻和暖如春城侧,著一層夾襖步出監(jiān)牢的瞬間易遣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來泰國打工嫌佑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豆茫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓屋摇,卻偏偏與公主長得像揩魂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子炮温,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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