Swift之Moya使用和封裝

Moya是什么偿衰?

Moya是對(duì)請(qǐng)求庫(kù)Alamofire的抽象封裝低匙,相當(dāng)于OC中YTKNetwork和AFNetworking的關(guān)系一汽。

為什么用Moya避消?

我們用Moya在Github上的一張圖來(lái)解釋。

image.png

TargetType

TargetType這個(gè)是使用moya必須要實(shí)現(xiàn)的一個(gè)協(xié)議召夹,先看一下它有哪些定義

/// The protocol used to define the specifications necessary for a `MoyaProvider`.
public protocol TargetType {

    /// The target's base `URL`.
    var baseURL: URL { get }

    /// The path to be appended to `baseURL` to form the full `URL`.
    var path: String { get }

    /// The HTTP method used in the request.
    var method: Moya.Method { get }

    /// Provides stub data for use in testing. Default is `Data()`.
    var sampleData: Data { get }

    /// The type of HTTP task to be performed.
    var task: Task { get }

    /// The type of validation to perform on the request. Default is `.none`.
    var validationType: ValidationType { get }

    /// The headers to be used in the request.
    var headers: [String: String]? { get }
}

看完之后我們來(lái)定義一個(gè)結(jié)構(gòu)體岩喷,遵守TargetType協(xié)議

import Foundation
import Moya

// NetworkAPI就是一個(gè)遵循TargetType協(xié)議的枚舉
enum NetworkAPI {
    //測(cè)試天氣
    case realtimeWeather(cityId:String)
    
}

extension NetworkAPI: TargetType{
    var baseURL: URL {
        return URL(string: "https://go.apipost.cn/")!
    }
    
    var path: String {
        switch self {
        case .realtimeWeather:
            return "?Query=test"
            
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .realtimeWeather:
            return .post
        }
    }
    
    var task: Moya.Task {
        // 公共參數(shù)
        var params: [String: Any] = [:]
//        params["token"] = "Gz1qYLXeBW8MZuUfDlr9wsAYuVS1cZFMJY9BbaF842L2gRps747o4w=="
        
        switch self {
        case .realtimeWeather(let cityId):
            params["cityId"] = cityId
        }
        return .requestParameters(parameters: params, encoding: JSONEncoding.default)
    }
    
    var headers: [String : String]? {
        var headers: [String: String] = [:]
        
        switch self {
        case .realtimeWeather:
            headers["Content-type"] = "application/json;charset=utf-8"
        }
        
        return headers
    }
    
}

這里只說(shuō)一下task,它是一個(gè)枚舉监憎,這里看一下moya內(nèi)部定義纱意,我們可以根據(jù)需要進(jìn)行創(chuàng)建

/// Represents an HTTP task.
public enum Task {

    /// A request with no additional data.
    case requestPlain

    /// A requests body set with data.
    case requestData(Data)

    /// A request body set with `Encodable` type
    case requestJSONEncodable(Encodable)

    /// A request body set with `Encodable` type and custom encoder
    case requestCustomJSONEncodable(Encodable, encoder: JSONEncoder)

    /// A requests body set with encoded parameters.
    case requestParameters(parameters: [String: Any], encoding: ParameterEncoding)

    /// A requests body set with data, combined with url parameters.
    case requestCompositeData(bodyData: Data, urlParameters: [String: Any])

    /// A requests body set with encoded parameters combined with url parameters.
    case requestCompositeParameters(bodyParameters: [String: Any], bodyEncoding: ParameterEncoding, urlParameters: [String: Any])

    /// A file upload task.
    case uploadFile(URL)

    /// A "multipart/form-data" upload task.
    case uploadMultipart([MultipartFormData])

    /// A "multipart/form-data" upload task  combined with url parameters.
    case uploadCompositeMultipart([MultipartFormData], urlParameters: [String: Any])

    /// A file download task to a destination.
    case downloadDestination(DownloadDestination)

    /// A file download task to a destination with extra parameters using the given encoding.
    case downloadParameters(parameters: [String: Any], encoding: ParameterEncoding, destination: DownloadDestination)
}

MoyaProvider

