結(jié)合Retrofit使用post請求訪問WebService

背景

沒啥背景,實(shí)在是受夠了ksoap2這個(gè)jar包,而公司服務(wù)端是基于c#語言的.net開發(fā),不懂他們的技術(shù),而他們好像只能通過WebService與我們Android端進(jìn)行數(shù)據(jù)交互(如果有前輩知道別的技術(shù),望指點(diǎn)!謝謝~).
Retrofit作為當(dāng)前最火的網(wǎng)絡(luò)請求框架.如果不去學(xué),永遠(yuǎn)不會用.因此,我想把這個(gè)框架引入到公司項(xiàng)目里邊來,把ksoap2替換掉,用Retrofit來訪問WebService
既然有了想法,就要去做.網(wǎng)上關(guān)于Retrofit的講解一大堆,奈何關(guān)于使用Retrofit去訪問WebService的文章少之又少,并且各位前輩的經(jīng)驗(yàn)都是基于自己公司的業(yè)務(wù)情況總結(jié)出來的,和我現(xiàn)在的情況多少有些出入,因此,我把各位前輩的經(jīng)驗(yàn)綜合起來,寫了這篇文章,一來,總結(jié)下經(jīng)驗(yàn);二來,希望以后有朋友再遇到這種問題的時(shí)候,能少走些彎路.
首先將前輩的鏈接奉上:
Retrofit2+Okhttp3+Rxjava通過SOAP協(xié)議請求WebService
在WebService中使用Retrofit+RxJava
轉(zhuǎn)載----使用 Retrofit 操作 SOAP Web Service
也正是有了各位前輩總結(jié)的經(jīng)驗(yàn),才有了我今天這篇文章,向各位前輩致敬!
第一次寫文章,如有不妥之處,希望各位前輩指出!

工具準(zhǔn)備

  • FireFox(火狐瀏覽器)
  • RESTClient(火狐瀏覽器調(diào)試插件)

這里,我使用的是火狐瀏覽器+RESTClient去調(diào)試http請求.
因?yàn)槊總€(gè)公司定義的格式可能會不一樣.我們在封裝以及分析數(shù)據(jù)的時(shí)候需要與之對應(yīng).所以我們?nèi)フ{(diào)試分析http請求.弄清每次請求及響應(yīng)的格式.
關(guān)于soap,WebService以及http,各位前輩已經(jīng)分析的很透徹了,這里我就不多說了,如果想了解的話,可以去看下前輩總結(jié)的文章.下面,咱們正式開始.

開工

RESTClient界面

在火狐瀏覽器中安裝好RESTClient插件后,將其打開,界面應(yīng)該會和圖1類似,是空的,沒有任何數(shù)據(jù).為了方便分析,我又截取了圖2,明顯是一次成功請求后的界面,我先用圖2分析下整個(gè)界面,然后再告訴你,這些數(shù)據(jù)都是怎么填上去的.


圖1

在圖2中,我把一次請求分為了兩個(gè)部分,分別用綠線和藍(lán)線框了起來.其中,綠線內(nèi)是本次http請求發(fā)送的數(shù)據(jù),也就是我們作為Android端需要封裝的數(shù)據(jù),藍(lán)線內(nèi),就是本次請求服務(wù)端返回給我們的數(shù)據(jù),也就是我們需要解析的數(shù)據(jù).
我們先來分析下我們需要發(fā)送的數(shù)據(jù),如圖所示,我用紅線標(biāo)出了4個(gè)位置,它們分別表示什么意思呢?

  1. Method: 表示這次請求的請求方式,一般常用的有g(shù)et和post,這里當(dāng)然選post(因?yàn)閃ebService就是post請求的一種)
  2. URL: 表示這次請求的地址
  3. Headers: 請求頭
  4. Body: 請求體


    圖2

調(diào)試http請求

介紹完了RESTClient的界面,下面就正式開始http調(diào)試.
所謂調(diào)試,無非就是模擬一次http請求,我們把需要發(fā)送給服務(wù)端的數(shù)據(jù)填到Request(圖2綠線內(nèi))中,點(diǎn)擊send按鈕,然后Response(圖2藍(lán)線內(nèi))中顯示服務(wù)端返回給我們的數(shù)據(jù).我們就是分析這堆數(shù)據(jù)而已.那么問題來了,Request中的這些數(shù)據(jù),是從哪來的?
我另外打開了一個(gè)瀏覽器頁面,在地址欄中輸入想要調(diào)試的地址,如圖3所示,為了方便,我讓服務(wù)端同事把服務(wù)器部署到我的電腦上了,所以看到的地址ip是192.168.191.1,
剛剛在地址欄輸入的就是這次要調(diào)試的地址,因此,我把他填入到了RESTClient的URL中.
既然url確定了,那請求頭(Headers)和請求體(Body)又該填什么呢?別急,接著往下看.


