OkHttp调用SOAP接口报错"Soap message is invalid!"求助
解决OkHttp结合ksoap2调用Microsoft Dynamics SOAP接口的"Soap message is invalid!"错误
你遇到的核心问题是构建请求体时用错了方法,导致发送给服务器的SOAP内容完全无效。咱们一步步来修正:
1. 错误根源:错误生成SOAP请求体
你代码里这一行是关键错误:
RequestBody body = RequestBody.create(SOAP_MEDIA_TYPE, String.valueOf(envelope.getResponse()));
envelope.getResponse()是用来解析服务器返回的SOAP响应的方法,在发送请求前调用它,得到的要么是空值,要么是无效内容,服务器自然会返回"Soap message is invalid!"。
正确的做法是把ksoap2的SoapSerializationEnvelope序列化成标准的SOAP XML字符串,作为请求体发送。
2. 代码修改步骤
步骤1:添加SOAP请求序列化逻辑
替换原来的请求体构建代码,改成以下内容:
// 将Envelope序列化为SOAP XML字符串 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); XmlSerializer serializer = Xml.newSerializer(); try { serializer.setOutput(outputStream, "UTF-8"); envelope.write(serializer); serializer.flush(); } catch (Exception e) { Log.e(HttpUtils.class.getSimpleName(), "Failed to serialize SOAP request", e); return; } String soapRequest = new String(outputStream.toByteArray(), "UTF-8"); // 构建正确的请求体 RequestBody body = RequestBody.create(SOAP_MEDIA_TYPE, soapRequest);
这段代码会把你构建的SoapObject和Envelope转换成符合SOAP规范的XML内容。
步骤2:(可选)修正方法名
你的方法名叫doGetRequest,但实际是发送POST请求,建议改成doSoapPostRequest避免混淆。
步骤3:验证NTLM认证实现
你使用了NTLMAuthenticator,要确保这个类正确实现了OkHttp的Authenticator接口,能处理Dynamics要求的NTLM认证流程。如果认证逻辑有问题,也可能导致请求失败,不过先解决请求体的问题,这是当前最紧急的。
3. 修改后的完整代码示例
package com.demo; import android.util.Log; import org.ksoap2.SoapEnvelope; import org.ksoap2.serialization.SoapObject; import org.ksoap2.serialization.SoapSerializationEnvelope; import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayOutputStream; import java.io.IOException; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; public class HttpUtils { private static final MediaType SOAP_MEDIA_TYPE = MediaType.parse("text/xml; charset=utf-8"); private static final String soap_action = "urn:microsoft-dynamics-schemas/codeunit/HelloWorld:PrintHello"; private static final String namespace = "urn:microsoft-dynamics-schemas/codeunit/HelloWorld"; private static final String method_name = "PrintHello"; public static void doSoapPostRequest() { SoapObject soapObject = new SoapObject(namespace, method_name); soapObject.addProperty("Username", "ravia"); soapObject.addProperty("Password", "Ravi@123"); soapObject.addProperty("Domain", "techcronus"); SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); envelope.dotNet = true; envelope.setOutputSoapObject(soapObject); // 序列化Envelope为SOAP XML ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); XmlSerializer serializer = Xml.newSerializer(); try { serializer.setOutput(outputStream, "UTF-8"); envelope.write(serializer); serializer.flush(); } catch (Exception e) { Log.e(HttpUtils.class.getSimpleName(), "SOAP serialization failed", e); return; } String soapRequest = new String(outputStream.toByteArray()); OkHttpClient okHttpClient = new OkHttpClient.Builder() .authenticator(new NTLMAuthenticator("ravia", "Ravi@123", "techcronus", "")) .build(); RequestBody body = RequestBody.create(SOAP_MEDIA_TYPE, soapRequest); Request request = new Request.Builder() .url("http://192.168.1.199:1013/MISReports/WS/Test/Codeunit/HelloWorld") .post(body) .addHeader("Content-Type", "text/xml; charset=utf-8") .addHeader("soapaction", soap_action) .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(HttpUtils.class.getSimpleName(), "Request failed: " + e.getMessage()); } @Override public void onResponse(Call call, okhttp3.Response response) throws IOException { if (response.isSuccessful()) { Log.d(HttpUtils.class.getSimpleName(), "Response: " + response.body().string()); } else { Log.e(HttpUtils.class.getSimpleName(), "Server error: " + response.body().string()); } } }); } }
4. 额外检查点
- 确认
namespace、method_name、soap_action完全匹配Dynamics WSDL中的定义,大小写和拼写不能错。 - 验证接口参数名(
Username、Password、Domain)是否和Dynamics端的参数一致。 - 确保设备能访问
http://192.168.1.199:1013这个地址,没有网络权限或者防火墙问题。
内容的提问来源于stack exchange,提问作者Bandish
相关产品推荐
相关产品推荐