我們對(duì)網(wǎng)絡(luò)的請(qǐng)求基本上就是MoyaProvider來(lái)調(diào)用了,我們先看一下它的定義鲸阔,具體的參數(shù)什么意思我們都可以在這個(gè)類中找到對(duì)應(yīng)的參數(shù)說(shuō)明偷霉。

    /// Initializes a provider.
    public init(endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,
                requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping,
                stubClosure: @escaping StubClosure = MoyaProvider.neverStub,
                callbackQueue: DispatchQueue? = nil,
                session: Session = MoyaProvider<Target>.defaultAlamofireSession(),
                plugins: [PluginType] = [],
                trackInflights: Bool = false) {

        self.endpointClosure = endpointClosure
        self.requestClosure = requestClosure
        self.stubClosure = stubClosure
        self.session = session
        self.plugins = plugins
        self.trackInflights = trackInflights
        self.callbackQueue = callbackQueue
    }

Moya的使用

接下來(lái)我們就可以使用最簡(jiǎn)單的調(diào)用方法:

        let networkProvider = MoyaProvider<NetworkAPI>()
        let target = NetworkAPI.realtimeWeather(cityId: "123")
        networkProvider.request(target) { result in
            
        }

當(dāng)然這些都只是簡(jiǎn)單的case幫助理解迄委,當(dāng)我們使用的時(shí)候還需要進(jìn)行二次封裝,以及進(jìn)行一些配置信息等类少,下面我就把我封裝好的代碼貼出來(lái)一起研究下
NetworkAPI.swift文件如下:
這里主要做一些接口的配置叙身,也是我們經(jīng)常使用的

import Foundation
import Moya

// NetworkAPI就是一個(gè)遵循TargetType協(xié)議的枚舉
enum NetworkAPI {
    //測(cè)試天氣
    case realtimeWeather(cityId:String)
    
}

extension NetworkAPI: TargetType{
    var baseURL: URL {
        return URL(string: "https://go.apipost.cn/")!
    }
    
    var path: String {
        switch self {
        case .realtimeWeather:
            return "?Query=test"
            
        }
    }
    
    var method: Moya.Method {
        switch self {
        case .realtimeWeather:
            return .post
        }
    }
    
    var task: Moya.Task {
        // 公共參數(shù)
        var params: [String: Any] = [:]
//        params["token"] = "Gz1qYLXeBW8MZuUfDlr9wsAYuVS1cZFMJY9BbaF842L2gRps747o4w=="
        
        switch self {
        case .realtimeWeather(let cityId):
            params["cityId"] = cityId
        }
        return .requestParameters(parameters: params, encoding: JSONEncoding.default)
    }
    
    var headers: [String : String]? {
        var headers: [String: String] = [:]
        
        switch self {
        case .realtimeWeather:
            headers["Content-type"] = "application/json;charset=utf-8"
        }
        
        return headers
    }
    
}

NetworkPlugin.swift文件如下:
這里是自定義的插件,當(dāng)然你也可以使用Moya默認(rèn)的四種

import Foundation
import Moya

/*
 Moya默認(rèn)有4個(gè)插件分別為:
 AccessTokenPlugin 管理AccessToken的插件
 CredentialsPlugin 管理認(rèn)證的插件
 NetworkActivityPlugin 管理網(wǎng)絡(luò)狀態(tài)的插件
 NetworkLoggerPlugin 管理網(wǎng)絡(luò)log的插件
 */
// 插件,實(shí)現(xiàn)pluginType可以實(shí)現(xiàn)在網(wǎng)絡(luò)請(qǐng)求前轉(zhuǎn)菊花硫狞,請(qǐng)求完成結(jié)束轉(zhuǎn)菊花信轿,或者寫(xiě)日志等功能
struct NetworkPlugin: PluginType {

    /// Called to modify a request before sending.(可進(jìn)行數(shù)據(jù)加密等)
    func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
        return request
    }
    
    /// Called immediately before a request is sent over the network (or stubbed).(可進(jìn)行網(wǎng)絡(luò)等待,loading等)
    func willSend(_ request: RequestType, target: TargetType) {
        
    }

    /// Called after a response has been received, but before the MoyaProvider has invoked its completion handler.(loading結(jié)束等)
    func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
        
    }

    /// Called to modify a result before completion.(可進(jìn)行數(shù)據(jù)解密等)
    func process(_ result: Result<Response, MoyaError>, target: TargetType) -> Result<Response, MoyaError> {
        return result
    }

}

