AFNetworking3.0源碼解析2(請(qǐng)求序列化)

上篇 分析了接口層的兩個(gè)類AFHttpSessionManager && AFURLSessionManager,他們的主要負(fù)責(zé)對(duì)外暴露接口供框架的使用者調(diào)用,具體的構(gòu)造請(qǐng)求和解析響應(yīng)頭則放在了序列化這塊。本篇主要講解請(qǐng)求的和響應(yīng)的序列化枚抵。

http基礎(chǔ)姿勢(shì)

1. http常用方法

http常用的請(qǐng)求方法有:get,post,head欺嗤,put,delete卫枝。其中post和put請(qǐng)求會(huì)將參數(shù)轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)后然后放入body中煎饼,get,head和delete方法則會(huì)將參數(shù)直接拼接在url中作為查詢參數(shù)校赤。比如對(duì)于接口https://xxx.com/v1/userinfo吆玖,有兩個(gè)參數(shù)userName和password,那么get方法和post方法最終的請(qǐng)求是:url:https://xxx.com/v1/userinfo?userName='terry'&password='123' 和url:https://xxx.com/v1/userinfo httpbody:userName='terry'&password='123'马篮。對(duì)于post方法沾乘,如果除了參數(shù)以外還有其他數(shù)據(jù),比如上傳一張圖片浑测,那個(gè)圖片數(shù)據(jù)將會(huì)和參數(shù)數(shù)據(jù)一樣轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)后寫入httpbody里翅阵。

2. http請(qǐng)求的上傳多個(gè)文件具體原理

如果一個(gè)http請(qǐng)求要上傳多個(gè)文件歪玲,那么首先要告訴服務(wù)器要做什么,多個(gè)文件之間怎么分隔的掷匠,文件大小多大等滥崩。這部分參考了這篇博文HTTP協(xié)議下實(shí)現(xiàn)上傳文件。至于http頭部的其他信息暫時(shí)忽略讹语,重點(diǎn)介紹幾個(gè)頭部:
Content-Type:表示數(shù)據(jù)類型钙皮。如果是文件上傳必須是multipart/form-data; boundary=--xxxx 這些協(xié)議規(guī)定的,后面表示多文件之間的分隔符顽决。
Content-Length:表示總的數(shù)據(jù)包大小短条,包括分隔符以及前后換行+正文數(shù)據(jù)。

3. http請(qǐng)求的緩存處理

有時(shí)候?qū)τ谝恍┎怀W兊臄?shù)據(jù)才菠,服務(wù)端為了節(jié)省帶寬或者資源會(huì)讓客戶端使用上次請(qǐng)求數(shù)據(jù)(也就是使用緩存)茸时。會(huì)返回304狀態(tài)碼(Not Modified),需要服務(wù)端在http的響應(yīng)里寫入一些數(shù)據(jù)last-modify鸠儿,etag等信息屹蚊,不然客戶端怎么知道要不要緩存請(qǐng)求,而且怎么判斷請(qǐng)求的資源有沒(méi)有發(fā)生變化进每⌒谠粒客戶端拿到這個(gè)狀態(tài)碼之后就不會(huì)去讀取當(dāng)前響應(yīng)的body(body里其實(shí)也木有內(nèi)容),而是取上次緩存的響應(yīng)頭田晚。
知道這些就夠了嘱兼。下面進(jìn)入正題:

序列化

1. 請(qǐng)求序列化(直白的說(shuō)就是把請(qǐng)求里各種參數(shù),文件數(shù)據(jù)轉(zhuǎn)換成二進(jìn)制數(shù)據(jù))

在請(qǐng)求序列化的過(guò)程中主要涉及類和各功能如下圖:

AFNetworking請(qǐng)求序列化相關(guān)類.png

先看下請(qǐng)求序列化類的基類頭文件

@interface AFHTTPRequestSerializer : NSObject <AFURLRequestSerialization>

/**
 The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default.
 */
@property (nonatomic, assign) NSStringEncoding stringEncoding;

/**
 Whether created requests can use the device’s cellular radio (if present). `YES` by default.

 @see NSMutableURLRequest -setAllowsCellularAccess:
 */
@property (nonatomic, assign) BOOL allowsCellularAccess;

/**
 The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default.

 @see NSMutableURLRequest -setCachePolicy:
 */
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;

/**
 Whether created requests should use the default cookie handling. `YES` by default.

 @see NSMutableURLRequest -setHTTPShouldHandleCookies:
 */
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;

/**
 Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default

 @see NSMutableURLRequest -setHTTPShouldUsePipelining:
 */
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;

