7.1.3 Android HttpURLConnection

標(biāo)注:本文為個(gè)人學(xué)習(xí)使用,僅做自己學(xué)習(xí)參考使用贸弥,請(qǐng)勿轉(zhuǎn)載和轉(zhuǎn)發(fā)
2018-09-05: 初稿小压,最近學(xué)習(xí)進(jìn)度好慢,參考博主coder-pig

0. 引言

  1. 前面兩節(jié)主要是一些概念性的東西羽资,Http的協(xié)議以及協(xié)議頭等,有時(shí)間還是需要深入研究一下
  2. 本節(jié)學(xué)習(xí)的是Http的請(qǐng)求方式之一:HttpURLConnection遵班,除了這種還有一種HttpClient屠升,下一節(jié)來描述
  3. HttpURLConnection請(qǐng)求復(fù)雜,使用起來比較麻煩狭郑,在4.4版本中HttpURLConnection已經(jīng)被替換稱OkHttp了腹暖!但在實(shí)際開發(fā)中并不會(huì)用HttpURLConnection和HttpClient,因?yàn)閯e人已經(jīng)封裝好的第三方網(wǎng)絡(luò)請(qǐng)求框架翰萨,諸如Volley脏答、android-async-http,loopj等,因?yàn)榫W(wǎng)絡(luò)操作涉及到異步以及多線程以蕴,自己動(dòng)手實(shí)現(xiàn)的話糙麦,比較麻煩,所以實(shí)際開發(fā)的過程還是直接使用的第三方丛肮,但是第三方畢竟也是在這些的基礎(chǔ)上架構(gòu)起來的赡磅,所以還是得從頭來。

1. HttpURLConnection介紹

  • 一種多用途宝与、輕量級(jí)的HTTP客戶端焚廊,使用它來進(jìn)行Http操作可以適用于大多數(shù)的應(yīng)用程序,雖然HttpURLConnection的API提供比較簡(jiǎn)單习劫,但是同時(shí)這個(gè)也使我們可以更加容易的去使用和擴(kuò)展它咆瘟,集成至URLConnection,抽象類诽里,無法直接實(shí)例化對(duì)象袒餐。通過調(diào)用openCollection()方法獲得對(duì)象實(shí)例,默認(rèn)是帶gzip壓縮的谤狡;

2. HttpURLConnection的使用步驟

  • 使用HttpURLConnection的步驟如下:
  1. 創(chuàng)建一個(gè)URL對(duì)象:
    URL url = new URL(http://www.baidu.com);

  2. 調(diào)用URL對(duì)象的openConnection()來獲取HttpURLConnect對(duì)象實(shí)例:
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();

  3. 設(shè)置Http請(qǐng)求使用的方法灸眼,GET活著POST,活著其他請(qǐng)求方式比如:PUT
    conn.setRequestMethod("GET");

  4. 設(shè)置連接超時(shí)墓懂,讀取超時(shí)的毫秒書焰宣,以及服務(wù)器希望得到的一些消息頭
    conn.setConnectTimeout(6 * 1000);
    conn.setReadTimeout(6 * 1000)

  5. 調(diào)用getInputStream()方法獲得服務(wù)器返回的輸入流,然后輸入流進(jìn)行讀取了
    InputStream in = conn.getInputStream();

  6. 最后調(diào)用disconnect()方法將Http連接關(guān)掉
    conn.disconnect();

備注
  1. 除了上面這些外捕仔,有時(shí)后還需要對(duì)相應(yīng)碼進(jìn)行判斷匕积,比如200,if (conn.getResponseCode() != 200) 然后進(jìn)行一些處理榜跌,
  2. 還有有時(shí)我們并不需要傳遞什么參數(shù)闪唆,而是直接去訪問一個(gè)界面,就可以直接用 final InputStream in = new URL("url").openStream(); 然后直接讀流钓葫,不過這個(gè)方法適合于直接訪問頁(yè)面的情況悄蕾,底層實(shí)現(xiàn)其實(shí)也是return openConnection.getInputStream(), 而且我們還不能設(shè)置一些請(qǐng)求頭的東西,看情況定瓤逼。

3. HttpURLConnection使用示例

  • 這里我們主要針對(duì)GET和POST請(qǐng)求寫兩種不同的示例笼吟,我們可以conn.getInputStream()獲取到的是一個(gè)流库物,所以我們需要寫一個(gè)類霸旗,將流轉(zhuǎn)化為二進(jìn)制數(shù)組,定義為工具類如下:
    StreamTool.java
public class StreamTool {
    //從流中讀取數(shù)據(jù)
    public static byte[] read(InputStream inStream) throws Exception{
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = inStream.read(buffer)) != -1)
        {
            outStream.write(buffer,0,len);
        }
        inStream.close();
        return outStream.toByteArray();
    }
}

