XMNetworking 網(wǎng)絡(luò)庫的設(shè)計(jì)與使用

XMNetwoking 是我們團(tuán)隊(duì)開源的一個(gè)網(wǎng)絡(luò)庫,詳見:GitHub

XMNetworking 是一個(gè)輕量的搏屑、簡(jiǎn)單易用但功能強(qiáng)大的網(wǎng)絡(luò)庫,基于 AFNetworking 3.0 封裝。
其中糠雨,XM 前綴是我們團(tuán)隊(duì) Xcode-Men 的縮寫。英文文檔

簡(jiǎn)介

XMNetworking.png

如上圖所示徘跪,XMNetworking 采用中心化的設(shè)計(jì)思想甘邀,由 XMCenter 統(tǒng)一發(fā)起并管理所有的 XMRequest 請(qǐng)求,并可通過 XMCenter 給所有請(qǐng)求配置回調(diào)線程垮庐、公共 Server URL松邪、Header、Parameter 等信息哨查,同時(shí)也可以 Block 注入的方式實(shí)現(xiàn)自定義的響應(yīng)結(jié)果處理邏輯逗抑,如數(shù)據(jù)模型轉(zhuǎn)換、業(yè)務(wù)錯(cuò)誤碼判斷寒亥、網(wǎng)絡(luò)緩存等邮府。另外增加了 XMEgine 這一層是為了隔離底層第三方庫依賴,便于以后切換其他底層網(wǎng)絡(luò)庫或自己實(shí)現(xiàn)底層邏輯溉奕。

特性

  • 簡(jiǎn)單易用挟纱,發(fā)送請(qǐng)求只需調(diào)用一個(gè)方法,通過 Block 配置信息腐宋,代碼緊湊紊服;
  • 功能強(qiáng)大,適用于幾乎所有的網(wǎng)絡(luò)請(qǐng)求使用場(chǎng)景(普通請(qǐng)求胸竞、上傳欺嗤、下載);
  • 專為 RESTful Server API 設(shè)計(jì)卫枝,并提供多種不同的請(qǐng)求和響應(yīng)的序列化類型煎饼;
  • 支持批量請(qǐng)求、鏈?zhǔn)秸?qǐng)求等復(fù)雜業(yè)務(wù)邏輯的網(wǎng)絡(luò)需求;
  • 可隨時(shí)取消未完成的網(wǎng)絡(luò)請(qǐng)求校赤,支持自動(dòng)重試失敗的請(qǐng)求吆玖;
  • 全局配置所有請(qǐng)求的公共信息筒溃,自定義回調(diào)線程以及響應(yīng)處理邏輯;
  • 支持檢查網(wǎng)絡(luò)連接類型沾乘,并集成 AFNetworking 強(qiáng)大的安全策略模塊怜奖。

系統(tǒng)要求

  • iOS 7.0 以上系統(tǒng)
  • Xcode 7.3 或更高版本

安裝說明

CocoaPods

在你工程的 Podfile 文件中添加如下一行,并執(zhí)行 pod installpod update翅阵。

pod 'XMNetworking'

注意: XMNetworking 已經(jīng)包含了 AFNetworking 3.1.0 的源代碼歪玲,所以你工程里的 Podfile 文件不能再添加 pod AFNetworking 去導(dǎo)入 AFNetworking,否則會(huì)有沖突掷匠!

Carthage (只支持 iOS 8+)

與 CocoaPods 不同的是滥崩,Carthage 是一個(gè)去中心化的第三方依賴庫管理工具,它自動(dòng)幫你編譯所依賴的第三方庫并以 framework 形式提供給你讹语。

你可以通過 Homebrew 執(zhí)行以下命令來安裝 Carthage:

$ brew update
$ brew install carthage

成功安裝完 Carthage 后钙皮,在你工程的 Cartfile 文件中添加如下一行:

github "kangzubin/XMNetworking"

然后執(zhí)行 carthage update --platform ios 命令生成 framework 包,并把生成的 XMNetworking.framework 拖拽到你的工程中顽决。

注意: XMNetworking 已經(jīng)包含了 AFNetworking 3.1.0 的源代碼短条,所以你無需通過 Carthage 生成 AFNetworking.framework 導(dǎo)到你工程中,否則會(huì)有沖突擎值!

