基于Alamofire封裝一套多功能的Swift版網(wǎng)絡(luò)庫(kù)

WXNetworkingSwift

簡(jiǎn)介

有沒(méi)有遇到過(guò)這樣一種情況爷辙,每次在項(xiàng)目中使用請(qǐng)求庫(kù)去請(qǐng)求數(shù)據(jù)時(shí),各種小功能需要自己在每個(gè)請(qǐng)求里面單獨(dú)去開(kāi)發(fā)褐墅,比如請(qǐng)求緩存痴颊、請(qǐng)求HUD赏迟、設(shè)置請(qǐng)求頭、設(shè)置失敗重試機(jī)制蠢棱、判斷是否請(qǐng)求成功锌杀、請(qǐng)求個(gè)性化打印日志、控制批量請(qǐng)求泻仙、頁(yè)面請(qǐng)求重復(fù)寫數(shù)據(jù)轉(zhuǎn)模型......, 甚至使用了很久的第三方網(wǎng)絡(luò)某一天不維護(hù)了抛丽,導(dǎo)致項(xiàng)目那里面每個(gè)頁(yè)面到處直接使用的Api更換起來(lái)簡(jiǎn)直就是災(zāi)難,面對(duì)這種情況特意 底層基于Alamofire庫(kù) 封裝一套支持高度自定義擴(kuò)展多功能的網(wǎng)絡(luò)請(qǐng)求庫(kù)饰豺,幫我們?cè)陂_(kāi)發(fā)時(shí)省去了很多配置亿鲜,也可以增加自定義插件使,即使以后更換底層請(qǐng)求庫(kù)也很方便,后續(xù)也會(huì)不斷維護(hù)更新各種小功能蒿柳,目前支持的主要功能如下: OC版本的見(jiàn)這里

功能列表:

  • 1饶套、自定義請(qǐng)求頭;簡(jiǎn)單配置請(qǐng)求頭或加密頭

  • 2垒探、自動(dòng)處理是否緩存妓蛮;設(shè)置緩存機(jī)制,自動(dòng)失效時(shí)間等

  • 3圾叼、請(qǐng)求失敗自定義多次重試蛤克;支持失敗后每隔幾秒嘗試再試請(qǐng)求,如啟動(dòng)App后一定要請(qǐng)求的必要數(shù)據(jù)接口夷蚊。

  • 4构挤、支持上傳接口抓包日志;如上傳到公司內(nèi)部日志服務(wù)器系統(tǒng)上惕鼓,供測(cè)試人員排查問(wèn)題或快速抓包排查問(wèn)題筋现。

  • 5、極簡(jiǎn)上傳下載文件監(jiān)聽(tīng); 簡(jiǎn)單配置監(jiān)聽(tīng)上傳下載文件進(jìn)度箱歧。

  • 6矾飞、支持全局/單個(gè)配置請(qǐng)求成功后keyPath模型映射;頁(yè)面上無(wú)需每個(gè)接口編寫解析字典轉(zhuǎn)模型的重新代碼呀邢,支持?jǐn)?shù)組和自定義模型洒沦;

  • 7、約定全局請(qǐng)求的提示Hud ToastKey价淌;支持單個(gè)配置或全局配置請(qǐng)求失敗時(shí)的HUD Toast自動(dòng)彈框提示申眼。

  • 8、請(qǐng)求遇到相應(yīng)Code時(shí)觸發(fā)通知输钩;如:Token失效全部重新登錄等;

  • 9、網(wǎng)絡(luò)請(qǐng)求過(guò)程多鏈路回調(diào)管理仲智;如:請(qǐng)求將要開(kāi)始回調(diào)买乃,請(qǐng)求回調(diào)將要停止,請(qǐng)求已經(jīng)回調(diào)完成;

  • 10钓辆、格式化打印網(wǎng)絡(luò)日志剪验;輸出日志一目了然,如:請(qǐng)求接口地址前联、參數(shù)功戚、請(qǐng)求頭、耗時(shí)似嗤、響應(yīng);

  • 11啸臀、批量請(qǐng)求;支持自定義每個(gè)請(qǐng)求的所有配置,并且可配置等待全部完成才回調(diào)還是一起完成才回調(diào);

  • 12乘粒、支持debug模式不請(qǐng)求網(wǎng)絡(luò)快速調(diào)試模擬接口響應(yīng)數(shù)據(jù)豌注;如:本地json string,Dictionary灯萍,local json file, http test url

    . . . . . .(持續(xù)完善-ing)

使用環(huán)境

iOS, swift 5.0

安裝方式