NetworkManager.swift文件如下
這里就是我們的網(wǎng)絡(luò)請(qǐng)求管理中心了残吩,一些配置信息都在這里設(shè)置

import Foundation
import Moya


/// 超時(shí)時(shí)長(zhǎng)
private var requestTimeOut: Double = 30

//這個(gè)closure存放了一些moya進(jìn)行網(wǎng)絡(luò)請(qǐng)求前的一些數(shù)據(jù),可以在閉包中設(shè)置公共headers
private let endpointClosure = { (target: NetworkAPI) -> Endpoint in
     var endpoint: Endpoint = MoyaProvider.defaultEndpointMapping(for: target)
//     endpoint = endpoint.adding(newHTTPHeaderFields: ["platform": "iOS", "version" : "1.0"])
     return endpoint
 }
//這個(gè)閉包是moya提供給我們對(duì)網(wǎng)絡(luò)請(qǐng)求開(kāi)始前最后一次機(jī)會(huì)對(duì)請(qǐng)求進(jìn)行修改财忽,比如設(shè)置超時(shí)時(shí)間(默認(rèn)是60s),禁用cookie等
private let requestClosure = { (endpoint: Endpoint, done: @escaping MoyaProvider<NetworkAPI>.RequestResultClosure) -> Void in
//     guard var request = try? endpoint.urlRequest() else { return }
//     // 設(shè)置請(qǐng)求超時(shí)時(shí)間
//     request.timeoutInterval = 30
//     done(.success(request))
     do {
         var request = try endpoint.urlRequest()
         // 設(shè)置請(qǐng)求時(shí)長(zhǎng)
         request.timeoutInterval = requestTimeOut
         // 打印請(qǐng)求參數(shù)
         if let requestData = request.httpBody {
             print("請(qǐng)求的url:\(request.url!)" + "\n" + "\(request.httpMethod ?? "")" + "發(fā)送參數(shù)" + "\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
         } else {
             print("請(qǐng)求的url:\(request.url!)" + "\(String(describing: request.httpMethod))")
         }

         if let header = request.allHTTPHeaderFields {
             print("請(qǐng)求頭內(nèi)容\(header)")
         }

         done(.success(request))
     } catch {
         done(.failure(MoyaError.underlying(error, nil)))
     }
     
 }

private let networkProvider = MoyaProvider<NetworkAPI>(endpointClosure: endpointClosure, requestClosure: requestClosure, plugins: [NetworkPlugin()], trackInflights: false)



class NetworkManager {
    /// progress的回調(diào)
    typealias NetworkProgress = (CGFloat) -> Void
    
    static func request(_ target: NetworkAPI, progress: NetworkProgress? = nil, completion: @escaping Completion) -> Cancellable {
        
        let task = networkProvider.request(target, callbackQueue: DispatchQueue.main) { progressResponse in
            progress?(CGFloat(progressResponse.progress))
        } completion: { result in
            completion(result)
        }
        return task
        
    }
}

以上封裝還不夠徹底世剖,因?yàn)槲覀儾](méi)有對(duì)返回的數(shù)據(jù)進(jìn)行處理定罢,所以等有時(shí)間,我會(huì)在NetworkManager做一個(gè)對(duì)返回?cái)?shù)據(jù)進(jìn)行簡(jiǎn)單處理成json的方法旁瘫,最后在給到我們的業(yè)務(wù)層使用,先到這里吧琼蚯,祝大家國(guó)慶節(jié)快樂(lè)酬凳!

10.10日更新##

Response的封裝

第一層封裝:我們返回一個(gè)NetworkResult的結(jié)構(gòu)體

import Foundation

struct NetworkResult<M> {
    
    var data: M?
    var info: String?
    var code: String
    
}

那么NetworkManager文件里面的請(qǐng)求接口就改為以下:

struct NetworkManager<M> {
    /// progress的回調(diào)
    typealias NetworkProgress = (CGFloat) -> Void
    /// 請(qǐng)求完成的回調(diào)
    typealias NetworkCompletion = (NetworkResult<M>) -> Void
    