手動(dòng)安裝

下載 XMNetworking 子文件夾的所有內(nèi)容慌烧,并把其中的源文件添加(拖放)到你的工程中。

使用教程

頭文件的導(dǎo)入

  • 如果是通過 CocoaPods 或 Carthage 安裝鸠儿,則:
#import <XMNetworking/XMNetworking.h>
  • 如果是手動(dòng)下載源碼安裝屹蚊,則:
#import "XMNetworking.h"

全局網(wǎng)絡(luò)配置

[XMCenter setupConfig:^(XMConfig *config) {
    config.generalServer = @"general server address";
    config.generalHeaders = @{@"general-header": @"general header value"};
    config.generalParameters = @{@"general-parameter": @"general parameter value"};
    config.generalUserInfo = nil;
    config.callbackQueue = dispatch_get_main_queue();
#ifdef DEBUG
    config.consoleLog = YES;
#endif
}];

你可以調(diào)用 XMCenter+setupConfig: 類方法,通過修改傳入的 XMConfig 對(duì)象來配置全局網(wǎng)絡(luò)請(qǐng)求的公共信息进每,包括如下:

  • generalServer: 公共服務(wù)端地址汹粤,如果一個(gè) XMRequest 請(qǐng)求對(duì)象的 server 屬性為 nil,且其 useGeneralServerYES(默認(rèn))田晚,那么該請(qǐng)求的服務(wù)端地址 server 將會(huì)取 XMCenter 中 generalServer 的值嘱兼。
  • generalParameters: 公共請(qǐng)求參數(shù),如果一個(gè) XMRequest 請(qǐng)求對(duì)象的 useGeneralParameters 屬性為 YES(默認(rèn))贤徒,并且 XMCenter 的公共參數(shù) generalParameters 不為空芹壕,那么這些公共參數(shù)會(huì)自動(dòng)加到該請(qǐng)求的 parameters 中。
  • generalHeaders: 公共請(qǐng)求頭接奈,如果一個(gè) XMRequest 請(qǐng)求對(duì)象的 useGeneralHeaders 屬性為 YES(默認(rèn))踢涌,并且 XMCenter 的公共請(qǐng)求頭 generalHeaders 不為空,那么這些公共請(qǐng)求頭會(huì)自動(dòng)加到該請(qǐng)求的 headers 中序宦。
  • generalUserInfo: 公共用戶信息睁壁,默認(rèn)為 nil,如果一個(gè) XMRequest 請(qǐng)求對(duì)象的 userInfo 屬性為 nil(默認(rèn))而該字段不為 nil,那么該字段會(huì)自動(dòng)賦值給 XMRequest 對(duì)象的 userInfo潘明。而 userInfo 屬性可用于區(qū)分具有相同上下文信息的不同請(qǐng)求行剂。
  • callbackQueue: 請(qǐng)求的回調(diào) Block 執(zhí)行的 dispatch 隊(duì)列(線程),如果為 NULL(默認(rèn))钳降,那么會(huì)在一個(gè)私有的并發(fā)隊(duì)列(子線程)中執(zhí)行回調(diào) Block厚宰。
  • consoleLog: 一個(gè) BOOL 值,用于表示是否在控制臺(tái)輸出請(qǐng)求和響應(yīng)的信息牲阁,默認(rèn)為 NO固阁。

另外壤躲,你可以通過調(diào)用 XMCenter 的以下兩個(gè)類方法來隨時(shí)修改全局公共的 header 和 parameter:

+ (void)setGeneralHeaderValue:(nullable NSString *)value forField:(NSString *)field;
+ (void)setGeneralParameterValue:(nullable NSString *)value forKey:(NSString *)key;

普通請(qǐng)求

GET

[XMCenter sendRequest:^(XMRequest *request) {
    request.url = @"http://example.com/v1/foo/bar";
    //request.server = @"http://example.com/v1/";
    //request.api = @"foo/bar";
    request.parameters = @{@"param1": @"value1", @"param2": @"value2"};
    request.headers = @{@"User-Agent": @"Custom User Agent"};
    request.httpMethod = kXMHTTPMethodGET;
} onSuccess:^(id responseObject) {
   NSLog(@"onSuccess: %@", responseObject);
} onFailure:^(NSError *error) {
   NSLog(@"onFailure: %@", error);
} onFinished:^(id responseObject, NSError *error) {
   NSLog(@"onFinished");
}];