3.1 HttpURLConnection發(fā)送GET請(qǐng)求代碼示例

運(yùn)行效果圖


核心部分代碼
布局:activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/txtMenu"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="#4EA9E9"
        android:clickable="true"
        android:gravity="center"
        android:text="長(zhǎng)按我戚揭,加載菜單"
        android:textSize="20sp" />

    <ImageView
        android:id="@+id/imgPic"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

    <ScrollView
        android:id="@+id/scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone">

        <TextView
            android:id="@+id/txtshow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </ScrollView>

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

獲取數(shù)據(jù)類:GetData.java

public class GetData {
    // 定義一個(gè)獲取網(wǎng)絡(luò)圖片數(shù)據(jù)的方法:
    public static byte[] getImage(String path) throws Exception {
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 設(shè)置連接超時(shí)為5秒
        conn.setConnectTimeout(5000);
        // 設(shè)置請(qǐng)求類型為Get類型
        conn.setRequestMethod("GET");
        // 判斷請(qǐng)求Url是否成功
        if (conn.getResponseCode() != 200) {
            throw new RuntimeException("請(qǐng)求url失敗");
        }
        InputStream inStream = conn.getInputStream();
        byte[] bt = StreamTool.read(inStream);
        inStream.close();
        return bt;
    }

    // 獲取網(wǎng)頁(yè)的html源代碼
    public static String getHtml(String path) throws Exception {
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
        if (conn.getResponseCode() == 200) {
            InputStream in = conn.getInputStream();
            byte[] data = StreamTool.read(in);
            String html = new String(data, "UTF-8");
            return html;
        }
        return null;
    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private TextView txtMenu, txtshow;
    private ImageView imgPic;
    private WebView webView;
    private ScrollView scroll;
    private Bitmap bitmap;
    private String detail = "";
    private boolean flag = false;
    private final static String PIC_URL = "http://ww2.sinaimg.cn/large/7a8aed7bgw1evshgr5z3oj20hs0qo0vq.jpg";
    private final static String HTML_URL = "http://www.baidu.com";

    // 用于刷新界面
    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case 0x001:
                    hideAllWidget();
                    imgPic.setVisibility(View.VISIBLE);
                    imgPic.setImageBitmap(bitmap);
                    Toast.makeText(MainActivity.this, "圖片加載完畢", Toast.LENGTH_SHORT).show();
                    break;
                case 0x002:
                    hideAllWidget();
                    scroll.setVisibility(View.VISIBLE);
                    txtshow.setText(detail);
                    Toast.makeText(MainActivity.this, "HTML代碼加載完畢", Toast.LENGTH_SHORT).show();
                    break;
                case 0x003:
                    hideAllWidget();
                    webView.setVisibility(View.VISIBLE);
                    webView.loadDataWithBaseURL("", detail, "text/html", "UTF-8", "");
                    Toast.makeText(MainActivity.this, "網(wǎng)頁(yè)加載完畢", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }

        ;
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setViews();
    }

    private void setViews() {
        txtMenu = (TextView) findViewById(R.id.txtMenu);
        txtshow = (TextView) findViewById(R.id.txtshow);
        imgPic = (ImageView) findViewById(R.id.imgPic);
        webView = (WebView) findViewById(R.id.webView);
        scroll = (ScrollView) findViewById(R.id.scroll);
        registerForContextMenu(txtMenu);
    }

    // 定義一個(gè)隱藏所有控件的方法:
    private void hideAllWidget() {
        imgPic.setVisibility(View.GONE);
        scroll.setVisibility(View.GONE);
        webView.setVisibility(View.GONE);
    }

    @Override
    // 重寫上下文菜單的創(chuàng)建方法
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        MenuInflater inflator = new MenuInflater(this);
        inflator.inflate(R.menu.menus, menu);
        super.onCreateContextMenu(menu, v, menuInfo);
    }

    // 上下文菜單被點(diǎn)擊是觸發(fā)該方法
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.one:
                new Thread() {
                    public void run() {
                        try {
                            byte[] data = GetData.getImage(PIC_URL);
                            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        handler.sendEmptyMessage(0x001);
                    }

                    ;
                }.start();
                break;
            case R.id.two:
                new Thread() {
                    public void run() {
                        try {
                            detail = GetData.getHtml(HTML_URL);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        handler.sendEmptyMessage(0x002);
                    };
                }.start();
                break;
            case R.id.three:
                if (detail.equals("")) {
                    Toast.makeText(MainActivity.this, "先請(qǐng)求HTML先嘛~", Toast.LENGTH_SHORT).show();
                } else {
                    handler.sendEmptyMessage(0x003);
                }
                break;
        }
        return true;
    }
}