    @discardableResult
    func request(_ target: NetworkAPI, progress: NetworkProgress? = nil, completion: @escaping NetworkCompletion) -> Cancellable {
        
        let task = networkProvider.request(target, callbackQueue: DispatchQueue.main) { progressResponse in
            progress?(CGFloat(progressResponse.progress))
        } completion: { result in
            //result轉(zhuǎn)為NetworkResult結(jié)構(gòu)體
            switch result {
            case .success(let response):
                do{
                    guard let json = try response.mapJSON() as? [String: Any] else {
                        let networkResult = NetworkResult<M>(info: "服務(wù)器返回的不是JSON數(shù)據(jù)", code: "-1")
                        completion(networkResult)
                        return
                    }
                    
                    let networkResult = NetworkResult<M>(data: json["data"] as? M, info: json["message"] as? String, code: json["code"] as? String ?? "-1")
                    completion(networkResult)
                    
                } catch {
                    let networkResult = NetworkResult<M>(info: "解析出錯(cuò):\(error.localizedDescription)", code: "-1")
                    completion(networkResult)
                }
                
            case .failure(let error):
                let networkResult = NetworkResult<M>(info: "請(qǐng)求失敗:\(String(describing: error.errorDescription))", code: "-1")
                completion(networkResult)
                
            }
        }
        return task
        
    }
}

第二層封裝:結(jié)合Swift4.0中的Encodable、Decodable協(xié)議遭庶,利用泛型知識(shí)將NetworkResult中的data轉(zhuǎn)為對(duì)應(yīng)的數(shù)據(jù)模型返回出去宁仔。
NetworkResult.swift文件如下

import Foundation
import Moya

struct NetworkResult<M: Decodable> {
    
    var data: M?
    var info: String?
    var code: String
    
    init(json: [String: Any]) {
        code = json["code"] as? String ?? "-1"
        info = json["message"] as? String
//        data = parseData(jsonObj: json["data"])
        data = parseData(jsonObj: json["data"])
    }
    
    init(errorMsg: String?) {
        code = "-1"
        info = errorMsg
    }
    
    /// 解析數(shù)據(jù)
    func parseData(jsonObj: Any?) -> M? {
        /// 判斷是否為nil
        guard let dataObj = jsonObj else {
            return nil
        }
        
        /// 判斷是否為NSNull
        guard !(dataObj as AnyObject).isEqual(NSNull()) else {
            return nil
        }
        
        /// 本身為M類型,直接賦值
        if let dataObj = dataObj as? M {
            return dataObj
        }
        
        /// 轉(zhuǎn)模型
        let jsonData = try? JSONSerialization.data(withJSONObject: dataObj, options: .prettyPrinted)
        guard let data = jsonData else { return nil }
        do{
            return try JSONDecoder().decode(M.self, from: data)
        } catch {
            print(error)
            return nil
        }
    }
    
}

NetworkManager.swift文件請(qǐng)求接口如下:

struct NetworkManager<M: Codable> {
    /// progress的回調(diào)
    typealias NetworkProgress = (CGFloat) -> Void
    /// 請(qǐng)求完成的回調(diào)
    typealias NetworkCompletion = (NetworkResult<M>) -> Void
    
    @discardableResult
    func request(_ target: NetworkAPI, progress: NetworkProgress? = nil, completion: @escaping NetworkCompletion) -> Cancellable {
        
        let task = networkProvider.request(target, callbackQueue: DispatchQueue.main) { progressResponse in
            progress?(CGFloat(progressResponse.progress))
        } completion: { result in
            //result轉(zhuǎn)為NetworkResult結(jié)構(gòu)體
            switch result {
            case .success(let response):
                do{
                    guard let json = try response.mapJSON() as? [String: Any] else {
                        completion(NetworkResult<M>(errorMsg: "服務(wù)器返回的不是JSON數(shù)據(jù)"))
                        return
                    }
                    
                    completion(NetworkResult<M>(json: json))
                    
                } catch {
                    //解析出錯(cuò)
                    completion(NetworkResult<M>(errorMsg:error.localizedDescription))
                }
                
            case .failure(let error):
                //請(qǐng)求出錯(cuò)
                completion(NetworkResult<M>(errorMsg: error.errorDescription))
                
            }
        }
        return task
        
    }
}

