iOS WKWebView

1. WKWebView

1.1 常用屬性
// 導(dǎo)航代理
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
// UI代理
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;

// 頁面標(biāo)題, 一般使用KVO動態(tài)獲取
@property (nullable, nonatomic, readonly, copy) NSString *title;
// 頁面加載進度, 一般使用KVO動態(tài)獲取
@property (nonatomic, readonly) double estimatedProgress;

// 可返回的頁面列表, 已打開過的網(wǎng)頁, 有點類似于navigationController的viewControllers屬性
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;

// 頁面url
@property (nullable, nonatomic, readonly, copy) NSURL *URL;
// 頁面是否在加載中
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
// 是否可返回
@property (nonatomic, readonly) BOOL canGoBack;
// 是否可向前
@property (nonatomic, readonly) BOOL canGoForward;
// WKWebView繼承自UIView, 所以如果想設(shè)置scrollView的一些屬性, 需要對此屬性進行配置
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
// 是否允許手勢左滑返回上一級, 類似導(dǎo)航控制的左滑返回
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

//自定義UserAgent, 會覆蓋默認的值 ,iOS 9之后有效
@property (nullable, nonatomic, copy) NSString *customUserAgent

1.2 一些方法:
// 帶配置信息的初始化方法
// configuration 配置信息
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
// 加載請求
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
// 加載HTML
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// 返回上一級
- (nullable WKNavigation *)goBack;
// 前進下一級, 需要曾經(jīng)打開過, 才能前進
- (nullable WKNavigation *)goForward;
// 刷新頁面
- (nullable WKNavigation *)reload;
// 根據(jù)緩存有效期來刷新頁面
- (nullable WKNavigation *)reloadFromOrigin;
// 停止加載頁面
- (void)stopLoading;
// 執(zhí)行JavaScript代碼
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

2. WKWebViewConfiguration

// 通過此屬性來執(zhí)行JavaScript代碼來修改頁面的行為
@property (nonatomic, strong) WKUserContentController *userContentController;

//***********下面屬性一般不需要設(shè)置
// 首選項設(shè)置,  
//可設(shè)置最小字號, 是否允許執(zhí)行js
//是否通過js自動打開新的窗口
@property (nonatomic, strong) WKPreferences *preferences;
// 是否允許播放媒體文件
@property (nonatomic) BOOL allowsAirPlayForMediaPlayback
// 需要用戶來操作才能播放的多媒體類型
@property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback
// 是使用h5的視頻播放器在線播放, 還是使用原生播放器全屏播放
@property (nonatomic) BOOL allowsInlineMediaPlayback;

3. WKUserContentController

WKUserContentController 是JavaScript與原生進行交互的橋梁, 主要使用的方法有:

// 注入JavaScript與原生交互協(xié)議
// JS 端可通過 window.webkit.messageHandlers.<name>.postMessage(<messageBody>) 發(fā)送消息
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
// 移除注入的協(xié)議, 在deinit方法中調(diào)用
- (void)removeScriptMessageHandlerForName:(NSString *)name;

// 通過WKUserScript注入需要執(zhí)行的JavaScript代碼
- (void)addUserScript:(WKUserScript *)userScript;
// 移除所有注入的JavaScript代碼
- (void)removeAllUserScripts;

使用WKUserContentController注入的交互協(xié)議, 需要遵循WKScriptMessageHandler協(xié)議, 在其協(xié)議方法中獲取JavaScript端傳遞的事件和參數(shù):

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

WKScriptMessage包含了傳遞的協(xié)議名稱及參數(shù), 主要從下面的屬性中獲取:

// 協(xié)議名稱, 即上面的add方法傳遞的name
@property (nonatomic, readonly, copy) NSString *name;
// 傳遞的參數(shù)
@property (nonatomic, readonly, copy) id body;

4. WKUserScript

WKUserScript用于往加載的頁面中添加額外需要執(zhí)行的JavaScript代碼, 主要是一個初始化方法:

/*
source: 需要執(zhí)行的JavaScript代碼
injectionTime: 加入的位置, 是一個枚舉
typedef NS_ENUM(NSInteger, WKUserScriptInjectionTime) {
    WKUserScriptInjectionTimeAtDocumentStart,
    WKUserScriptInjectionTimeAtDocumentEnd
} API_AVAILABLE(macosx(10.10), ios(8.0));

forMainFrameOnly: 是加入所有框架, 還是只加入主框架
*/
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;

5. WKUIDelegate

這個代理方法, 主要是用來處理使用系統(tǒng)的彈框來替換JS中的一些彈框的,比如: 警告框, 選擇框, 輸入框, 主要使用的是下面三個代理方法:

/**
 webView中彈出警告框時調(diào)用, 只能有一個按鈕

 @param webView webView
 @param message 提示信息
 @param frame 可用于區(qū)分哪個窗口調(diào)用的
 @param completionHandler 警告框消失的時候調(diào)用, 回調(diào)給JS
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"警告" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"我知道了" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];

    [alert addAction:ok];
    [self presentViewController:alert animated:YES completion:nil];
}

/** 對應(yīng)js的confirm方法
 webView中彈出選擇框時調(diào)用, 兩個按鈕

 @param webView webView description
 @param message 提示信息
 @param frame 可用于區(qū)分哪個窗口調(diào)用的
 @param completionHandler 確認框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為選擇結(jié)果: YES or NO
 */
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請選擇" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }];

    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"不同意" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }];

    [alert addAction:ok];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];
}

/** 對應(yīng)js的prompt方法
 webView中彈出輸入框時調(diào)用, 兩個按鈕 和 一個輸入框

 @param webView webView description
 @param prompt 提示信息
 @param defaultText 默認提示文本
 @param frame 可用于區(qū)分哪個窗口調(diào)用的
 @param completionHandler 輸入框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為輸入的內(nèi)容
 */
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler {

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請輸入" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];

    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = @"請輸入";
    }];

    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"確定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {

        UITextField *tf = [alert.textFields firstObject];

                completionHandler(tf.text);
    }];

    UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
                completionHandler(defaultText);
    }];

    [alert addAction:ok];
    [alert addAction:cancel];
    [self presentViewController:alert animated:YES completion:nil];
}

6. WKNavigationDelegate

// 決定導(dǎo)航的動作闷畸,通常用于處理跨域的鏈接能否導(dǎo)航涤伐。
// WebKit對跨域進行了安全檢查限制稍浆,不允許跨域,因此我們要對不能跨域的鏈接單獨處理崇摄。
// 但是,對于Safari是允許跨域的,不用這么處理郑叠。
// 這個是決定是否Request
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    //  在發(fā)送請求之前孙技,決定是否跳轉(zhuǎn)
    decisionHandler(WKNavigationActionPolicyAllow);  
}

// 是否接收響應(yīng)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    // 在收到響應(yīng)后产禾,決定是否跳轉(zhuǎn)和發(fā)送請求之前那個允許配套使用
    decisionHandler(WKNavigationResponsePolicyAllow);
}

//用于授權(quán)驗證的API,與AFN牵啦、UIWebView的授權(quán)驗證API是一樣的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{

    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling ,nil);
}

// main frame的導(dǎo)航開始請求時調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{

}

// 當(dāng)main frame接收到服務(wù)重定向時調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    // 接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用
}

// 當(dāng)main frame開始加載數(shù)據(jù)失敗時亚情,會回調(diào)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {

}

// 當(dāng)內(nèi)容開始返回時調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{  
}

//當(dāng)main frame導(dǎo)航完成時,會回調(diào)
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    // 頁面加載完成之后調(diào)用
}

// 當(dāng)main frame最后下載數(shù)據(jù)失敗時哈雏,會回調(diào)
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
}

// 當(dāng)web content處理完成時楞件,會回調(diào)
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
}

7. WKWebsiteDataStore

WKWebsiteDataStore 提供了網(wǎng)站所能使用的數(shù)據(jù)類型,包括 cookies裳瘪,硬盤緩存履因,內(nèi)存緩存活在一些WebSQL的數(shù)據(jù)持久化和本地持久化№镉蓿可通過 WKWebViewConfiguration 類的屬性 websiteDataStore 進行相關(guān)的設(shè)置栅迄。WKWebsiteDataStore 相關(guān)的API也比較簡單:

// 默認的data store
+ (WKWebsiteDataStore *)defaultDataStore;

// 如果為webView設(shè)置了這個data Store,則不會有數(shù)據(jù)緩存被寫入文件
// 當(dāng)需要實現(xiàn)隱私瀏覽的時候皆怕,可使用這個
+ (WKWebsiteDataStore *)nonPersistentDataStore;

// 是否是可緩存數(shù)據(jù)的毅舆,只讀
@property (nonatomic, readonly, getter=isPersistent) BOOL persistent;

// 獲取所有可使用的數(shù)據(jù)類型
+ (NSSet<NSString *> *)allWebsiteDataTypes;

