美文网首页android
结合Retrofit使用post请求访问WebService

结合Retrofit使用post请求访问WebService

作者: 王明超Honor | 来源:发表于2017-02-23 14:52 被阅读2757次

    背景

    没啥背景,实在是受够了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,感兴趣的同学可以去看一下,很简单的小程序.
    项目地址
    有不明白的同学,欢迎向我提出问题,我们共同学习.
    第一次写文章,还望各位前辈多多批评指正,不胜感激!

    相关文章

      网友评论

        本文标题:结合Retrofit使用post请求访问WebService

        本文链接:https://www.haomeiwen.com/subject/jhdmwttx.html