Retrofit框架请求SOAP WebService

来源:互联网 发布:淘宝怎么用图片找同款 编辑:程序博客网 时间:2024/06/05 09:16

前言:

        近期android使用Retrofit框架进行网络请求,已经非常流行,但是网上的例子或是现有请求后台服务器一般基于Rest方式,即发送请求时发送的参数一般直接放到header或是path路径里,稍复杂点的参数放到表单里请求。如果后台基于WebService方式,发送的数据格式是SOAP型的XML数据,大家一般用的都是ksoap框架来请求。如果一个客户端如果有两种后台服务,在客户端代码中用两套网络框架,一般不是大家希望的。随着Retrofit框架的不断更新,此框架进行网络请求应该是大势所趋。然而用Retrofit进行Webservice请求,网上资源太少,这两天查找,也只找到两篇相关的文档和一个demo:

        文章一:http://m.blog.csdn.net/article/details?id=51133893这是转载一个台湾人写的,只有片断式的零星代码,没有从头到尾讲清楚数据的组装,更没有一个可以直接运行的demo,也没有讲清楚不同的SOAP版本,请求也大不相同;

        文章二:http://m.blog.csdn.net/article/details?id=51163808这篇是作者本人的实践,不得不说费了一番功夫,但是数据组装也没有讲清楚,也没有展示的demo。且作者在发送请求时,直接把请求的model直接用字符串传过去,没有发挥出retrofit的SimpleXmlConverterFactory的强大优势。

        例子:demo下载地址是https://github.com/asanchezyu/RetrofitSoapSample,不得不说,这是一个能运行完整demo,但这是基于SOAP Version1.2的,对于不同的SOAP版本, 在请求上还是大不一样。而且作者的返回里没有数据,无法看到解析返回数据。


       看到网上这点零星的资料,好几次都想转回ksoap战营。但做为一名开发人员,不能一直copy他人代码,不动脑筋的程序员不是好程序员。下定决心一定要用Retrofit成功请求Webservice数据,终于经过两天研究,终于功夫不负有心人,成功请求出数据。


        SOAP Webservice的测试工具一般都是基于SoapUI工具,此工具在导入工程后可以清楚查看WebService包含的方法,SOAP版本,各方法请求参数。还可以直接模拟网络请求,查看返回结果。具体的SoapUI使用方法在这里就不多说,下面是用http://www.webservicex.net/uszip.asmx?WSDL这个工程测试的,左侧展示的是工程包含的方法,中间是请求发送的xml包,右边是服务器返回xml数据。


        

        如果成功调用过函数,也可用charles抓包工具看看请求数据的封装。

        这是成功用retrofit框架请求webservice的截图



      通过SoapUI工具可以清楚查看到请求体,实现方面也会更容易。下面具体说说如何用Retrofit实现SOAP WebService网络请求。


实现:

1. SOAP版本

不同SOAP版本所需的命名空间有些不一样,具体查看,可以通过SoapUI工具,也可以问后台开发人员,通过查看SoapEnvelop源码可以看到。



从上图可以看出,1.0,1.1,1.2版本,命名空间都有些不一样,这会影响到后面编写Envelope包封装的请求。


2. 准备工作,因为WebService数据请求及返回都是xml形式,需要导入Retrofit xml解析的依赖包(需要把stax|stax-api|xpp3这三个类排除掉,否则编译时会报错)