WXNetworkingSwift is available through CocoaPods. To install
it, simply add the following line to your Podfile:

pod 'WXNetworkingSwift'

用法方法見(jiàn) (示例Demo

可靈活配置的基礎(chǔ)請(qǐng)求對(duì)象

///請(qǐng)求基礎(chǔ)對(duì)象, 外部上不建議直接用轧铁,請(qǐng)使用子類請(qǐng)求方法
open class WXBaseRequest: NSObject {
    ///請(qǐng)求Method類型
    fileprivate (set) var requestMethod: HTTPMethod = .post
    ///請(qǐng)求地址
    fileprivate (set) var requestURL: String = ""
    ///請(qǐng)求參數(shù)
    fileprivate var parameters: WXDictionaryStrAny? = nil
    ///請(qǐng)求超時(shí),默認(rèn)30是
    public var timeOut: TimeInterval = 30
    ///請(qǐng)求自定義頭信息
    public var requestHeaderDict: Dictionary<String, String>? = nil
    ///請(qǐng)求序列化對(duì)象 (json, form表單)
    public var requestSerializer: WXRequestSerializerType = .EncodingJSON
    ///請(qǐng)求任務(wù)對(duì)象
    fileprivate var requestDataTask: Request? = nil
    
    ///請(qǐng)求方法見(jiàn)源碼
}

可靈活配置的單個(gè)請(qǐng)求對(duì)象:


/// 單個(gè)請(qǐng)求對(duì)象, 功能根據(jù)需求可多種自定義
open class WXRequestApi: WXBaseRequest {
    
    ///請(qǐng)求成功時(shí)是否自動(dòng)緩存響應(yīng)數(shù)據(jù), 默認(rèn)不緩存
    public var autoCacheResponse: Bool = false
    
    ///自定義請(qǐng)求成功時(shí)的緩存數(shù)據(jù), (返回的字典為此次需要保存的緩存數(shù)據(jù), 返回nil時(shí)底層則不緩存)
    public var cacheResponseBlock: ( (WXResponseModel) -> (WXDictionaryStrAny?) )? = nil
    
    ///自定義解析成功時(shí)的響應(yīng)數(shù)據(jù), (例如: 在請(qǐng)求成功后 需要解密響應(yīng)的json結(jié)果后才能真正獲取成功標(biāo)識(shí), 解析模型等等..)
    public var decryptHandlerResponse: ((AnyObject) -> AnyObject)? = nil
    
    ///自定義請(qǐng)求成功映射Key/Value, (key可以是KeyPath模式進(jìn)行匹配 如: data.status)
    ///注意: 每個(gè)請(qǐng)求狀態(tài)優(yōu)先使用此屬性判斷, 如果此屬性值為空, 則再取全局的 WXNetworkConfig.successStatusMap的值進(jìn)行判斷
    public var successStatusMap: (key: String, value: String)? = nil

    ///請(qǐng)求成功時(shí)自動(dòng)解析數(shù)據(jù)模型映射:Key/ModelType, (key可以是KeyPath模式進(jìn)行匹配 如: data.returnData)
    ///成功解析的模型在 WXResponseModel.parseKeyPathModel 中返回
    public var parseModelMap: (parseKey: String, modelType: Convertible.Type)? = nil
    
    ///times: 請(qǐng)求失敗之后重新請(qǐng)求次數(shù), delay: 每次重試的間隔
    public var retryWhenFailTuple: (times: Int, delay: Double)? = nil
    
    /// [??僅DEBUG模式生效??] 作用:方便開(kāi)發(fā)時(shí)調(diào)試接口使用,設(shè)置的值可為以下4種:
    /// 1. json String: 則不會(huì)請(qǐng)求網(wǎng)絡(luò), 直接響應(yīng)回調(diào)此json值
    /// 2. Dictionary: 則不會(huì)請(qǐng)求網(wǎng)絡(luò), 直接響應(yīng)回調(diào)此Dictionary值
    /// 3. local file path: 則直接讀取當(dāng)前本地的path文件內(nèi)容
    /// 4. http(是) path: 則直接請(qǐng)求當(dāng)前設(shè)置的path
    public var debugJsonResponse: Any? = nil

    ///請(qǐng)求轉(zhuǎn)圈的父視圖
    public var loadingSuperView: UIView? = nil
    
    ///上傳文件Data數(shù)組
    public var uploadFileDataArr: [ Data ]? = nil
    
    ///自定義上傳時(shí)包裝的數(shù)據(jù)Data對(duì)象
    public var uploadConfigDataBlock: ( (MultipartFormData) -> Void )? = nil
    
    ///監(jiān)聽(tīng)上傳/下載進(jìn)度
    public var fileProgressBlock: WXProgressBlock? = nil
    
    ///網(wǎng)絡(luò)請(qǐng)求過(guò)程多鏈路回調(diào)<將要開(kāi)始, 將要停止, 已經(jīng)完成>
    /// 注意: 如果沒(méi)有實(shí)現(xiàn)此代理則會(huì)回調(diào)單例中的全局代理<globleMulticenterDelegate>
    public var multicenterDelegate: WXNetworkMulticenter? = nil
    
    ///可以用來(lái)添加幾個(gè)accossories對(duì)象 來(lái)做額外的插件等特殊功能
    ///如: (請(qǐng)求HUD, 加解密, 自定義打印, 上傳統(tǒng)計(jì))
    public var requestAccessories: [WXNetworkMulticenter]? = nil
    
    ///請(qǐng)求方法見(jiàn)源碼
}

請(qǐng)求響應(yīng)對(duì)象的豐富信息

//MARK: - 請(qǐng)求響應(yīng)對(duì)象

///包裝的響應(yīng)數(shù)據(jù)
public class WXResponseModel: NSObject {
    /**
     * 是否請(qǐng)求成功,優(yōu)先使用 WXRequestApi.successStatusMap 來(lái)判斷是否成功
     * 否則使用 WXNetworkConfig.successStatusMap 標(biāo)識(shí)來(lái)判斷是否請(qǐng)求成功
     ***/
    public var isSuccess: Bool = false
    ///本次響應(yīng)Code碼
    public var responseCode: Int? = nil
    ///本次響應(yīng)的提示信息 (頁(yè)面可直接用于Toast提示,
    ///如果接口有返回messageTipKeyAndFailInfo.tipKey則會(huì)取這個(gè)值, 如果沒(méi)有返回則取defaultTip的默認(rèn)值)
    public var responseMsg: String? = nil
    ///本次數(shù)據(jù)是否為緩存
    public var isCacheData: Bool = false
    ///請(qǐng)求耗時(shí)(毫秒)
    public var responseDuration: TimeInterval? = nil
    ///解析數(shù)據(jù)的模型: 可KeyPath匹配, 返回 Model對(duì)象 或者數(shù)組模型 [Model]
    public var parseKeyPathModel: AnyObject? = nil
    ///本次響應(yīng)的原始數(shù)據(jù): NSDictionary/ UIImage/ NSData /...
    public var responseObject: AnyObject? = nil
    ///本次響應(yīng)的原始字典數(shù)據(jù)
    public var responseDict: WXDictionaryStrAny? = nil
    ///本次響應(yīng)的數(shù)據(jù)是否為Debug測(cè)試數(shù)據(jù)
    public var isDebugResponse: Bool = false
    ///失敗時(shí)的錯(cuò)誤信息
    public var error: NSError? = nil
    ///原始響應(yīng)
    public var urlResponse: HTTPURLResponse? = nil
    ///原始請(qǐng)求
    public var urlRequest: URLRequest? = nil
}

可靈活配置的批量請(qǐng)求對(duì)象:

///批量請(qǐng)求對(duì)象, 可以
open class WXBatchRequestApi {
    
    ///全部請(qǐng)求是否都成功了
    public var isAllSuccess: Bool = false
    
    ///全部響應(yīng)數(shù)據(jù), 按請(qǐng)求requestArray的Api添加順序排序返回
    public var responseDataArray: [WXResponseModel] = []
    
    ///全部請(qǐng)求對(duì)象, 響應(yīng)時(shí)Api按添加順序返回
    fileprivate var requestArray: [WXRequestApi]
    ///請(qǐng)求轉(zhuǎn)圈的父視圖
    fileprivate (set) var loadingSuperView: UIView? = nil
    
    ///請(qǐng)求方法見(jiàn)源碼
}

1.單個(gè)請(qǐng)求示例

func testRequest() {
        let url = "http://123.207.32.32:8000/home/multidata"
        let api = WXRequestApi(url, method: .get)
        api.timeOut = 40 //設(shè)置超時(shí)時(shí)間
        api.loadingSuperView = view //請(qǐng)求loading HUD
        api.autoCacheResponse = true //是否需要緩存
        api.requestHeaderDict = [:] //設(shè)置請(qǐng)求自定義頭信息
        api.successStatusMap = (key: "returnCode",  value: "SUCCESS") //設(shè)置請(qǐng)求成功標(biāo)識(shí)key(支持keyPath)
        api.parseModelMap = (parseKey: "data.dKeyword", modelType: DKeywordModel.self)  //設(shè)置請(qǐng)求成功模型解析(支持keyPath)
        api.retryWhenFailTuple = (times: 3, delay: 2.0) //設(shè)置請(qǐng)求失敗重試機(jī)制
        api.multicenterDelegate = self //網(wǎng)絡(luò)請(qǐng)求過(guò)程多鏈路回調(diào)<將要開(kāi)始, 將要停止, 已經(jīng)完成>
        
        //設(shè)置自定義解析成功時(shí)的響應(yīng)數(shù)據(jù)
        api.decryptHandlerResponse = { (response: AnyObject) -> AnyObject in
            // 自定義解析數(shù)據(jù)
        }
        //設(shè)置自定義請(qǐng)求成功時(shí)的緩存數(shù)據(jù)
        api.cacheResponseBlock = { WXResponseModel -> WXDictionaryStrAny? in
            //自定義緩存
        }
        
        requestTask = api.startRequest { [weak self] responseModel in
            if responseModel.isSuccess {
                self?.textView.text = responseModel.parseKeyPathModel?.description
            } else {
                self?.textView.text = responseModel.responseMsg
            }
        }
    }

2.批量請(qǐng)求示例

func testBatchRequest() {
        let url1 = "https://httpbin.org/get"
        let api1 = WXRequestApi(url1, method: .get)
        api1.autoCacheResponse = true
        
        let url2 = "https://httpbin.org/delay/5"
        let para2: [String : Any] = ["name" : "張三"]
        let api2 = WXRequestApi(url2, method: .get, parameters: para2)
        
        let api = WXBatchRequestApi(apiArray: [api1, api2], loadingTo: view)
        api.startRequest({ [weak self] batchApi in
            print("批量請(qǐng)求回調(diào)", batchApi.responseDataArray)
            self?.textView.text = batchApi.responseForRequest(request: api1)?.responseDict?.description
            
        }, waitAllDone: true)
    }

3.Json請(qǐng)求解析模型示例

func testParseModel() {
        let url = "http://app.u17.com/v3/appV3_3/ios/phone/comic/boutiqueListNew"
        let param: [String : Any] = ["sexType" : 1]

        let api = WXRequestApi(url, method: .get, parameters: param)
//        api.debugJsonResponse = "http://10.8.41.162:8090/app/activity/page/detail/92546"  //http( s ) test URL
//        api.debugJsonResponse = "/Users/xinGe/Desktop/test.json"                          //Desktop json file
//        api.debugJsonResponse = "test.json"                                               //Bundle json file
//        api.debugJsonResponse = ["code" : "1", "data" : ["message" : "測(cè)試字典"]]          //Dictionary Object
//        api.debugJsonResponse =
//"""
//        {"data":{"message":"成功","stateCode":1,"returnData":{"galleryItems":[],"comicLists":[{"comics":[{"subTitle":"少年 搞笑","short_description":"突破次元壁的漫畫旦棉!","is_vip":4,"cornerInfo":"190","comicId":181616,"author_name":"壁水羽","cover":"https://cover-oss.u17i.com/2021/07/12647_1625125865_1za73F2a4fD1.sbig.jpg","description":"漫畫角色發(fā)現(xiàn)自己生活在一個(gè)漫畫的籠子里齿风,于是奮起反抗作者,面對(duì)角色的不配合绑洛,作者不得已要不斷更改題材救斑,恐怖,魔幻诊笤,勵(lì)志輪番上陣系谐,主角們要一一面對(duì),全力通關(guān)","name":"籠中人","tags":["少年","搞笑"]}],"comicType":6,"sortId":"86","newTitleIconUrl":"https://image.mylife.u17t.com/2017/07/10/1499657929_N7oo9pPOhaYH.png","argType":3,"argValue":8,"titleIconUrl":"https://image.mylife.u17t.com/2017/08/29/1503986106_7TY5gK000yjZ.png","itemTitle":"強(qiáng)力推薦作品","description":"更多","canedit":0,"argName":"topic"}],"textItems":[],"editTime":"0"}},"code":1}
//"""

        api.timeOut = 40
        api.loadingSuperView = view
        api.autoCacheResponse = false
        api.retryWhenFailTuple = (times: 3, delay: 1.0)
        api.successStatusMap = (key: "code", value: "1")
        // api.parseModelMap = (parseKey: "data.returnData.comicLists", modelType: ComicListModel.self)

        requestTask = api.startRequest { [weak self] responseModel in
            self?.textView.backgroundColor = .groupTableViewBackground
            if let rspData = responseModel.responseObject as? Data {
                if let image = UIImage(data: rspData) {
                    self?.textView.backgroundColor = .init(patternImage: image)
                }
            }
        }
    }

4.上傳文件示例

func testUploadFile() {
        let url = "http://10.8.31.5:8090/uploadImage"
        let param = [
            "appName" : "TEST",
            "platform" : "iOS",
            "version" : "7.3.3",
        ]
        let api = WXRequestApi(url, method: .post, parameters: param)
        api.loadingSuperView = view
        api.retryWhenFailTuple = (times: 3, delay: 3.0)
        api.successStatusMap = (key: "code", value: "200")
        
        let image = UIImage(named: "womenPic")!
        let imageData = UIImagePNGRepresentation(image)
        
        api.uploadFileDataArr = [imageData!]
        api.uploadConfigDataBlock = { multipartFormData in
            multipartFormData.append(imageData!, withName: "files", fileName: "womenPic.png", mimeType: "image/png")
        }
        api.fileProgressBlock = { progress in
            let total = Float(progress.totalUnitCount)
            let completed = Float(progress.completedUnitCount)
            let percentage = completed / total * 100
            print("上傳進(jìn)度: \(String(format:"%.2f",percentage)) %")
        }
        requestTask = api.uploadFile { [weak self] responseModel in
            if responseModel.isSuccess {
                self?.textView.backgroundColor = .init(patternImage: image)
            }
        }
    }

5.下載文文件示例

func testDownloadFile() {
        //壓縮包
        var  url = "http://i.gtimg.cn/qqshow/admindata/comdata/vipThemeNew_item_2135/2135_i_4_7_i_1.zip"
        //視頻: (來(lái)源于: http://sp.jzsc.net)
        url = "http://down1.jzsc.net//sp/video/2019-06-22/d4fe4a94-0c21-4c99-ad35-2bdb23ab4de9.mp4"
        //圖片
        url = "https://picsum.photos/375/667?random=1"
        
        let api = WXRequestApi(url, method: .get, parameters: nil)
        api.loadingSuperView = view
        
        api.fileProgressBlock = { progress in
            let total = Double(progress.totalUnitCount)
            let completed = Double(progress.completedUnitCount)
            let percentage = completed / total * 100.0
            print("下載進(jìn)度: \(String(format:"%.2f",percentage)) %")
        }
        requestTask = api.downloadFile { [weak self] responseModel in
            if let rspData = responseModel.responseObject as? Data {
                if let image = UIImage(data: rspData) {
                    self?.textView.backgroundColor = .init(patternImage: image)
                }
                if var mimeType = responseModel.urlResponse?.mimeType {
                    mimeType = mimeType.replacingOccurrences(of: "/", with: ".")
                    let url = URL(fileURLWithPath: "/Users/xin610582/Desktop/" + mimeType, isDirectory: true)
                    try? rspData.write(to: url)
                }
            }
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末讨跟,一起剝皮案震驚了整個(gè)濱河市纪他,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晾匠,老刑警劉巖茶袒,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凉馆,居然都是意外死亡薪寓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門澜共,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)向叉,“玉大人,你說(shuō)我怎么就攤上這事嗦董∧富眩” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵京革,是天一觀的道長(zhǎng)奇唤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)匹摇,這世上最難降的妖魔是什么咬扇? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮廊勃,結(jié)果婚禮上懈贺,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好隅居,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布钠至。 她就那樣靜靜地躺著,像睡著了一般胎源。 火紅的嫁衣襯著肌膚如雪棉钧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天涕蚤,我揣著相機(jī)與錄音宪卿,去河邊找鬼。 笑死万栅,一個(gè)胖子當(dāng)著我的面吹牛佑钾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烦粒,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼休溶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了扰她?” 一聲冷哼從身側(cè)響起兽掰,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎徒役,沒(méi)想到半個(gè)月后孽尽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡忧勿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年杉女,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸳吸。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡熏挎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晌砾,到底是詐尸還是另有隱情坎拐,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布贡羔,位于F島的核電站廉白,受9級(jí)特大地震影響个初,放射性物質(zhì)發(fā)生泄漏乖寒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一院溺、第九天 我趴在偏房一處隱蔽的房頂上張望楣嘁。 院中可真熱鬧,春花似錦、人聲如沸逐虚。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)叭爱。三九已至撮躁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間买雾,已是汗流浹背把曼。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漓穿,地道東北人嗤军。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像晃危,于是被迫代替她去往敵國(guó)和親叙赚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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