/**
 The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default.

 @see NSMutableURLRequest -setNetworkServiceType:
 */
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;

/**
 The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds.

 @see NSMutableURLRequest -setTimeoutInterval:
 */
@property (nonatomic, assign) NSTimeInterval timeoutInterval;

///---------------------------------------
/// @name Configuring HTTP Request Headers
///---------------------------------------

/**
 Default HTTP header field values to be applied to serialized requests. By default, these include the following:

 - `Accept-Language` with the contents of `NSLocale +preferredLanguages`
 - `User-Agent` with the contents of various bundle identifiers and OS designations

 @discussion To add or remove default request headers, use `setValue:forHTTPHeaderField:`.
 */
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;

/**
 Creates and returns a serializer with default configuration.
 */
+ (instancetype)serializer;

/**
 Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header.

 @param field The HTTP header to set a default value for
 @param value The value set as default for the specified header, or `nil`
 */
- (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field;

/**
 Returns the value for the HTTP headers set in the request serializer.

 @param field The HTTP header to retrieve the default value for

 @return The value set as default for the specified header, or `nil`
 */
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;

/**
 Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header.

 @param username The HTTP basic auth username
 @param password The HTTP basic auth password
 */
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password;

/**
 Clears any existing value for the "Authorization" HTTP header.
 */
- (void)clearAuthorizationHeader;

///-------------------------------------------------------
/// @name Configuring Query String Parameter Serialization
///-------------------------------------------------------

/**
 HTTP methods for which serialized requests will encode parameters as a query string. `GET`, `HEAD`, and `DELETE` by default.
 */
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;

/**
 Set the method of query string serialization according to one of the pre-defined styles.

 @param style The serialization style.

 @see AFHTTPRequestQueryStringSerializationStyle
 */
- (void)setQueryStringSerializationWithStyle:(AFHTTPRequestQueryStringSerializationStyle)style;

/**
 Set the a custom method of query string serialization according to the specified block.

 @param block A block that defines a process of encoding parameters into a query string. This block returns the query string and takes three arguments: the request, the parameters to encode, and the error that occurred when attempting to encode parameters for the given request.
 */
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;

///-------------------------------
/// @name Creating Request Objects
///-------------------------------

/**
 Creates an `NSMutableURLRequest` object with the specified HTTP method and URL string.

 If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body.

 @param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`.
 @param URLString The URL string used to create the request URL.
 @param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body.
 @param error The error that occurred while constructing the request.

 @return An `NSMutableURLRequest` object.
 */
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(nullable id)parameters
                                     error:(NSError * _Nullable __autoreleasing *)error;

/**
 Creates an `NSMutableURLRequest` object with the specified HTTP method and URLString, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2

 Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream.

 @param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`.
 @param URLString The URL string used to create the request URL.
 @param parameters The parameters to be encoded and set in the request HTTP body.
 @param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol.
 @param error The error that occurred while constructing the request.

 @return An `NSMutableURLRequest` object
 */
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(nullable NSDictionary <NSString *, id> *)parameters
                              constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                                                  error:(NSError * _Nullable __autoreleasing *)error;

/**
 Creates an `NSMutableURLRequest` by removing the `HTTPBodyStream` from a request, and asynchronously writing its contents into the specified file, invoking the completion handler when finished.

 @param request The multipart form request. The `HTTPBodyStream` property of `request` must not be `nil`.
 @param fileURL The file URL to write multipart form contents to.
 @param handler A handler block to execute.

 @discussion There is a bug in `NSURLSessionTask` that causes requests to not send a `Content-Length` header when streaming contents from an HTTP body, which is notably problematic when interacting with the Amazon S3 webservice. As a workaround, this method takes a request constructed with `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:error:`, or any other request with an `HTTPBodyStream`, writes the contents to the specified file and returns a copy of the original request with the `HTTPBodyStream` property set to `nil`. From here, the file can either be passed to `AFURLSessionManager -uploadTaskWithRequest:fromFile:progress:completionHandler:`, or have its contents read into an `NSData` that's assigned to the `HTTPBody` property of the request.

 @see https://github.com/AFNetworking/AFNetworking/issues/1398
 */
- (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
                             writingStreamContentsToFile:(NSURL *)fileURL
                                       completionHandler:(nullable void (^)(NSError * _Nullable error))handler;

@end

stringEncoding:對(duì)于參數(shù)使用的編碼贤徒,默認(rèn)是NSUTF8StringEncoding
allowsCellularAccess:是否允許使用蜂窩網(wǎng)絡(luò)芹壕,默認(rèn)允許
cachePolicy:緩存策略。默認(rèn)NSURLRequestUseProtocolCachePolicy接奈,也就是協(xié)議的緩存策略踢涌,至于協(xié)議怎么實(shí)現(xiàn)了就是iOS系統(tǒng)的事了。在實(shí)際測(cè)試過(guò)程中序宦,iOS是遵循標(biāo)準(zhǔn)http緩存協(xié)議的睁壁,也就是對(duì)304狀態(tài)碼可以正確處理。
HTTPShouldHandleCookies:是否處理cookie互捌,默認(rèn)YES潘明,具體怎么處理iOS系統(tǒng)自己的事了。
HTTPShouldUsePipelining:是否使用http管道技術(shù)秕噪,默認(rèn)不使用钳降。也就是在同一個(gè)連接上,并行多個(gè)請(qǐng)求腌巾,服務(wù)端和客戶端依然可以正常工作遂填。需要客戶端和服務(wù)端同時(shí)支持铲觉。具體的http管道技術(shù)可以參考博文HTTP的長(zhǎng)連接和短連接的第六部分。
HTTPRequestHeaders:http頭部字段

  - (NSMutableURLRequest *)requestWithMethod:(NSString *)methodURLString:(NSString *)URLStringparameters:(nullable    id)parameterserror:(NSError * _Nullable __autoreleasing *)error

這個(gè)方法用于構(gòu)造get城菊,head或者delete請(qǐng)求备燃,他們的參數(shù)將放在url中以key=value的方式編碼。

  -(NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)methodURLString:(NSString *)URLStringparameters:(nullable NSDictionary <NSString *, id> *)parametersconstructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))blockerror:(NSError * _Nullable __autoreleasing *)error