compile("com.squareup.retrofit2:converter-simplexml:2.0.0-beta3"){    exclude module: 'stax'    exclude module: 'stax-api'    exclude module: 'xpp3'}
3. 编写Retrofit请求对象,包括请求的基址,请求头Content-Type设为text/xml;charset=UTF-8,转换器设置为
SimpleXmlConverterFactory
[java] view plain copy
  1. /** 
  2.  * 服务器Retrofit变量初始化 
  3.  * Created by SmileXie on 16/7/16. 
  4.  */  
  5. public class JXHDRetrofitGenerator {  
  6.   
  7.     private static Strategy strategy = new AnnotationStrategy();  
  8.     private static Serializer serializer = new Persister(strategy);  
  9.   
  10.     private static OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();  
  11.   
  12.    private static Retrofit.Builder retrofitBuilder =  new Retrofit.Builder()  
  13.             .addConverterFactory(SimpleXmlConverterFactory.create(serializer))  
  14.             .baseUrl(Constant.BASE_URL); //请求的webservice基址  
  15.   
  16.     public static <S> S createService(Class<S> serviceClass) {  
  17.         okHttpClient.interceptors().add(new Interceptor() {  
  18.             @Override  
  19.             public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {  
  20.                 Request original = chain.request();  
  21.   
  22.                 Request.Builder requestBuilder = original.newBuilder()  
  23.                         .header("Content-Type""text/xml;charset=UTF-8")//添加请求头  
  24.                         .method(original.method(), original.body());  
  25.   
  26.                 Request request = requestBuilder.build();  
  27.                 return chain.proceed(request);  
  28.             }  
  29.         });  
  30.   
  31.         OkHttpClient client = okHttpClient.connectTimeout(2, TimeUnit.MINUTES)  
  32.                 .writeTimeout(2, TimeUnit.MINUTES)  
  33.                 .readTimeout(2, TimeUnit.MINUTES)  
  34.                 .build();  
  35.         Retrofit retrofit = retrofitBuilder.client(client).build();  
  36.         return retrofit.create(serviceClass);  
  37.     }  
  38. }  

4. 编写请求函数
[java] view plain copy
  1. /** 
  2.  * 定义请求接口 
  3.  * Created by SmileXie on 16/7/15. 
  4.  */  
  5. public interface JXHDInterfaceFun {  
  6.     @Headers({"SOAPAction: getRoleinfo"})//请求的Action,类似于方法名  
  7.     @POST("service?wsdl")//请求的地址  
  8.     Call<RoleInfoResponseEnvelope> getRoleInfo(@Body RoleInfoRequestEnvelope requestEnvelope);  
  9. }  
5. 组装请求Request Model
主要组装xml数据如下:
1)组装最外层的soapenv:Envelope,这里请求就涉及到命名空间,不同的SOAP版本是不一样的,下面是SOAP1.1版本的:
[java] view plain copy
  1. /** 
  2.  * Created by SmileXie on 16/7/15. 
  3.  */  
  4. @Root(name = "soapenv:Envelope")  
  5. @NamespaceList({  
  6.         @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),  
  7.         @Namespace(reference = "http://www.w3.org/2001/XMLSchema", prefix = "xsd"),  
  8.         @Namespace(reference = "http://schemas.xmlsoap.org/soap/encoding/", prefix = "enc"),  
  9.         @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soapenv")  
  10. })  
  11. public class RoleInfoRequestEnvelope {  
  12.     @Element(name = "soapenv:Body", required = false)  
  13.     public RoleInfoRequestBody body;  
  14.   
  15.     @Element(name = "soapenv:Header", required = false)  
  16.     public String aHeader;  
  17.   
  18. }  
如果你服务器是SOAP1.2版本的,命名空间应该这样写,前缀可以自己定义,但要对应上@Element里的前缀。
[java] view plain copy
  1. @Root(name = "soap12:Envelope")  
  2. @NamespaceList({  
  3.         @Namespace( prefix = "xsi", reference = "http://www.w3.org/2001/XMLSchema-instance"),  
  4.         @Namespace( prefix = "xsd", reference = "http://www.w3.org/2001/XMLSchema"),  
  5.         @Namespace( prefix = "soap12", reference = "http://www.w3.org/2003/05/soap-envelope")  
  6. })  
2)组装最外层的Body部分:
[java] view plain copy
  1. /** 
  2.  * 用户角色返回body 
  3.  * Created by SmileXie on 16/7/15. 
  4.  */  
  5. @Root(name = "soapenv:Body", strict = false)  
  6. public class RoleInfoRequestBody {  
  7.     @Element(name = "getroleinfo", required = false)  
  8.     public UserInfoBaseRequest getroleinfo;  
  9. }  