當(dāng)然峦睡,我們也可以給Result再增加一個(gè)擴(kuò)展方法翎苫,優(yōu)化我們的NetworkManager文件,以下就是最終的文件了
NetworkResult

import Foundation
import Moya

struct NetworkResult<M: Decodable> {
    
    var data: M?
    var info: String?
    var code: String
    
    init(json: [String: Any]) {
        code = json["code"] as? String ?? "-1"
        info = json["message"] as? String
//        data = parseData(jsonObj: json["data"])
        data = parseData(jsonObj: json["data"])
    }
    
    init(errorMsg: String?) {
        code = "-1"
        info = errorMsg
    }
    
    /// 解析數(shù)據(jù)
    func parseData(jsonObj: Any?) -> M? {
        /// 判斷是否為nil
        guard let dataObj = jsonObj else {
            return nil
        }
        
        /// 判斷是否為NSNull
        guard !(dataObj as AnyObject).isEqual(NSNull()) else {
            return nil
        }
        
        /// 本身為M類型榨了,直接賦值
        if let dataObj = dataObj as? M {
            return dataObj
        }
        
        /// 轉(zhuǎn)模型
        let jsonData = try? JSONSerialization.data(withJSONObject: dataObj, options: .prettyPrinted)
        guard let data = jsonData else { return nil }
        do{
            return try JSONDecoder().decode(M.self, from: data)
        } catch {
            print(error)
            return nil
        }
    }
    
}
//給Result增加一個(gè)擴(kuò)展方法
extension Result where Success: Response, Failure == MoyaError {
    func mapNetworkResult<M>(_ type: M.Type) -> NetworkResult<M> where M: Decodable {
        switch self {
        case .success(let response):
            do {
                guard let json = try response.mapJSON() as? [String: Any] else {
                    /// 不是JSON數(shù)據(jù)
                    return NetworkResult(errorMsg: "服務(wù)器返回的不是JSON數(shù)據(jù)")
                }
                return NetworkResult(json: json)
            } catch {
                /// 解析出錯(cuò)
                return NetworkResult(errorMsg: error.localizedDescription)
            }
        case .failure(let error):
            /// 請(qǐng)求出錯(cuò)
            return NetworkResult(errorMsg: error.errorDescription)
        }
    }
    
}

NetworkManager

import Foundation
import Moya


/// 超時(shí)時(shí)長(zhǎng)
private var requestTimeOut: Double = 30

//這個(gè)closure存放了一些moya進(jìn)行網(wǎng)絡(luò)請(qǐng)求前的一些數(shù)據(jù),可以在閉包中設(shè)置公共headers
private let endpointClosure = { (target: NetworkAPI) -> Endpoint in
     var endpoint: Endpoint = MoyaProvider.defaultEndpointMapping(for: target)
//     endpoint = endpoint.adding(newHTTPHeaderFields: ["platform": "iOS", "version" : "1.0"])
     return endpoint
 }