這個(gè)方法用于構(gòu)造post或者put請(qǐng)求凌唬,第四個(gè)參數(shù)用于在上傳文件的時(shí)候?qū)?shù)據(jù)拼接在formData里,可以拼接多個(gè)文件數(shù)據(jù)漏麦。

  -(NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)requestwritingStreamContentsToFile:(NSURL *)fileURLcompletionHandler:(nullable void (^)(NSError * _Nullable error))handler;

這個(gè)方法和上面的方法類似客税,只是多了一個(gè)將流內(nèi)容異步寫入指定文件的功能。
對(duì)于請(qǐng)求的序列化比較復(fù)雜的情況是帶參數(shù)撕贞,帶文件的post上傳請(qǐng)求更耻。下面主要跟著這個(gè)方法

  -(NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)methodURLString:(NSString *)URLStringparameters:(nullable NSDictionary <NSString *, id> *)parametersconstructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))blockerror:(NSError * _Nullable __autoreleasing *)error

它的實(shí)現(xiàn)如下:

- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
                                              URLString:(NSString *)URLString
                                             parameters:(NSDictionary *)parameters
                              constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                                                  error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);//請(qǐng)求方法不能為為GET或者HEAD

    NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];//構(gòu)造一個(gè)不帶參數(shù)的請(qǐng)求對(duì)象

    __block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];//http最終上傳的數(shù)據(jù)包括參數(shù),以及要上傳的文件數(shù)據(jù)都放這里捏膨,同時(shí)會(huì)持有這個(gè)request對(duì)象

    if (parameters) {//如果參數(shù)不空則將參數(shù)放入以key=value的方式放入formdata的bodyparts數(shù)組中
        for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
            NSData *data = nil;
            if ([pair.value isKindOfClass:[NSData class]]) {
                data = pair.value;
            } else if ([pair.value isEqual:[NSNull null]]) {
                data = [NSData data];
            } else {
                data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
            }

            if (data) {
                [formData appendPartWithFormData:data name:[pair.field description]];
            }
        }
    }

    if (block) {//執(zhí)行調(diào)用方上傳文件block秧均,也是通過(guò)調(diào)用類似 [formData appendPartWithFormData:data name:[pair.field description]];這樣的接口放入formdata的bodyparts數(shù)組中,這步完后我們要上傳的參數(shù)以及文件數(shù)據(jù)都構(gòu)造完成号涯。
        block(formData);
    }

    return [formData requestByFinalizingMultipartFormData];//設(shè)置request的httpbodyStream 設(shè)置為formData中的輸入流目胡。同時(shí)設(shè)置一下頭部信息比如Content-Length,Content-Type等链快。
}

下面來(lái)看看怎么將輸入formData(AFStreamingMultipartFormData)的數(shù)據(jù)寫入到文件流的誉己。先看下

AFStreamingMultipartFormData類.png

以及


AFMultipartBodyStream類.png