注意1:可以通過以下兩種方法設(shè)置一個(gè)請(qǐng)求對(duì)象的 URL 地址城菊,但當(dāng) serverapiurl 三個(gè)屬性被同時(shí)賦值時(shí)碉克,url 的優(yōu)先級(jí)比較高凌唬,而此時(shí) serverapi 的值會(huì)被忽略漏麦。

request.url = @"http://example.com/v1/foo/bar";
// 如果 request.server 為 `nil`客税,且 request.useGeneralServer 為 `YES`,那么此時(shí) request.server 會(huì)取 XMCenter.generalServer 的值撕贞。
request.server = @"http://example.com/v1/";
request.api = @"foo/bar";

注意2:一個(gè)請(qǐng)求對(duì)象的回調(diào) Block (success/failure/finished/progress) 是非必需的(默認(rèn)為 nil)更耻,XMCenter 提供了多個(gè)設(shè)置不同回調(diào) Block 參數(shù)的方法用于發(fā)送請(qǐng)求。另外捏膨,需要注意的是秧均,success/faillure/finished 等回調(diào) Block 會(huì)在 XMCenter 設(shè)置的 callbackQueue 隊(duì)列中被執(zhí)行,但 progress 回調(diào) Block 將在 NSURLSession 自己的隊(duì)列中執(zhí)行号涯,而不是 callbackQueue目胡。

POST

[XMCenter sendRequest:^(XMRequest *request) {
    //request.server = @"http://example.com/v1/"; // 可選,如果為空則讀取 XMCenter.generalServer
    request.api = @"foo/bar";
    request.parameters = @{@"param1": @"value1", @"param2": @"value2"};
    request.httpMethod = kXMHTTPMethodPOST; // 可選链快,默認(rèn)為 `POST`
    request.requestType = kXMRequestNormal; // 可選誉己,默認(rèn)為 `Normal`
} onSuccess:^(id responseObject) {
   NSLog(@"onSuccess: %@", responseObject);
} onFailure:^(NSError *error) {
   NSLog(@"onFailure: %@", error);
}];

其他 HTTP 方法

XMRequest 同樣支持其他 HTTP 方法,比如:HEAD, DELETE, PUT, PATCH 等域蜗,使用方式與上述類似巨双,不再贅述。

詳見 XMConst霉祸、XMRequestXMCenter 等幾個(gè)文件中的代碼和注釋筑累。

上傳請(qǐng)求

// `NSData` form data.
UIImage *image = [UIImage imageNamed:@"testImage"];
NSData *fileData1 = UIImageJPEGRepresentation(image, 1.0);
// `NSURL` form data.
NSString *path = [NSHomeDirectory() stringByAppendingString:@"/Documents/testImage.png"];
NSURL *fileURL2 = [NSURL fileURLWithPath:path isDirectory:NO];

[XMCenter sendRequest:^(XMRequest *request) {
    request.server = @"http://example.com/v1/";
    request.api = @"foo/bar";
    request.requestType = kXMRequestUpload;
    [request addFormDataWithName:@"image[]" fileName:@"temp.jpg" mimeType:@"image/jpeg" fileData:fileData1];
    [request addFormDataWithName:@"image[]" fileURL:fileURL2];
    // see `XMUploadFormData` for more details.
} onProgress:^(NSProgress *progress) {
    // the progress block is running on the session queue.
    if (progress) {
        NSLog(@"onProgress: %f", progress.fractionCompleted);
    }
} onSuccess:^(id responseObject) {
    NSLog(@"onSuccess: %@", responseObject);
} onFailure:^(NSError *error) {
    NSLog(@"onFailure: %@", error);
} onFinished:^(id responseObject, NSError *error) {
    NSLog(@"onFinished");
}];

下載請(qǐng)求

[XMCenter sendRequest:^(XMRequest *request) {
    request.url = @"http://example.com/v1/testDownFile.zip";
    request.downloadSavePath = [NSHomeDirectory() stringByAppendingString:@"/Documents/"];
    request.requestType = kXMRequestDownload;
} onProgress:^(NSProgress *progress) {
    // the progress block is running on the session queue.
    if (progress) {
        NSLog(@"onProgress: %f", progress.fractionCompleted);
    }
} onSuccess:^(id responseObject) {
    NSLog(@"onSuccess: %@", responseObject);
} onFailure:^(NSError *error) {
    NSLog(@"onFailure: %@", error);
}];