圖3

這次我要調(diào)試的就是圖3中紅線內(nèi)的接口.名為AssetMaterialInfo,點(diǎn)擊這個(gè)接口,打開的界面如圖4所示.圖中有SOAP1.2請求和響應(yīng)示例,紅線標(biāo)注的是占位符,在模擬數(shù)據(jù)的時(shí)候需要將其替換為真實(shí)數(shù)據(jù).
而我們所需要的請求頭(Headers)和請求體(Body)就藏在請求示例中.為了方便分析,我單獨(dú)將請求示例截取了圖片,放在下邊,也就是圖5.


圖4

圖5中,紅線內(nèi)這兩行內(nèi)容,就是請求頭,藍(lán)線內(nèi)的就是請求體.請求體很簡單,我們只需要將藍(lán)線中內(nèi)容復(fù)制到RESTClient界面的Body中,然后把占位符替換掉就可以了(如圖2所示),請求頭怎么弄呢?
圖5

關(guān)于在RESTClient中添加請求頭,我舉一個(gè)例子(圖6),大家就都明白了.
在RESTClient界面中,點(diǎn)擊頂部Headers,再點(diǎn)擊CustomHeader,會打開圖7這個(gè)界面.
圖6

在圖7所示界面,Name欄中填入Content-Type,Value欄中填入text/xml; charset=utf-8,然后點(diǎn)擊Okay,我們就將一個(gè)請求頭添加到本次請求中了.

同理,將第二個(gè)請求頭也添加進(jìn)來.我就不再演示了.


圖7

這樣,我們就將本次請求需要攜帶的數(shù)據(jù)都添加進(jìn)來了.點(diǎn)擊SEND按鈕,就完成了本次請求.

開始寫Demo

拿到了http請求的數(shù)據(jù),我們就可以根據(jù)數(shù)據(jù)去寫我們的例子程序了,在我們Android端通過Retrofit使用post請求去訪問剛剛我們調(diào)試的WebService接口.
首先,在看下邊代碼之前,你要保證自己已經(jīng)基本了解了Retrofit框架.關(guān)于Retrofit不會介紹太多,因?yàn)樗皇潜酒恼碌闹攸c(diǎn).


導(dǎo)包
compile'com.squareup.retrofit2:retrofit:2.0.1'
compile('com.squareup.retrofit2:converter-simplexml:2.1.0') {
        exclude group:'xpp3',module:'xpp3'
        exclude group:'stax',module:'stax-api'
        exclude group:'stax',module:'stax'
    }
請求體實(shí)例

對應(yīng)圖2中Request的Body部分,需要寫三個(gè)類來作為請求體.分別對應(yīng)請求體中的三個(gè)節(jié)點(diǎn)
根據(jù)由外到內(nèi)的層級關(guān)系,它們分別是(類名 <---> 節(jié)點(diǎn)名):

  • RequestEnvelope <---> soap:Envelope
  • RequestBody <---> soap:Body
  • RequestModel <---> AssetMaterialInfo

下面,我詳細(xì)介紹下這三個(gè)類.

@Root(name = "soap:Envelope")
@NamespaceList({
       @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),
        @Namespace(reference = "http://www.w3.org/2001/XMLSchema", prefix = "xsd"),
        @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soap")
})
public class RequestEnvelope {
      @Element(name = "soap:Body", required = false)
      public RequestBody body;
}

RequestEnvelope類中,Root注解用來指定節(jié)點(diǎn)名稱,NamespaceList用來指定多個(gè)命名空間,Element用來指定子節(jié)點(diǎn)名稱.

@Root(name = "soap:Body", strict = false)
public class RequestBody {
    @Element(name = "AssetMaterialInfo", required = false)
    public RequestModel AssetMaterialInfo;
}

同理,RequestBody類中,Root注解用來指定節(jié)點(diǎn)名稱,Element注解用來指定子節(jié)點(diǎn)名稱,因?yàn)楫?dāng)前節(jié)點(diǎn)沒有命名空間,因此不需要NamespaceList注解