最后別忘了加上聯(lián)網(wǎng)權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
注意事項(xiàng)

  1. 用handler的原因就不用講了吧~ 另外我們加載html代碼的使用的是webView的loadDataWithBaseURL而非LoadData诱告, 如果用LoadData又要去糾結(jié)中文亂碼的問題,so…用loadDataWithBaseURL就可以不用糾結(jié)那么多了 另外有些頁(yè)面可能需要我們提交一些參數(shù)民晒,比如賬號(hào)密碼:我們只需把對(duì)應(yīng)參數(shù)拼接到url尾部即可精居,比如: http://192.168.191.1:8080/ComentServer/LoginServlet?passwd=123&name=Jack 然后服務(wù)端getParamater("passwd")這樣就可以獲得相應(yīng)的參數(shù)了锄禽,我們請(qǐng)求時(shí)這些東西都會(huì)看得清清楚楚 ,所以說GET方式并不安全靴姿!另外還有一點(diǎn)要注意的就是Android從4.0開始就不允許在非UI線程中進(jìn)行UI操作!
3.2 HttpURLConnection發(fā)送POST請(qǐng)求代碼示例
  • 有GET自然有POST沃但,通過openConnection獲取到的HttpURLConnection默認(rèn)是進(jìn)行Get請(qǐng)求的,所以我們使用POST提交數(shù)據(jù)佛吓,應(yīng)提前設(shè)置好相關(guān)的參數(shù):conn.setRequestMethod("POST");
  • 設(shè)置conn.setDoOutput(true);conn.setDoInput(true);允許輸入宵晚、輸出。
  • 設(shè)置 conn.setUseCaches(false); POST方法不能緩存维雇,要手動(dòng)設(shè)置為false淤刃,具體實(shí)現(xiàn)看代碼:
    運(yùn)行效果圖:

    核心代碼
    PostUtils.java
public class PostUtils {
    public static String LOGIN_URL = "http://172.16.2.54:8080/HttpTest/ServletForPost";
    public static String LoginByPost(String number,String passwd)
    {
        String msg = "";
        try{
            HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection();
            //設(shè)置請(qǐng)求方式,請(qǐng)求超時(shí)信息
            conn.setRequestMethod("POST");
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(5000);
            //設(shè)置運(yùn)行輸入,輸出:
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //Post方式不能緩存,需手動(dòng)設(shè)置為false
            conn.setUseCaches(false);
            //我們請(qǐng)求的數(shù)據(jù):
            String data = "passwd="+ URLEncoder.encode(passwd, "UTF-8")+
                    "&number="+ URLEncoder.encode(number, "UTF-8");
            //這里可以寫一些請(qǐng)求頭的東東...
            //獲取輸出流
            OutputStream out = conn.getOutputStream();
            out.write(data.getBytes());
            out.flush();
             if (conn.getResponseCode() == 200) {  
                    // 獲取響應(yīng)的輸入流對(duì)象  
                    InputStream is = conn.getInputStream();  
                    // 創(chuàng)建字節(jié)輸出流對(duì)象  
                    ByteArrayOutputStream message = new ByteArrayOutputStream();  
                    // 定義讀取的長(zhǎng)度  
                    int len = 0;  
                    // 定義緩沖區(qū)  
                    byte buffer[] = new byte[1024];  
                    // 按照緩沖區(qū)的大小,循環(huán)讀取  
                    while ((len = is.read(buffer)) != -1) {  
                        // 根據(jù)讀取的長(zhǎng)度寫入到os對(duì)象中  
                        message.write(buffer, 0, len);  
                    }  
                    // 釋放資源  
                    is.close();  
                    message.close();  
                    // 返回字符串  
                    msg = new String(message.toByteArray());  
                    return msg;
             }
        }catch(Exception e){e.printStackTrace();}
        return msg;
    }
}