序列化

XMRequest 中有兩個(gè)屬性 requestSerializerTyperesponseSerializerType 分別用于設(shè)置請(qǐng)求參數(shù)和響應(yīng)結(jié)果的序列化類型。

其中脉执,XMRequestSerializerTypeXMResponseSerializerType 枚舉的定義如下:

typedef NS_ENUM(NSInteger, XMRequestSerializerType) {
    kXMRequestSerializerRAW     = 0, // default
    kXMRequestSerializerJSON    = 1,
    kXMRequestSerializerPlist   = 2,
};
typedef NS_ENUM(NSInteger, XMResponseSerializerType) {
    kXMResponseSerializerRAW    = 0,
    kXMResponseSerializerJSON   = 1, // default
    kXMResponseSerializerPlist  = 2,
    kXMResponseSerializerXML    = 3,
};

詳見 AFURLRequestSerialization.hAFURLResponseSerialization.h 獲取更多細(xì)節(jié)疼阔。

自定義響應(yīng)結(jié)果的處理邏輯

通常地,一個(gè)請(qǐng)求成功結(jié)束時(shí),會(huì)執(zhí)行 success block婆廊,當(dāng)有錯(cuò)誤發(fā)生時(shí)迅细,執(zhí)行 failure block。然而淘邻,開發(fā)中更常見的情況是茵典,即使是一個(gè)請(qǐng)求成功結(jié)束,我們也需要進(jìn)一步處理宾舅,比如驗(yàn)證響應(yīng)結(jié)果數(shù)據(jù)统阿、判斷與服務(wù)端商量好的業(yè)務(wù)錯(cuò)誤碼類型等,再?zèng)Q定執(zhí)行 success block 還是 failure block筹我。

現(xiàn)在扶平,你可以調(diào)用 [XMCenter setResponseProcessBlock:...] 方法以 Block 注入的方式設(shè)置自定義的處理邏輯,當(dāng)請(qǐng)求成功結(jié)束時(shí)蔬蕊,這個(gè) Block 會(huì)在 success block 被執(zhí)行前調(diào)用结澄,如果傳入 *error 參數(shù)被賦值,則接下來會(huì)執(zhí)行 failure block岸夯。

[XMCenter setResponseProcessBlock:^(XMRequest *request, id responseObject, NSError *__autoreleasing *error) {
    // 自定義響應(yīng)結(jié)果處理邏輯麻献,如果 `*error` 被賦值,則接下來會(huì)執(zhí)行 failure block猜扮。
}];

批量請(qǐng)求

XMNetworking 支持同時(shí)發(fā)一組批量請(qǐng)求勉吻,這組請(qǐng)求在業(yè)務(wù)邏輯上相關(guān),但請(qǐng)求本身是互相獨(dú)立的旅赢,success block 會(huì)在所有請(qǐng)求都成功結(jié)束時(shí)才執(zhí)行齿桃,而一旦有一個(gè)請(qǐng)求失敗,則會(huì)執(zhí)行 failure block鲜漩。注:回調(diào) Block 中的 responseObjectserrors 中元素的順序與每個(gè) XMRequest 對(duì)象在 batchRequest.requestArray 中的順序一致源譬。

[XMCenter sendBatchRequest:^(XMBatchRequest *batchRequest) {
    XMRequest *request1 = [XMRequest request];
    request1.url = @"server url 1";
    // set other properties for request1
        
    XMRequest *request2 = [XMRequest request];
    request2.url = @"server url 2";
    // set other properties for request2
        
    [batchRequest.requestArray addObject:request1];
    [batchRequest.requestArray addObject:request2];
} onSuccess:^(NSArray<id> *responseObjects) {
    NSLog(@"onSuccess: %@", responseObjects);
} onFailure:^(NSArray<id> *errors) {
    NSLog(@"onFailure: %@", errors);
} onFinished:^(NSArray<id> *responseObjects, NSArray<id> *errors) {
    NSLog(@"onFinished");
}];