3)组装getroleinfo部分:
[java] view plain copy
  1. /** 
  2.  * 用户信息 
  3.  * Created by SmileXie on 16/7/15. 
  4.  */  
  5.   
  6. public class UserInfoBaseRequest {  
  7.   
  8.     @Element(name = "token", required = false)  
  9.     public String token;  
  10.   
  11.     @Element(name = "userId")  
  12.     public String userid;  
  13.   
  14.     @Element(name = "userType")  
  15.     public String userType;  
  16.   
  17.     public String getToken() {  
  18.         return token;  
  19.     }  
  20.   
  21. }  
至此,Request部分组装完成。所有请求变量都可以写成private,再用setter方法赋值。因为这里不涉及到修改数据,所以笔者全部用了public。
6. 编写请求Response Model
如果事先不知道返回的数据格式,这部分可以先不写,返回数据直接用ResponseBody来接收,通过抓包工具,查看Response部分,获取到数据的返回结果,再进行编写。
接收的数据格式跟Request Model一样,一层一层解析。Retrofit默认用的是SAX解析,所以每个标签都需要进行解析,要不会报错。
1)先解析最外层的soapevn:Envelope
[java] view plain copy
  1. /** 
  2.  * 用户角色返回总信息 
  3.  * Created by SmileXie on 16/7/15. 
  4.  */  
  5. @Root(name = "soapenv:Envelope")  
  6. @NamespaceList({  
  7.         @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),  
  8.         @Namespace(reference = "http://www.w3.org/2001/XMLSchema", prefix = "xsd"),  
  9.         @Namespace(reference = "http://schemas.xmlsoap.org/soap/encoding/", prefix = "enc"),  
  10.         @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soapenv")  
  11. })  
  12. public class RoleInfoResponseEnvelope {  
  13.     @Element(name = "Body")  
  14.     public RoleInfoResponseBody body;  
  15.   
  16. }  
2)解析soapenv:Body部分
[java] view plain copy
  1. /** 
  2.  * 用户角色返回body 
  3.  * Created by SmileXie on 16/7/15. 
  4.  */  
  5. @Root(name = "Body")  
  6. public class RoleInfoResponseBody {  
  7.   
  8.     @Element(name = "getroleinfoResponse", required = false)  
  9.     public RoleInfoResponse roleInfoResponse;  
  10.   
  11. }  
3)解析getroleinfoResponse部分
[java] view plain copy
  1. /** 
  2.  * 用户角色返回 
  3.  * Created by SmileXie on 16/7/15. 
  4.  */  
  5.   
  6. @Root(name = "getroleinfoResponse")  
  7. @NamespaceList({  
  8.         @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),  
  9.         @Namespace(reference = "http://www.w3.org/2001/XMLSchema", prefix = "xsd"),  
  10.         @Namespace(reference = "http://schemas.xmlsoap.org/soap/encoding/", prefix = "enc"),  
  11.         @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope", prefix = "env"),  
  12.         @Namespace(reference = "http://schemas.xmlsoap.org/soap/encoding/", prefix = "encodingStyle"),  
  13. })  
  14. public class RoleInfoResponse {  
  15.     @Attribute(name = "encodingStyle")  
  16.     public String encodingStyle;  
  17.   
  18.   
  19.     @Element(name = "getroleinfoReturn")  
  20.     public RoleInfoResponseReturn reolInfo;  
  21.   
  22. }  
4)解析最里一层,getroleinfoReturn部分
[java] view plain copy
  1. /** 
  2.  * 用户角色返回 
  3.  * Created by SmileXie on 16/7/18. 
  4.  */  
  5.   
  6. @Root(name = "getroleinfoReturn")  
  7. @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")  
  8. public class RoleInfoResponseReturn {  
  9.     @Attribute(name = "type")  
  10.     public String xsiType;  
  11.   
  12.     @Text()  
  13.     public String message;  
  14.   
  15. }  