@Root(name = "AssetMaterialInfo", strict = false)
@Namespace(reference = "http://tempuri.org/")
public class RequestModel {
    @Element(name = "date", required = false)
    public String date;
    @Element(name = "page", required = false)
    public int page;
}

RequestModel類中,Root注解用來指定節(jié)點(diǎn)名稱,因?yàn)楫?dāng)前節(jié)點(diǎn)只有一個(gè)命名空間,因此使用Namespace注解而不是NamespaceList.Element注解指定子節(jié)點(diǎn)名稱,因?yàn)楫?dāng)前節(jié)點(diǎn)有兩個(gè)子節(jié)點(diǎn),因此這個(gè)類有兩個(gè)參數(shù),且都被Element注解修飾.
到這里,關(guān)于請求體的實(shí)體類,就創(chuàng)建好了.下面我們準(zhǔn)備響應(yīng)體的實(shí)體類.

響應(yīng)體實(shí)例

跟請求體類似,我們也是需要根據(jù)服務(wù)器響應(yīng)的xml文件格式來創(chuàng)建實(shí)體類.
通過分析xml格式,我們也需要創(chuàng)建三個(gè)實(shí)體類來分別對應(yīng)三個(gè)節(jié)點(diǎn).它們的對應(yīng)關(guān)系如下(類名 <---> 節(jié)點(diǎn)名):

  • AssetResponseEnvelope <---> soap:Envelope
  • AssetResponseBody <---> Body
  • AssetResponseModel <---> AssetMaterialInfoResponse

為了與系統(tǒng)類名區(qū)分開,我為這三個(gè)類名添加了Asset前綴.

@Root(name = "soap:Envelope")
@NamespaceList({
        @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance/", prefix = "xsi"),
        @Namespace(reference = "http://www.w3.org/2001/XMLSchema/", prefix = "xsd"),
        @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soap")
})
public class AssetResponseEnvelope {
    @Element(name = "Body", required = false)
    public AssetResponseBody responseBody;
}
@Root(name = "Body", strict = false)
public class AssetResponseBody {
    @Element(name = "AssetMaterialInfoResponse", required = false)
    public AssetResponseModel responseModel;
}
@Root(name = "AssetMaterialInfoResponse")
public class AssetResponseModel {
    @Attribute(name = "xmlns", empty = "http://tempuri.org/", required = false)
    public String nameSpace;
    @Element(name = "AssetMaterialInfoResult")
    public String result;
}

因?yàn)榕c請求體類似,這里我也不用對這三個(gè)類做過多介紹.不過,值得一提的是,在AssetResponseModel類中,我沒有再用Namespace注解去指定命名空間,而是添加了一個(gè)成員變量,用Attribute注解將其指定.需要注意的是,再用Attribute注解指定的時(shí)候,name,empty,required三個(gè)屬性,缺一不可.
下面奉上SimpleXml的地址,上面有關(guān)于xml與實(shí)體類之間綁定的詳細(xì)介紹:
SimpleXml

創(chuàng)建Interface

三個(gè)請求類和三個(gè)響應(yīng)類創(chuàng)建好了,下一步就是創(chuàng)建請求接口Interface.(不明白為啥要這樣做的,可以去復(fù)習(xí)下Retrofit,哦不對,是預(yù)習(xí)~)
直接上代碼:

public interface ApiStore {
    @Headers({
            "Content-Type: text/xml; charset=utf-8",
            "SOAPAction: http://tempuri.org/AssetMaterialInfo"
    })
    @POST("GetService.asmx")
    Call<AssetResponseEnvelope> getAssetInfo(@Body RequestEnvelope requestEnvelope);
}

在請求接口中,我們定義了一個(gè)函數(shù),將其請求方式制定為post請求,并且為其添加了請求頭.這個(gè)函數(shù)接收一個(gè)RequestEnvelope參數(shù).

使用Retrofit請求