[XMCenter sendBatchRequest:...] 方法會(huì)返回剛發(fā)起的新的 XMBatchRequest 對(duì)象,你可以保存這個(gè)對(duì)象孕似,并在必要的時(shí)候調(diào)用它的 -cancelWithBlock: 方法取消這組批量請(qǐng)求踩娘。

鏈?zhǔn)秸?qǐng)求

XMNetworking 同樣支持發(fā)一組鏈?zhǔn)秸?qǐng)求,這組請(qǐng)求之間互相依賴喉祭,下一請(qǐng)求是否發(fā)送以及請(qǐng)求的參數(shù)取決于上一個(gè)請(qǐng)求的結(jié)果养渴,success block 會(huì)在所有的鏈?zhǔn)秸?qǐng)求都成功結(jié)束時(shí)才執(zhí)行,而中間一旦有一個(gè)請(qǐng)求失敗泛烙,則會(huì)執(zhí)行 failure block理卑。注:回調(diào) Block 中的 responseObjectserrors 中元素的順序與每個(gè)鏈?zhǔn)秸?qǐng)求 XMRequest 對(duì)象的先后順序一致。

[XMCenter sendChainRequest:^(XMChainRequest *chainRequest) {
    [[[[chainRequest onFirst:^(XMRequest *request) {
        request.url = @"server url 1";
        // set other properties for request
    }] onNext:^(XMRequest *request, id responseObject, BOOL *sendNext) {
        NSDictionary *params = responseObject;
        if (params.count > 0) {
            request.url = @"server url 2";
            request.parameters = params;
        } else {
            *sendNext = NO;
        }
    }] onNext:^(XMRequest *request, id responseObject, BOOL *sendNext) {
        request.url = @"server url 3";
        request.parameters = @{@"param1": @"value1", @"param2": @"value2"};
    }] onNext: ...];    
} onSuccess:^(NSArray<id> *responseObjects) {
    NSLog(@"onSuccess: %@", responseObjects);
} onFailure:^(NSArray<id> *errors) {
    NSLog(@"onFailure: %@", errors);
} onFinished:^(NSArray<id> *responseObjects, NSArray<id> *errors) {
    NSLog(@"onFinished");
}];

[XMCenter sendChainRequest:...] 方法會(huì)返回剛發(fā)起的新的 XMChainRequest 對(duì)象蔽氨,你可以保存這個(gè)對(duì)象藐唠,并在必要的時(shí)候調(diào)用它的 -cancelWithBlock: 方法取消這組鏈?zhǔn)秸?qǐng)求帆疟。

取消一個(gè)網(wǎng)絡(luò)請(qǐng)求

當(dāng)調(diào)用 [XMCenter sendRequest:...] 方法發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí),該方法會(huì)返回一個(gè)用于唯一標(biāo)識(shí)該請(qǐng)求對(duì)象的 identifier(如果請(qǐng)求發(fā)送失敗宇立,該值為 0)踪宠。在必要的時(shí)候,你可以通過這個(gè) identifier 來取消當(dāng)前網(wǎng)絡(luò)請(qǐng)求(如果一個(gè)請(qǐng)求已經(jīng)結(jié)束妈嘹,這時(shí)再用 identifier 來取消該請(qǐng)求時(shí)柳琢,會(huì)直接忽略)。

// send a request
NSUInteger identifier = [XMCenter sendRequest:^(XMRequest *request) {
    request.server = @"https://kangzubin.cn/";
    request.api = @"test/index.php";
    request.httpMethod = kXMHTTPMethodGET;
    request.timeoutInterval = 10;
    request.retryCount = 1;
} onFailure:^(NSError *error) {
    NSLog(@"onFailure: %@", error);
}];

// your business code
sleep(2);

// cancel the running request by identifier with cancel block
[XMCenter cancelRequest:identifier onCancel:^(XMRequest *request) {
    NSLog(@"onCancel");
}];

注意:調(diào)用 XMCenter cancelRequest:onCancel: 方法取消一個(gè)網(wǎng)絡(luò)請(qǐng)求時(shí)润脸,被取消的請(qǐng)求對(duì)象(如果存在)會(huì)以參數(shù)的形式傳給 cancel block柬脸,另外 cancel block 是在當(dāng)前調(diào)用 cancelRequest: 方法的線程中執(zhí)行,并不是 XMCenter 的 callbackQueue毙驯。

