美文网首页
SOAP-WebService 用Retrofit2请求

SOAP-WebService 用Retrofit2请求

作者: Demon鑫 | 来源:发表于2018-09-21 17:39 被阅读0次

    零、概述

    项目由于种种原因导致后台服务是用 C# 编写。请求与返回的数据格式是 SOAP 型的 XML 。外加上国内外对此描述的文章非常稀少,所以才有了此篇小作文儿。

    一、相关需求

    1. 解决 SOAP 1.1 或 SOAP 1.2 的 WebService 与 Android 通讯问题。
    2. 请求、响应数据格式如下图


      image

    二、选择方案

    1. 最终选择使用 SOAP 1.1 用来通讯。 SOAP 1.2 个人未做测试不过感觉原理差不多。
    2. 使用 simplexml 搭配 Retrofit2 进行 XML 解析。

    三、干货

    1.目录结构

    image

    PS:原本想把响应参数也抽调成共通 Base 结果发现抽调后不能正常解析。

    2.build.gradle 配置

    apply plugin: 'com.android.application'
    
    android {
        ...
    }
    
    dependencies {
        ...
        // Retrofit2
        implementation 'com.squareup.retrofit2:retrofit:2.4.0'
        implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
        implementation 'com.squareup.retrofit2:converter-scalars:2.4.0'
        // XML解析
        implementation('com.squareup.retrofit2:converter-simplexml:2.2.0') {
            exclude group: 'xpp3', module: 'xpp3'
            exclude group: 'stax', module: 'stax-api'
            exclude group: 'stax', module: 'stax'
        }
        // FastJson
        implementation 'com.alibaba:fastjson:1.2.49'
    }
    

    PS:由于早期开发 Java 后台习惯用 FastJson 来解析 Json ,所以使用了 FastJson 。

    3.网络模块

    BaseCallBean
    /**
     * @Description 请求返回基类
     */
    public class BaseCallBean<T> {
    
        public boolean success;
        public String msg;
        public T data;
    
        public boolean isSuccess() {
            return success;
        }
    
        public void setSuccess(boolean success) {
            this.success = success;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    }
    
    RetrofitEnum
    /**
     * @Description 枚举(用作单例)
     */
    public enum RetrofitEnum {
    
        /**
         * Retrofit 源
         */
        RETROFIT_SOURCE;
    
        private RetrofitManage manage;
    
        RetrofitEnum() {
            manage = new RetrofitManage();
        }
    
        public RetrofitService getApi() {
            return manage.getApi();
        }
    }
    

    PS:前些日子看到用枚举特性来实现单例,这里用用。

    RetrofitManage
    /**
     * @Description Retrofit2.0 管理类
     */
    public class RetrofitManage {
    
        /**
         * HTTP 请求地址
         **/
        private final String API_URL = "http://192.168.1.200:200";
    
        /**
         * 网络请求api
         */
        private RetrofitService api;
    
        /**
         * 构造方法
         */
        public RetrofitManage() {
            // OkHttp3 配置
            OkHttpClient client = new OkHttpClient.Builder()
                    // 连接超时时间
                    .connectTimeout(7676, TimeUnit.SECONDS)
                    // 读取时间
                    .readTimeout(7676, TimeUnit.SECONDS)
                    .build();
    
            Retrofit retrofit = new Retrofit.Builder()
                    // 服务器请求URL
                    .baseUrl(API_URL)
                    // 转换器方式为XML
                    .addConverterFactory(SimpleXmlConverterFactory.create())
                    // OkHttp3 对象
                    .client(client)
                    .build();
    
            // 获取Proxy.newProxyInstance动态代理对象
            api = retrofit.create(RetrofitService.class);
        }
    
        /**
         * 外部获取 RetrofitService
         * @return ZLRetrofitService
         */
        public RetrofitService getApi() {
            return api;
        }
    }
    

    PS:addConverterFactory 这里使用 SimpleXmlConverterFactory 来转换。注意 XML 转换方式与Json转换方式不能同时使用。如果同时加入则先加入的生效。还是来个例子说明一下吧。
    EG:下例 Gson 转换生效 XML 不能生效。

    Retrofit retrofit = new Retrofit.Builder()
            // 服务器请求URL
            .baseUrl(API_URL)
            // 添加Gson转换器 需添加依赖 'com.squareup.retrofit2:converter-gson:2.1.0'
            .addConverterFactory(GsonConverterFactory.create())
            // 转换器方式为XML
            .addConverterFactory(SimpleXmlConverterFactory.create())
            // OkHttp3 对象
            .client(client)
            .build();
    
    RetrofitService
    /**
     * @Description Retrofit 2.0 网络请求API
     */
    public interface RetrofitService {
    
        // 登录
        @Headers({
                "Content-Type: text/xml; charset=utf-8",
                "SOAPAction: http://tempuri.org/Login"
        })
        @POST("LoginService.asmx")
        Call<ResLoginEnvelope> login(@Body ReqBaseEnvelope envelope);
    
    }
    
    ReqBaseEnvelope
    import org.simpleframework.xml.Element;
    import org.simpleframework.xml.Namespace;
    import org.simpleframework.xml.NamespaceList;
    import org.simpleframework.xml.Root;
    
    /**
     * @Description Envelope
     */
    @Root(name = "soap:Envelope")
    @NamespaceList({
            @Namespace(reference = "http://www.w3.org/2001/XMLSchema", prefix = "xsd"),
            @Namespace(reference = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi"),
            @Namespace(reference = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soap")
    })
    public class ReqBaseEnvelope {
    
        @Element(name = "soap:Body", required = false)
        public ReqBaseBody body;
    
        public ReqBaseEnvelope(ReqBaseBody body) {
            this.body = body;
        }
    
        public ReqBaseBody getBody() {
            return body;
        }
    
        public void setBody(ReqBaseBody body) {
            this.body = body;
        }
    }
    
    ReqBaseBody
    import org.simpleframework.xml.Root;
    
    /**
     * @Description Body
     */
    @Root(name = "soap:Body")
    public interface ReqBaseBody {
    
    }
    

    4.业务模块

    ReqLoginBody
    import com.demon.soap.http.request.ReqBaseBody;
    
    import org.simpleframework.xml.Element;
    import org.simpleframework.xml.Root;
    
    /**
     * @Description 请求登录 Body
     */
    @Root(name = "soap:Body")
    public class ReqLoginBody implements ReqBaseBody {
    
        @Element(name = "Login", required = false)
        public ReqLoginModel model;
    
        public ReqLoginBody(ReqLoginModel model) {
            this.model = model;
        }
    
    }
    
    ReqLoginModel
    import org.simpleframework.xml.Element;
    import org.simpleframework.xml.Namespace;
    import org.simpleframework.xml.Root;
    
    /**
     * @Description 请求登录 Model
     */
    @Root(name = "Login")
    @Namespace(reference = "http://tempuri.org/")
    public class ReqLoginModel {
    
        @Element(name = "userName", required = false)
        public String userName;
        @Element(name = "password", required = false)
        public String password;
    
        public ReqLoginModel(String userName, String password) {
            this.userName = userName;
            this.password = password;
        }
    
    }
    
    ResLoginBody
    import org.simpleframework.xml.Element;
    import org.simpleframework.xml.Root;
    
    /**
     * @Description 响应登录 Body
     */
    @Root(name = "soap:Body")
    public class ResLoginBody {
    
        @Element(name = "LoginResponse", required = false)
        public ResLoginModel model;
    
    }
    
    ResLoginEnvelope
    import org.simpleframework.xml.Element;
    import org.simpleframework.xml.Namespace;
    import org.simpleframework.xml.NamespaceList;
    import org.simpleframework.xml.Root;
    
    /**
     * @Description 响应登录 Envelope
     */
    @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 ResLoginEnvelope {
    
        @Element(name = "Body", required = false)
        public ResLoginBody body;
    
    }
    
    ResLoginModel
    import org.simpleframework.xml.Element;
    import org.simpleframework.xml.Namespace;
    import org.simpleframework.xml.Root;
    
    /**
     * @Description 响应登录 返回值
     */
    @Root(name = "LoginResponse")
    @Namespace(reference = "http://tempuri.org/")
    public class ResLoginModel {
    
        @Element(name = "LoginResult")
        public String result;
    
    }
    
    activity
    ...
    import com.demon.soap.R;
    import com.demon.soap.business.http.request.ReqLoginBody;
    import com.demon.soap.business.http.request.ReqLoginModel;
    import com.demon.soap.business.http.response.ResLoginEnvelope;
    import com.demon.soap.http.BaseCallBean;
    import com.demon.soap.http.RetrofitEnum;
    import com.demon.soap.http.RetrofitService;
    import com.demon.soap.http.request.ReqBaseEnvelope;
    
    import retrofit2.Call;
    import retrofit2.Callback;
    import retrofit2.Response;
    
    /**
     * @Description : 登录
     */
    public class LoginActivity extends AppCompatActivity {
    
        private Button btnReq;
        private TextView tvRes;
    
        // 网络请求API
        private RetrofitService mApi;
        // 接口回调
        private Call<ResLoginEnvelope> mCall;
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.login_activity);
            initView();
            initHttp();
            initListener();
        }
    
        private void initView() {
            btnReq = findViewById(R.id.btn_req);
            tvRes = findViewById(R.id.tv_res);
        }
    
        private void initHttp() {
            mApi = RetrofitEnum.RETROFIT_SOURCE.getApi();
        }
    
        private void initListener() {
            btnReq.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    login("admin", "12346");
                }
            });
        }
    
        private void login(String parUserName, String parPassword) {
            mCall = mApi.login(new ReqBaseEnvelope(new ReqLoginBody(new ReqLoginModel(parUserName, parPassword))));
            mCall.enqueue(new Callback<ResLoginEnvelope>() {
                @Override
                public void onResponse(Call<ResLoginEnvelope> call, Response<ResLoginEnvelope> response) {
                    if (null != response.body()) {
                        Log.d("SOAP", "[login Success Result]:" + response.body().body.model.result);
                        tvRes.setText(response.body().body.model.result);
                        // 解析 result Json
                        BaseCallBean<String> callBean = JSON.parseObject(response.body().body.model.result, new TypeReference<BaseCallBean<String>>() {});
                        if (callBean.success) {
                            Toast.makeText(getApplicationContext(), callBean.msg, Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(getApplicationContext(), callBean.msg, Toast.LENGTH_SHORT).show();
                        }
                    } else {
                        Toast.makeText(getApplicationContext(), "未请求到数据!", Toast.LENGTH_SHORT).show();
                    }
                }
    
                @Override
                public void onFailure(Call<ResLoginEnvelope> call, Throwable t) {
                    Toast.makeText(getApplicationContext(), "请求失败,网络超时!", Toast.LENGTH_SHORT).show();
                }
            });
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mCall.isExecuted()) {
                mCall.cancel();
            }
        }
    }
    

    四、总结

    上面为除布局文件外的全部的代码,简化了很多内容。其实解决解析 SOAP XML 数据的过程还是有点意思的。从一开始 Google 不到相关信息到解决,其中还是有很多小坑的。不过过程是坎坷的结果是美好的。
    PS:有意见请留言!


    2018/9/21 17:37:51

    相关文章

      网友评论

          本文标题:SOAP-WebService 用Retrofit2请求

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