结合Retrofit使用post请求访问WebService

背景

没啥背景,实在是受够了ksoap2这个jar包,而公司服务端是基于c#语言的.net开发,不懂他们的技术,而他们好像只能通过WebService与我们Android端进行数据交互(如果有前辈知道别的技术,望指点!谢谢~).
Retrofit作为当前最火的网络请求框架.如果不去学,永远不会用.因此,我想把这个框架引入到公司项目里边来,把ksoap2替换掉,用Retrofit来访问WebService
既然有了想法,就要去做.网上关于Retrofit的讲解一大堆,奈何关于使用Retrofit去访问WebService的文章少之又少,并且各位前辈的经验都是基于自己公司的业务情况总结出来的,和我现在的情况多少有些出入,因此,我把各位前辈的经验综合起来,写了这篇文章,一来,总结下经验;二来,希望以后有朋友再遇到这种问题的时候,能少走些弯路.
首先将前辈的链接奉上:
Retrofit2+Okhttp3+Rxjava通过SOAP协议请求WebService
在WebService中使用Retrofit+RxJava
转载----使用 Retrofit 操作 SOAP Web Service
也正是有了各位前辈总结的经验,才有了我今天这篇文章,向各位前辈致敬!
第一次写文章,如有不妥之处,希望各位前辈指出!

工具准备

  • FireFox(火狐浏览器)
  • RESTClient(火狐浏览器调试插件)

这里,我使用的是火狐浏览器+RESTClient去调试http请求.
因为每个公司定义的格式可能会不一样.我们在封装以及分析数据的时候需要与之对应.所以我们去调试分析http请求.弄清每次请求及响应的格式.
关于soap,WebService以及http,各位前辈已经分析的很透彻了,这里我就不多说了,如果想了解的话,可以去看下前辈总结的文章.下面,咱们正式开始.

开工

RESTClient界面

在火狐浏览器中安装好RESTClient插件后,将其打开,界面应该会和图1类似,是空的,没有任何数据.为了方便分析,我又截取了图2,明显是一次成功请求后的界面,我先用图2分析下整个界面,然后再告诉你,这些数据都是怎么填上去的.


图1

在图2中,我把一次请求分为了两个部分,分别用绿线和蓝线框了起来.其中,绿线内是本次http请求发送的数据,也就是我们作为Android端需要封装的数据,蓝线内,就是本次请求服务端返回给我们的数据,也就是我们需要解析的数据.
我们先来分析下我们需要发送的数据,如图所示,我用红线标出了4个位置,它们分别表示什么意思呢?

  1. Method: 表示这次请求的请求方式,一般常用的有get和post,这里当然选post(因为WebService就是post请求的一种)
  2. URL: 表示这次请求的地址
  3. Headers: 请求头
  4. Body: 请求体


    图2

调试http请求

介绍完了RESTClient的界面,下面就正式开始http调试.
所谓调试,无非就是模拟一次http请求,我们把需要发送给服务端的数据填到Request(图2绿线内)中,点击send按钮,然后Response(图2蓝线内)中显示服务端返回给我们的数据.我们就是分析这堆数据而已.那么问题来了,Request中的这些数据,是从哪来的?
我另外打开了一个浏览器页面,在地址栏中输入想要调试的地址,如图3所示,为了方便,我让服务端同事把服务器部署到我的电脑上了,所以看到的地址ip是192.168.191.1,
刚刚在地址栏输入的就是这次要调试的地址,因此,我把他填入到了RESTClient的URL中.
既然url确定了,那请求头(Headers)和请求体(Body)又该填什么呢?别急,接着往下看.


图3

这次我要调试的就是图3中红线内的接口.名为AssetMaterialInfo,点击这个接口,打开的界面如图4所示.图中有SOAP1.2请求和响应示例,红线标注的是占位符,在模拟数据的时候需要将其替换为真实数据.
而我们所需要的请求头(Headers)和请求体(Body)就藏在请求示例中.为了方便分析,我单独将请求示例截取了图片,放在下边,也就是图5.


图4

图5中,红线内这两行内容,就是请求头,蓝线内的就是请求体.请求体很简单,我们只需要将蓝线中内容复制到RESTClient界面的Body中,然后把占位符替换掉就可以了(如图2所示),请求头怎么弄呢?
图5

关于在RESTClient中添加请求头,我举一个例子(图6),大家就都明白了.
在RESTClient界面中,点击顶部Headers,再点击CustomHeader,会打开图7这个界面.
图6