//這個(gè)閉包是moya提供給我們對(duì)網(wǎng)絡(luò)請(qǐng)求開(kāi)始前最后一次機(jī)會(huì)對(duì)請(qǐng)求進(jìn)行修改煎谍,比如設(shè)置超時(shí)時(shí)間(默認(rèn)是60s),禁用cookie等
private let requestClosure = { (endpoint: Endpoint, done: @escaping MoyaProvider<NetworkAPI>.RequestResultClosure) -> Void in
//     guard var request = try? endpoint.urlRequest() else { return }
//     // 設(shè)置請(qǐng)求超時(shí)時(shí)間
//     request.timeoutInterval = 30
//     done(.success(request))
     do {
         var request = try endpoint.urlRequest()
         // 設(shè)置請(qǐng)求時(shí)長(zhǎng)
         request.timeoutInterval = requestTimeOut
         // 打印請(qǐng)求參數(shù)
         if let requestData = request.httpBody {
             print("請(qǐng)求的url:\(request.url!)" + "\n" + "\(request.httpMethod ?? "")" + "發(fā)送參數(shù)" + "\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
         } else {
             print("請(qǐng)求的url:\(request.url!)" + "\(String(describing: request.httpMethod))")
         }

         if let header = request.allHTTPHeaderFields {
             print("請(qǐng)求頭內(nèi)容\(header)")
         }

         done(.success(request))
     } catch {
         done(.failure(MoyaError.underlying(error, nil)))
     }
     
 }

private let networkProvider = MoyaProvider<NetworkAPI>(endpointClosure: endpointClosure, requestClosure: requestClosure, plugins: [NetworkPlugin()], trackInflights: false)


struct NetworkManager<M: Codable> {
    /// progress的回調(diào)
    typealias NetworkProgress = (CGFloat) -> Void
    /// 請(qǐng)求完成的回調(diào)
    typealias NetworkCompletion = (NetworkResult<M>) -> Void
    
    @discardableResult
    func request(_ target: NetworkAPI, progress: NetworkProgress? = nil, completion: @escaping NetworkCompletion) -> Cancellable {
        
        let task = networkProvider.request(target, callbackQueue: DispatchQueue.main) { progressResponse in
            progress?(CGFloat(progressResponse.progress))
        } completion: { result in
            //result轉(zhuǎn)為NetworkResult結(jié)構(gòu)體
            let networkResult = result.mapNetworkResult(M.self)
            completion(networkResult)
        }
        return task
        
    }
}

使用起來(lái)就簡(jiǎn)單多了龙屉,比如我們來(lái)個(gè)用戶登錄:
先來(lái)創(chuàng)建個(gè)和服務(wù)器商量好的結(jié)構(gòu)體模型

import Foundation

struct LoginModel: Codable  {
    
    var token: String
    var user: User
}

struct User: Codable {
    
    var id: String
    var mobile: String
    var userName: String?
    var url: String?
    
    init(id: String, mobile: String) {
        self.id = id
        self.mobile = mobile
    }
}

然后我們配置NetworkApi文件(這個(gè)就不寫(xiě)了)呐粘,接下來(lái)就是調(diào)用:

        let target = NetworkAPI.login(mobile: "12345789", verifyCode: "6666")
        NetworkManager<LoginModel>().request(target){ [weak self] (result) in
            guard let self = self else { return }
            if let loginModel = result.data {
                print("登錄成功!")
                let userMessage = "token: \(loginModel.token)" + "\n\nid: \(loginModel.user.id)" + "\n\nmobile: \(loginModel.user.mobile)"
               print(userMessage)
            } else {
                //失敗
                print(result.info)
            }
            
        }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末转捕,一起剝皮案震驚了整個(gè)濱河市作岖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌五芝,老刑警劉巖痘儡,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異枢步,居然都是意外死亡沉删,警方通過(guò)查閱死者的電腦和手機(jī)渐尿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)丑念,“玉大人涡戳,你說(shuō)我怎么就攤上這事「校” “怎么了渔彰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)推正。 經(jīng)常有香客問(wèn)我恍涂,道長(zhǎng),這世上最難降的妖魔是什么植榕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任再沧,我火速辦了婚禮,結(jié)果婚禮上尊残,老公的妹妹穿的比我還像新娘炒瘸。我一直安慰自己,他們只是感情好寝衫,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布顷扩。 她就那樣靜靜地躺著,像睡著了一般慰毅。 火紅的嫁衣襯著肌膚如雪隘截。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,158評(píng)論 1 308
  • 那天汹胃,我揣著相機(jī)與錄音婶芭,去河邊找鬼。 笑死着饥,一個(gè)胖子當(dāng)著我的面吹牛犀农,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贱勃,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼井赌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了贵扰?” 一聲冷哼從身側(cè)響起仇穗,我...
    開(kāi)封第一講書(shū)人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎戚绕,沒(méi)想到半個(gè)月后纹坐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舞丛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年耘子,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了果漾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谷誓,死狀恐怖绒障,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捍歪,我是刑警寧澤户辱,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站糙臼,受9級(jí)特大地震影響庐镐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜变逃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一必逆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧揽乱,春花似錦名眉、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至渊啰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間申屹,已是汗流浹背绘证。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哗讥,地道東北人嚷那。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像杆煞,于是被迫代替她去往敵國(guó)和親魏宽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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