網(wǎng)絡(luò)可連接性檢查

我們提供了兩種方法用于獲取網(wǎng)絡(luò)的可連接性倒堕,分別如下:

[XMCenter isNetworkReachable];
// 該方法會(huì)返回一個(gè) Bool 值用于表示當(dāng)前網(wǎng)絡(luò)是否可連接。
[[XMEngine sharedEngine] networkReachability]; 
// 該方法會(huì)返回一個(gè)當(dāng)前網(wǎng)絡(luò)的狀態(tài)值尔苦,-1 表示 `Unknown`涩馆,0 表示 `NotReachable行施,1 表示 `WWAN`允坚,2 表示 `WiFi`  

詳見 AFNetworkReachabilityManager 獲取更多細(xì)節(jié).

HTTPS 請(qǐng)求的本地證書校驗(yàn)(SSL Pinning)

在你的應(yīng)用程序包里添加 (pinned) 相應(yīng)的 SSL 證書做校驗(yàn)有助于防止中間人攻擊和其他安全漏洞。非常方便的是蛾号,AFNetworking 的 AFSecurityPolicy 安全模塊可以通過校驗(yàn)本地保存的證書或公鑰幫助我們?cè)u(píng)估服務(wù)器是否可信任以及建立安全連接稠项。

我們?cè)?XMEngine 中暴露了一個(gè) AFHTTPSessionManager 對(duì)象叫 sessionManager,你可以通過修改該對(duì)象的 securityPolicy 類型鲜结,以開啟 SSL Pinning 功能展运,并把你們服務(wù)器對(duì)應(yīng)的 .cer 證書或者公鑰放到你的工程中。

[XMEngine sharedEngine].sessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

詳見 AFSecurityPolicy 獲取更多細(xì)節(jié).

文檔

詳見 XMNetworking Documents Link.

結(jié)構(gòu)

XMNetworking 的代碼結(jié)構(gòu)非常簡(jiǎn)潔和緊湊精刷,只包含了 4 個(gè)核心文件:XMConst.h 用于定義全局常量枚舉和 Block拗胜,XMRequestXMCenterXMEngine 則是核心類的聲明和實(shí)現(xiàn)怒允,具體的代碼結(jié)構(gòu)如下圖所示:

Architecture.png

待完善

  • 支持?jǐn)帱c(diǎn)下載
  • 支持網(wǎng)絡(luò)層緩存
  • 兼容測(cè)試支持 tvOS/watchOS/OS X
  • 更加強(qiáng)大的自定義模型轉(zhuǎn)換
  • 實(shí)現(xiàn)一套可擴(kuò)展的插件機(jī)制埂软,便于 XMNetworking 增加新功能

作者

貢獻(xiàn)者

許可證

XMNetworking 使用 MIT 許可證,詳情見 LICENSE 文件纫事。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末勘畔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丽惶,更是在濱河造成了極大的恐慌炫七,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钾唬,死亡現(xiàn)場(chǎng)離奇詭異万哪,居然都是意外死亡侠驯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門奕巍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陵霉,“玉大人,你說我怎么就攤上這事伍绳∮荒樱” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵冲杀,是天一觀的道長(zhǎng)效床。 經(jīng)常有香客問我,道長(zhǎng)权谁,這世上最難降的妖魔是什么剩檀? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮旺芽,結(jié)果婚禮上沪猴,老公的妹妹穿的比我還像新娘。我一直安慰自己采章,他們只是感情好运嗜,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悯舟,像睡著了一般担租。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抵怎,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天奋救,我揣著相機(jī)與錄音,去河邊找鬼反惕。 笑死尝艘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姿染。 我是一名探鬼主播背亥,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盔粹!你這毒婦竟也來了隘梨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤舷嗡,失蹤者是張志新(化名)和其女友劉穎轴猎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體进萄,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捻脖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年锐峭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片可婶。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡沿癞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出矛渴,到底是詐尸還是另有隱情椎扬,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布具温,位于F島的核電站蚕涤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏铣猩。R本人自食惡果不足惜揖铜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望达皿。 院中可真熱鬧天吓,春花似錦、人聲如沸峦椰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽们何。三九已至萄焦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冤竹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工茬射, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鹦蠕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓在抛,卻偏偏與公主長(zhǎng)得像钟病,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刚梭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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