AFMultipartBodyStream輸入流類定義了怎么讀取數(shù)據(jù)。
在看具體怎么讀取數(shù)據(jù)之前域蜗,先了解下iOS的輸入流(NSInputStream)巨双。iOS關(guān)于NSInputStream說(shuō)明

NSInputStream is an abstract superclass of a class cluster consisting of concrete subclasses of NSStream that provide standard read-only access to stream data. Although NSInputStream is probably sufficient for most situations requiring access to stream data, you can create a subclass of NSInputStream if you want more specialized behavior (for example, you want to record statistics on the data in a stream).
Methods to Override
To create a subclass of NSInputStream you may have to implement initializers for the type of stream data supported and suitably re-implement existing initializers. You must also provide complete implementations of the following methods:
read:maxLength: From the current read index, take up to the number of bytes specified in the second parameter from the stream and place them in the client-supplied buffer (first parameter). The buffer must be of the size specified by the second parameter. Return the actual number of bytes placed in the buffer; if there is nothing left in the stream, return 0. Reset the index into the stream for the next read operation.
getBuffer:length: Return in 0(1) a pointer to the subclass-allocated buffer (first parameter). Return by reference in the second parameter the number of bytes actually put into the buffer. The buffer’s contents are valid only until the next stream operation. Return NO if you cannot access data in the buffer; otherwise, return YES. If this method is not appropriate for your type of stream, you may return NO.
hasBytesAvailable Return YES if there is more data to read in the stream, NO if there is not. If you want to be semantically compatible with NSInputStream, return YES if a read must be attempted to determine if bytes are available.

那既然是AFMultipartBodyStream顯然也實(shí)現(xiàn)了這幾個(gè)方法,在AF里的實(shí)現(xiàn)代碼如下:

AFMultipartBodyStream的接口實(shí)現(xiàn).png

可以看到最終的序列化是調(diào)用AFHTTPBodyPart的read:maxLength方法把一個(gè)個(gè)AFHTTPBodyPart序列化的霉祸。那再進(jìn)一步跟蹤下AFHTTPBodyPart又是怎么把數(shù)據(jù)單元序列化的筑累。

AFHTTPBodyPart寫數(shù)據(jù)到緩沖.png

這個(gè)地方可能會(huì)有疑問(wèn)是怎么將上圖中的文件數(shù)據(jù)寫入緩沖中的也就是下面這句:

numberOfBytesRead = [self.inputStream read:&buffer[totalNumberOfBytesRead] maxLength:(length - (NSUInteger)totalNumberOfBytesRead)];

可以看下AFHTTPBodyPart 構(gòu)造過(guò)程:

AFHTTPBodyPart構(gòu)造.png

再看看AFHTTPBodyPart里的InpustStream里生成:

AFHTTPBodyPart的InpustStream.png

就是把body數(shù)據(jù)讀入,其實(shí)這里作者也給我們演示了NSInputStream的兩種使用方法丝蹭。
說(shuō)到這里基本上整個(gè)請(qǐng)求序列化就說(shuō)完了慢宗,至于AFJSONRequestSerializer和AFPropertyListRequestSerializer就是把http頭部字段取不同值而已Content-Type。說(shuō)了這么多半夷,還是建議好好看著源碼在體會(huì)下比較婆廊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市巫橄,隨后出現(xiàn)的幾起案子淘邻,更是在濱河造成了極大的恐慌,老刑警劉巖湘换,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宾舅,死亡現(xiàn)場(chǎng)離奇詭異统阿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)筹我,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門扶平,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蔬蕊,你說(shuō)我怎么就攤上這事结澄。” “怎么了岸夯?”我有些...
    開(kāi)封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵麻献,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我猜扮,道長(zhǎng)勉吻,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任旅赢,我火速辦了婚禮齿桃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘煮盼。我一直安慰自己短纵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布孕似。 她就那樣靜靜地躺著踩娘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪喉祭。 梳的紋絲不亂的頭發(fā)上养渴,一...
    開(kāi)封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音泛烙,去河邊找鬼理卑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蔽氨,可吹牛的內(nèi)容都是我干的藐唠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鹉究,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宇立!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起自赔,我...
    開(kāi)封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妈嘹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后绍妨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體润脸,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柬脸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了毙驯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倒堕。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖爆价,靈堂內(nèi)的尸體忽然破棺而出垦巴,到底是詐尸還是另有隱情,我是刑警寧澤允坚,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布魂那,位于F島的核電站,受9級(jí)特大地震影響稠项,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲜结,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一展运、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧精刷,春花似錦拗胜、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至纫事,卻和暖如春勘畔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背丽惶。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工炫七, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钾唬。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓万哪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親抡秆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奕巍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

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