4. Cookie問題的處理

  • 首先要理解兩個(gè)概念:Session和Cookie吱型,Cookie只是Session機(jī)制的一種常用形式逸贾,我們也可以使用其他方式來作為客戶端的一個(gè)唯一表示,這個(gè)由服務(wù)器決定津滞,唯一能夠證明一個(gè)客戶端的標(biāo)識(shí)就好铝侵!
  • 除了這種方式外,我們還可以使用URL重寫方法來實(shí)現(xiàn)据沈,所以不能說Session就是Cookie
  • 下面有一個(gè)例子可以理解Cookie:原博主通過賬號(hào)登陸學(xué)校的教務(wù)系統(tǒng)哟沫,然后訪問課表信息成功,然后如果你是用的Chrome锌介,按F12進(jìn)入來法模式:來到Resources界面就可以看到我們的Cookies




    點(diǎn)擊后我們可以看到里面保存的內(nèi)容嗜诀,由名稱;值孔祸;cookie所在的域(domain)隆敢; cookie所在的目錄(path)Asp.net默認(rèn)為/即根目錄;過期時(shí)間崔慧;Cookie大蟹餍:
    我們可以看到請(qǐng)求頭中有一個(gè)Cookie的字段:



    恩呢,現(xiàn)在我們把Cookie清掉(或者等幾分鐘)惶室,然后再訪問下述鏈接:

    這時(shí)候温自,頁(yè)面竟然自動(dòng)跳回登陸頁(yè)面了!當(dāng)然一些其他的網(wǎng)站可能會(huì)彈出一個(gè)對(duì)話框說 "登陸超時(shí)"之類的東西皇钞!

小結(jié)

  1. Http請(qǐng)求登陸的一個(gè)簡(jiǎn)單的流程悼泌,一般是登陸的時(shí)候,服務(wù)器通過Set-Cookie相應(yīng)頭夹界,返回一個(gè)Cookie馆里,瀏覽器默認(rèn)保存這個(gè)Cookie,后續(xù)訪問相關(guān)頁(yè)面的時(shí)候會(huì)帶上這個(gè)Cookie,通過Cookie請(qǐng)求頭來完成訪問鸠踪,如果沒Cookie活著Cookie過期丙者,就是提示用戶沒登陸,登陸超時(shí)营密,訪問需要登陸之類的信息械媒!
  2. 而我們使用HttpClient和HttpURLConnection其實(shí)也就是模擬這一個(gè)流程,登陸后拿到cookie拿著它去發(fā)送請(qǐng)求:關(guān)鍵代碼如下:
    獲得Cookie
    conn.getHeaderField("Set-Cookie");
    請(qǐng)求時(shí)帶上Cookie
    conn.setRequestProperty("Cookie", cookie);
  3. 另外评汰,除了這種設(shè)置請(qǐng)求頭的方式外滥沫,還可以用另一種折衷的方式:
    URL重寫
    就是在原先請(qǐng)求連接的基礎(chǔ)上,加上一個(gè)…&sessionid=xxxxx的參數(shù)键俱,然后由服務(wù)器來解析兰绣、判斷!GET可以這么寫编振,而Post的寫法如下:

5. 使用HttpURLConnection發(fā)送的PUT請(qǐng)求

  • Put請(qǐng)求對(duì)于很多朋友來說可能有點(diǎn)陌生缀辩,畢竟平時(shí)接觸的不多,這個(gè)是需要在POST的基礎(chǔ)上改一些東西就可以使用了踪央!而HttpClient也給我們提供了一個(gè)HttpPut的API臀玄,原博主實(shí)現(xiàn)的代碼如下
public static String LoginByPut(Context mContext, String mobile, String password, int from, 
          String devid,String version_name, int remember_me) {
    String resp = "";
    try {
        HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection();
        conn.setRequestMethod("PUT");
        conn.setReadTimeout(5000);
        conn.setConnectTimeout(5000);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);

        String data = "mobile=" + mobile + "&password=" + password + "&from=" + from + "&devid=" + "devid"
                + "&version_name=" + "version_name" + "&remember_me=" + remember_me;
        ;
        // 獲取輸出流:
        OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
        writer.write(data);
        writer.flush();
        writer.close();

        // 獲取相應(yīng)流對(duì)象:
        InputStream in = conn.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        StringBuilder response = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null)
            response.append(line);
        SPUtils.put(mContext, "session", conn.getHeaderField("Set-Cookie"));
        // 資源釋放:
        in.close();
        // 返回字符串
        Log.e("HEHE", response.toString());
        return response.toString();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}
?著作權(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
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至湿滓,卻和暖如春滴须,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叽奥。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工扔水, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人朝氓。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓铭污,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親膀篮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嘹狞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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