所有初始化工作都做完之后,使用Retrofit去請求WebService這塊還是蠻簡單的.
不得不說,Retrofit確實(shí)很強(qiáng)大,整個(gè)請求流程下來,結(jié)構(gòu)清晰明了,一點(diǎn)都不拖泥帶水.如果再結(jié)合上RxJava,豈不是更爽?

    /**
     * 去服務(wù)端請求數(shù)據(jù)
     */
    private void request() {
        String url = "http://192.168.191.1:2000/";
        // 初始化Retrofit
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(SimpleXmlConverterFactory.create()) // 返回?cái)?shù)據(jù)為xml,因此要加入xml解析
                .build();
        ApiStore apiStore = retrofit.create(ApiStore.class);
        // 初始化請求體
        RequestModel requestModel = new RequestModel("2012-01-01", 0);
        RequestBody requestBody = new RequestBody(requestModel);
        RequestEnvelope requestEnvelope = new RequestEnvelope(requestBody);
        // 開始請求
        Call<AssetResponseEnvelope> call = apiStore.getAssetInfo(requestEnvelope);
        call.enqueue(new Callback<AssetResponseEnvelope>() {
            @Override
            public void onResponse(Call<AssetResponseEnvelope> call, Response<AssetResponseEnvelope> response) {
                // 處理響應(yīng)體
                AssetResponseEnvelope responseEnvelope = response.body();
                if (responseEnvelope == null) {
                    Log.d(TAG, "onResponse: responseEnvelope == null");
                    return;
                }
                AssetResponseBody responseBody = responseEnvelope.responseBody;
                if (responseBody == null) {
                    Log.d(TAG, "onResponse: responseBody == null");
                    return;
                }
                AssetResponseModel responseModel = responseBody.responseModel;
                if (responseModel == null) {
                    Log.d(TAG, "onResponse: responseModel == null");
                    return;
                }
                String result = responseModel.result;
                Log.d(TAG, "onResponse: result : " + result);
//                showResult(result);
            }

            @Override
            public void onFailure(Call<AssetResponseEnvelope> call, Throwable t) {

            }
        });
    }

總結(jié)

到此為止,一個(gè)基于Retrofit使用post請求訪問WebService的小Demo就算寫完了.我已經(jīng)將代碼提交到了GitHub,感興趣的同學(xué)可以去看一下,很簡單的小程序.
項(xiàng)目地址
有不明白的同學(xué),歡迎向我提出問題,我們共同學(xué)習(xí).
第一次寫文章,還望各位前輩多多批評指正,不勝感激!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子友绝,更是在濱河造成了極大的恐慌馍忽,老刑警劉巖简珠,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件外盯,死亡現(xiàn)場離奇詭異,居然都是意外死亡挺尿,警方通過查閱死者的電腦和手機(jī)奏黑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來编矾,“玉大人熟史,你說我怎么就攤上這事≌危” “怎么了蹂匹?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凹蜈。 經(jīng)常有香客問我限寞,道長,這世上最難降的妖魔是什么踪区? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮吊骤,結(jié)果婚禮上缎岗,老公的妹妹穿的比我還像新娘。我一直安慰自己白粉,他們只是感情好传泊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布鼠渺。 她就那樣靜靜地躺著,像睡著了一般眷细。 火紅的嫁衣襯著肌膚如雪拦盹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天溪椎,我揣著相機(jī)與錄音普舆,去河邊找鬼。 笑死校读,一個(gè)胖子當(dāng)著我的面吹牛沼侣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播歉秫,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛾洛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雁芙?” 一聲冷哼從身側(cè)響起轧膘,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兔甘,沒想到半個(gè)月后谎碍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裂明,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年椿浓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闽晦。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扳碍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仙蛉,到底是詐尸還是另有隱情笋敞,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布荠瘪,位于F島的核電站夯巷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏哀墓。R本人自食惡果不足惜趁餐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篮绰。 院中可真熱鬧后雷,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至候学,卻和暖如春藕筋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梳码。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工隐圾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人边翁。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓翎承,卻偏偏與公主長得像,于是被迫代替她去往敵國和親符匾。 傳聞我的和親對象是個(gè)殘疾皇子叨咖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評論 25 707
  • 整體Retrofit內(nèi)容如下: 1、Retrofit解析1之前哨站——理解RESTful2啊胶、Retrofit解析2...
    隔壁老李頭閱讀 3,987評論 8 19
  • 1.下載:打開http://www.xmindchina.net/點(diǎn)擊免費(fèi)下載 2.下載完成雙擊xmind-8-w...
    三玉米閱讀 1,598評論 0 4
  • 好吧甸各,那就去上咯~BUT!我們剛到實(shí)測地點(diǎn)-翰林苑焰坪,又被告知老師一次只帶一個(gè)班趣倾,今天三班上課,我們是明天某饰。歐儒恋!然后...
    許小小麗閱讀 269評論 1 2
  • 快速讀完《舒克貝塔傳》。 童話故事不僅僅是寫給小孩子看的黔漂。作者借著鼠口道出的诫尽,是人世間常見的現(xiàn)象。自然而然地炬守,讓我...
    夏又幽閱讀 491評論 0 0