最里边的数据其实是String型,直接用@Text注解获取,我们这里返回的是一个json,最后转用Gson来解析的。
7. 编写请求代码
[java] view plain copy
  1. private void testSOAP() {  
  2.         JXHDInterfaceFun apiService = JXHDRetrofitGenerator.createService(JXHDInterfaceFun.class);  
  3.         RoleInfoRequestEnvelope requestEnvelope = new RoleInfoRequestEnvelope();    //soapenv:Envelope  
  4.         RoleInfoRequestBody requestBody = new RoleInfoRequestBody();    // body  
  5.         UserInfoBaseRequest baseRequest = new UserInfoBaseRequest();    // getroleinfo  
  6.         baseRequest.userid = "liliting";  
  7.         baseRequest.userType = "2";  
  8.         requestBody.getroleinfo = baseRequest;  
  9.         requestEnvelope.body = requestBody;  
  10.   
  11.         Call<RoleInfoResponseEnvelope> call = apiService.getRoleInfo(requestEnvelope);  
  12.         call.enqueue(new Callback<RoleInfoResponseEnvelope>() {  
  13.             @Override  
  14.             public void onResponse(Response<RoleInfoResponseEnvelope> response) {  
  15.                 Util.stopProgressDialog();  
  16.                 RoleInfoResponseEnvelope roleInfoResponseEnvelope = response.body();  
  17.                 if (roleInfoResponseEnvelope != null ) {  
  18.                     String message = roleInfoResponseEnvelope.body.roleInfoResponse.reolInfo.message;  
  19.                     BaseResponse baseResponse = GsonUtil.getJsonObject(message, BaseResponse.class);  
  20.                     if(baseResponse != null) {  
  21.                         RoleInfoResponse roleResponse = GsonUtil.getJsonObject(baseResponse.getMessage(), RoleInfoResponse.class);  
  22.                         String roleType = roleResponse.getRoles();  
  23.                     }  
  24.   
  25.                 }  
  26.             }  
  27.             @Override  
  28.             public void onFailure(Throwable t) {  
  29.                 Util.stopProgressDialog();  
  30.                 Toast.makeText(SendMessageActivity.this, getString(R.string.load_fail), Toast.LENGTH_SHORT).show();  
  31.             }  
  32.         });  
  33.     }  

采用Retrofit进行WebService请求,上面代码完整显示了请求数据的封装,返回数据的解析,请求函数的编写,Retrofit对象组装,请求代码等。稍候我会把在项目实施中遇到的一些问题进行总结,并把相应代码整理成demo,供大家参考。
后记:
两种框架对比:
用Retrofit进行WebService请求,在组装请求数据时,需要进行Envelop,Body,RequestMode的组装,至少要用到3个类;
在解析返回的XML数据时,也要进行Envelop,Body,ResponseModel的解析,至少需要用3个类。
这样Retrofit框架比ksoap框架进行WebService请求,类变多了,但并没有变麻烦。这样写使整个请示都清晰了,开发者可以清楚看到数据的组装。
用ksoap请求,请求头公共的,可以写在一起,请求的对象只需要通过HashMap传值过去,ksoap会相应的组装body部分;
返回的对象解析只需要用到一个类,比WebService编写简单。
但ksoap需要单独写个AsyncTask来进行网络请求,而Retrofit包含了同步和异步请求框架,不用开发者单独写线程请求。
且Retrofit结合RxJava,现在变得越来越流行起来。
具体大家如何决择,还得具体情况具体分析。
--------------------我是华丽的分隔线-----------------------
这两天写了个demo,利用Retrofit网络框架进行WebService SOAP1.1请求,以请求天气预报为例,天气预报的请求网址为:
http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?op=getWeatherbyCityName
这里边也很详情的说明了SOAP1.1和SOAP1.2请求model和返回model的数据格式。
工程下载地址为:
https://github.com/xiewenfeng/RetorfitWebServiceSample


转自:http://blog.csdn.net/smileiam/article/details/51957232