在图7所示界面,Name栏中填入Content-Type,Value栏中填入text/xml; charset=utf-8,然后点击Okay,我们就将一个请求头添加到本次请求中了.

同理,将第二个请求头也添加进来.我就不再演示了.


图7

这样,我们就将本次请求需要携带的数据都添加进来了.点击SEND按钮,就完成了本次请求.

开始写Demo

拿到了http请求的数据,我们就可以根据数据去写我们的例子程序了,在我们Android端通过Retrofit使用post请求去访问刚刚我们调试的WebService接口.
首先,在看下边代码之前,你要保证自己已经基本了解了Retrofit框架.关于Retrofit不会介绍太多,因为它不是本篇文章的重点.


导包
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'
    }
请求体实例

对应图2中Request的Body部分,需要写三个类来作为请求体.分别对应请求体中的三个节点
根据由外到内的层级关系,它们分别是(类名 <---> 节点名):

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

下面,我详细介绍下这三个类.

@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注解用来指定节点名称,NamespaceList用来指定多个命名空间,Element用来指定子节点名称.

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

同理,RequestBody类中,Root注解用来指定节点名称,Element注解用来指定子节点名称,因为当前节点没有命名空间,因此不需要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注解用来指定节点名称,因为当前节点只有一个命名空间,因此使用Namespace注解而不是NamespaceList.Element注解指定子节点名称,因为当前节点有两个子节点,因此这个类有两个参数,且都被Element注解修饰.
到这里,关于请求体的实体类,就创建好了.下面我们准备响应体的实体类.

响应体实例

跟请求体类似,我们也是需要根据服务器响应的xml文件格式来创建实体类.
通过分析xml格式,我们也需要创建三个实体类来分别对应三个节点.它们的对应关系如下(类名 <---> 节点名):

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

为了与系统类名区分开,我为这三个类名添加了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;
}

因为与请求体类似,这里我也不用对这三个类做过多介绍.不过,值得一提的是,在AssetResponseModel类中,我没有再用Namespace注解去指定命名空间,而是添加了一个成员变量,用Attribute注解将其指定.需要注意的是,再用Attribute注解指定的时候,name,empty,required三个属性,缺一不可.
下面奉上SimpleXml的地址,上面有关于xml与实体类之间绑定的详细介绍:
SimpleXml

创建Interface

三个请求类和三个响应类创建好了,下一步就是创建请求接口Interface.(不明白为啥要这样做的,可以去复习下Retrofit,哦不对,是预习~)
直接上代码:

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);
}

在请求接口中,我们定义了一个函数,将其请求方式制定为post请求,并且为其添加了请求头.这个函数接收一个RequestEnvelope参数.

使用Retrofit请求

所有初始化工作都做完之后,使用Retrofit去请求WebService这块还是蛮简单的.
不得不说,Retrofit确实很强大,整个请求流程下来,结构清晰明了,一点都不拖泥带水.如果再结合上RxJava,岂不是更爽?

    /**
     * 去服务端请求数据
     */
    private void request() {
        String url = "http://192.168.191.1:2000/";
        // 初始化Retrofit
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(SimpleXmlConverterFactory.create()) // 返回数据为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) {
                // 处理响应体
                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) {

            }
        });
    }

总结

到此为止,一个基于Retrofit使用post请求访问WebService的小Demo就算写完了.我已经将代码提交到了GitHub,感兴趣的同学可以去看一下,很简单的小程序.
项目地址
有不明白的同学,欢迎向我提出问题,我们共同学习.
第一次写文章,还望各位前辈多多批评指正,不胜感激!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,515评论 25 707
  • 整体Retrofit内容如下: 1、Retrofit解析1之前哨站——理解RESTful2、Retrofit解析2...
    隔壁老李头阅读 3,974评论 8 19
  • 1.下载:打开http://www.xmindchina.net/点击免费下载 2.下载完成双击xmind-8-w...
    三玉米阅读 1,576评论 0 4
  • 好吧,那就去上咯~BUT!我们刚到实测地点-翰林苑,又被告知老师一次只带一个班,今天三班上课,我们是明天。欧!然后...
    许小小丽阅读 259评论 1 2
  • 快速读完《舒克贝塔传》。 童话故事不仅仅是写给小孩子看的。作者借着鼠口道出的,是人世间常见的现象。自然而然地,让我...
    夏又幽阅读 424评论 0 0