// 查找指定類型的緩存數(shù)據(jù)
// 回調(diào)的值是WKWebsiteDataRecord的集合
- (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;

// 刪除指定的紀(jì)錄
// 這里的參數(shù)是通過上面的方法查找到的WKWebsiteDataRecord實例獲取的
- (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;

// 刪除某時間后修改的某類型的數(shù)據(jù)
- (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;

// 保存的HTTP cookies
@property (nonatomic, readonly) WKHTTPCookieStore *httpCookieStore

PS: 獲取 WKWebsiteDataStore 實例的方法 defaultDataStore/ nonPersistentDataStore ,是有一些區(qū)別的, 具體的可看這篇文章iOS11.3 WKWebView清除cookie所踩的坑

dataTyle
// 硬盤緩存
WKWebsiteDataTypeDiskCache,

// HTML離線web應(yīng)用程序緩存
WKWebsiteDataTypeOfflineWebApplicationCache,

// 內(nèi)存緩存
WKWebsiteDataTypeMemoryCache,

// 本地緩存
WKWebsiteDataTypeLocalStorage,

// cookies
WKWebsiteDataTypeCookies,

// HTML會話存儲
WKWebsiteDataTypeSessionStorage,

//  IndexedDB 數(shù)據(jù)庫
WKWebsiteDataTypeIndexedDBDatabases,

// WebSQL 數(shù)據(jù)庫
WKWebsiteDataTypeWebSQLDatabases

WKWebsiteDataRecord
// 展示名稱, 通常是域名
@property (nonatomic, readonly, copy) NSString *displayName;

// 包含的數(shù)據(jù)類型
@property (nonatomic, readonly, copy) NSSet<NSString *> *dataTypes;

WKHTTPCookieStore

關(guān)于cookie, 從WKWebsiteDataStore 的實例對象的數(shù)屬性httpCookieStore 可獲取一個 WKHTTPCookieStore 的實例對象, 通過此對象, 我們可以對cookie進行相關(guān)的操作, 官方提供的API也不難理解:

/*!  查找所有已存儲的cookie
 */
- (void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;

/*! 保存一個cookie, 保存成功后, 會走一次回調(diào)方法
 */
- (void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;

/*! 刪除一個cookie, 待刪除的cookie對象可通過 'getAllCookies' 方法獲取
 */
- (void)deleteCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;

/*! 添加一個觀察者, 需要遵循協(xié)議 WKHTTPCookieStoreObserver 
當(dāng)cookie發(fā)送變化時, 會通過 WKHTTPCookieStoreObserver 的協(xié)議方法通知該觀察者, 在使用完后需要移除觀察者
 */
- (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer;

/*! 移除觀察者
 */
- (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer;

WKHTTPCookieStoreObserver協(xié)議方法

@protocol WKHTTPCookieStoreObserver <NSObject>
@optional
- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore;
@end

簡單應(yīng)用

刪除指定時間的所有類型數(shù)據(jù)

NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
    NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
        // Done
        NSLog(@"釋放");
    }];

查找刪除

WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
    [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
        for (WKWebsiteDataRecord *record in records) {
            [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                // done
            }];
        }
    }];

查找刪除特定的內(nèi)容

WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
    [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
        for (WKWebsiteDataRecord *record in records) {
            if ([record.displayName isEqualToString:@"baidu"]) {
                [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                    // done
                }];
            }
        }
    }];

新增 cookie

NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{
                                                                NSHTTPCookieName: @"liuhuofeitong",
                                                                NSHTTPCookieValue: @"2018",
                                                                NSHTTPCookieDomain: @"baidu.com",
                                                                NSHTTPCookiePath: @"/",
                                                                NSHTTPCookieExpires :   [NSDate dateWithTimeIntervalSinceNow:60*60*24]
                                                                }];

    if (@available(iOS 11.0, *)) {
        [web.webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:^{

        }];
    } else {
        // Fallback on earlier versions
    }

獲取cookie

if (@available(iOS 11.0, *)) {

        [webView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
            NSLog(@"%@", cookies);
        }] ;

    } else {
        // Fallback on earlier versions
    }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市愈腾,隨后出現(xiàn)的幾起案子憋活,更是在濱河造成了極大的恐慌,老刑警劉巖虱黄,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悦即,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機辜梳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門粱甫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人作瞄,你說我怎么就攤上這事茶宵。” “怎么了宗挥?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵乌庶,是天一觀的道長。 經(jīng)常有香客問我契耿,道長瞒大,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任搪桂,我火速辦了婚禮糠赦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锅棕。我一直安慰自己拙泽,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布裸燎。 她就那樣靜靜地躺著顾瞻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪德绿。 梳的紋絲不亂的頭發(fā)上荷荤,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音移稳,去河邊找鬼蕴纳。 笑死,一個胖子當(dāng)著我的面吹牛个粱,可吹牛的內(nèi)容都是我干的古毛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼都许,長吁一口氣:“原來是場噩夢啊……” “哼稻薇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胶征,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤塞椎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后睛低,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體案狠,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡服傍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了骂铁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吹零。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖从铲,靈堂內(nèi)的尸體忽然破棺而出瘪校,到底是詐尸還是另有隱情澄暮,我是刑警寧澤名段,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站泣懊,受9級特大地震影響伸辟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜馍刮,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一信夫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卡啰,春花似錦静稻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亡脸,卻和暖如春押搪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浅碾。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工大州, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垂谢。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓厦画,卻偏偏與公主長得像,于是被迫代替她去往敵國和親滥朱。 傳聞我的和親對象是個殘疾皇子苛白,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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