【问题标题】:Trying to build a correct SOAP Request尝试构建正确的 SOAP 请求
【发布时间】:2012-07-30 13:49:37
【问题描述】:

我一直在努力尝试使用 ksoap2 for Android 构建正确的 SOAP 请求,但没有成功。理想的请求如下所示:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <AuthorizationToken xmlns="http://www.avectra.com/2005/">
      <Token>string</Token>
    </AuthorizationToken>
  </soap:Header>
  <soap:Body>
    <ExecuteMethod xmlns="http://www.avectra.com/2005/">
      <serviceName>string</serviceName>
      <methodName>string</methodName>
      <parameters>
        <Parameter>
          <Name>string</Name>
          <Value>string</Value>
        </Parameter>
      </parameters>
    </ExecuteMethod>
  </soap:Body>
</soap:Envelope>

我正在使用以下代码来生成我的请求:

    SoapObject request = new SoapObject(NAMESPACE, METHOD);
    request.addProperty("serviceName", SERVICENAME);
    request.addProperty("methodName", METHODNAME);

    SoapObject nestedParameters = new SoapObject(NAMESPACE, "parameters");
    SoapObject param = new SoapObject(NAMESPACE, "Parameter");
    param.addProperty("Name", name);
    param.addProperty("Value", value);
    nestedParameters.addSoapObject(param);
    request.addSoapObject(nestedParameters);

    SoapSerializationEnvelope envelope = 
            new SoapSerializationEnvelope(SoapEnvelope.VER11);
    envelope.setOutputSoapObject(request);
    envelope.dotNet = true;
    envelope.implicitTypes = true;

    envelope.headerOut = new Element[1];
    Element header = new Element().createElement(NAMESPACE, "AuthorizationToken");
    Element token = new Element().createElement(NAMESPACE, "Token");
    token.addChild(Node.TEXT, this.AUTH_TOKEN);
    header.addChild(Node.ELEMENT, token);
    envelope.headerOut[0] = header;

ksoap2 正在构建的是:

<v:Envelope xmlns:i="http://www.w3.org/1999/XMLSchema-instance" xmlns:d="http://www.w3.org/1999/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/">
  <v:Header>
    <n0:AuthorizationToken xmlns:n0="http://www.avectra.com/2005/">
      <n0:Token>string</n0:Token>
    </n0:AuthorizationToken>
  </v:Header>
  <v:Body>
    <ExecuteMethod xmlns="http://www.avectra.com/2005/" id="o0" c:root="1">   
      <serviceName>AHAWebServices</serviceName>
      <methodName>MemberDirectory</methodName>
      <parameters i:type="n1:parameters" xmlns:n1="http://www.avectra.com/2005/">
        <Parameter i:type="n1:Parameter">
          <Name>string</Name>
          <Value>string</Value>
        </Parameter>
      </parameters>
    </ExecuteMethod>
  </v:Body>
</v:Envelope>

我感觉问题出在带有 n0 前缀的标题中,但我不知道如何摆脱它们。我通过将implicitTypes 设置为true 从正文中删除了它们,但我找不到标题的类似设置。我是 SOAP 新手,因此非常感谢任何其他建议。有谁知道我该如何解决这个问题?

【问题讨论】:

    标签: java android xml soap android-ksoap2


    【解决方案1】:

    当使用 KSOAP 这对我有用

    SoapObject request = new SoapObject(WEBSERVICE_NAMESPACE, methodName);
        if(null != parameterMap && !parameterMap.isEmpty()){
            for(Entry<String, String> entry: parameterMap.entrySet()){
                request.addProperty(entry.getKey(), entry.getValue());
            }
        }
        // Declare the version of the SOAP request
    
        SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
        envelope.implicitTypes = true;
        envelope.dotNet = true;
        envelope.setOutputSoapObject(request);
    
    
        HttpTransportSE androidHttpTransport = new HttpTransportSE(ApplicationConstants.WEBSERVICE_WSDL_URL);
    
        // this is the actual part that will call the webservice
        try {
    
            androidHttpTransport.debug = true;
            androidHttpTransport.call(soapActionUrl, envelope);
            String ss = androidHttpTransport.responseDump;
    
            // Get the SoapResult from the envelope body.
    
            Log.d(TAG, "request: " + androidHttpTransport.requestDump);
            Log.d(TAG, "response: "+    androidHttpTransport.responseDump);
    
    
            SoapObject result = (SoapObject) envelope.getResponse();
    
            Log.d("soap response", "" + result);            
        } catch (IOException e) {
            Log.e(TAG, "IOException", e);
        } 
    

    注意:

    androidHttpTransport.debug = true;
    

    解决了我的问题。撞了我的头,但无法解释为什么设置 debug true 有助于解决问题。

    为什么需要使用 ksoap? 只需将 SOAP 请求的静态方作为字符串,将值附加到静态部分,您就可以最终获得完整的 SOAP 请求。最后使用 HTTP 方法发送您的 post 请求。

    没有额外的 JAR

    ksoap 也有一些问题,比如大型响应的 OOM 等。

    KSOAP OOM issue

    可以使用以下代码

    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.HashMap;
    import java.util.Map.Entry;
    
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.params.BasicHttpParams;
    import org.apache.http.params.HttpConnectionParams;
    import org.apache.http.params.HttpParams;
    import org.apache.http.protocol.HTTP;
    import org.apache.http.util.EntityUtils;
    
    import android.util.Log;
    
    public final class SOAPRequest{
    
    private static final String TAG = "SOAPRequest";
    private static final String TAG_SOAP_HEADER_START = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Header>";
    private static final String TAG_AUTHORIZATION_START = "<AuthorizationToken xmlns=\"http://www.avectra.com/2005/\">";
    private static final String TAG_TOKEN_START = "<TOKEN>";
    private static final String TAG_TOKEN_END = "</TOKEN>";
    private static final String TAG_AUTORIZATION_END = "</AuthorizationToken>";
    private static final String TAG_SOAPHEADER_END = "</soap:Header>"; 
    private static final String TAG_SOAP_BODY_START = "<soap:Body>";
    private static final String TAG_PARAM_NAME_START = "<Name>";
    private static final String TAG_PARAM_NAME_END = "</Name>";
    private static final String TAG_PARAM_VALUE_START = "<Value>";
    private static final String TAG_PARAM_VALUE_END = "</Value>";
    private static final String TAG_METHOD_START = "<methodName>";
    private static final String TAG_METHOD_END = "</methodName>";
    private static final String TAG_SERVICE_START = "<serviceName>";
    private static final String TAG_SERVICE_END = "</serviceName>";
    private static final String TAG_PARAMS_START = "<parameters><Parameter>";
    private static final String TAG_EXE_METHOD_START = "<ExecuteMethod xmlns=\"http://www.avectra.com/2005/\">";
    private static final String TAG_SOAP_REQ_END = "</Parameter></parameters></ExecuteMethod></soap:Body></soap:Envelope>";
    
    /**
     * Constructor intentionally made private 
     */
    private SOAPRequest() {
    
    }
    /**
     * Builds a SOAP request with the specified value
     * @param token Value of token
     * @param serviceName Value of servicename
     * @param methodName Value of methodName
     * @param paramsMap Collection of parameters as set of name value pair which needs to be sent
     * @return the complete soap request
     */
    public static String buildRequest(String token, String serviceName, String methodName, HashMap<String, String> paramsMap){
        StringBuilder requestBuilder = new StringBuilder(TAG_SOAP_HEADER_START);
        requestBuilder.append(TAG_AUTHORIZATION_START);
        requestBuilder.append(TAG_TOKEN_START);
        requestBuilder.append(token);
        requestBuilder.append(TAG_TOKEN_END);
        requestBuilder.append(TAG_AUTORIZATION_END);
        requestBuilder.append(TAG_SOAPHEADER_END);
        requestBuilder.append(TAG_SOAP_BODY_START);
        requestBuilder.append(TAG_EXE_METHOD_START);
        requestBuilder.append(TAG_SERVICE_START);
        requestBuilder.append(serviceName);
        requestBuilder.append(TAG_SERVICE_END);
        requestBuilder.append(TAG_METHOD_START);
        requestBuilder.append(methodName);
        requestBuilder.append(TAG_METHOD_END);
        requestBuilder.append(TAG_PARAMS_START);
        for(Entry<String, String> param :paramsMap.entrySet()){
            requestBuilder.append(TAG_PARAM_NAME_START);
            requestBuilder.append(param.getKey());
            requestBuilder.append(TAG_PARAM_NAME_END);
            requestBuilder.append(TAG_PARAM_VALUE_START);
            requestBuilder.append(param.getValue());
            requestBuilder.append(TAG_PARAM_VALUE_END);
        }
        requestBuilder.append(TAG_SOAP_REQ_END);
        return requestBuilder.toString();
    }
    
    /**
     * Connection timeout set for the HttpClient
     */
    private static final int CONNECTION_TIMEOUT= 6000;
    /**
     * Socket timeout set for the HttpClient
     */
    private static final int SOCKET_TIMEOUT = 10000; 
    
    /**
     * @return httpClient An instance of {@link DefaultHttpClient}
     */
    private static DefaultHttpClient getHttpClient() {
        HttpParams httpParameters = new BasicHttpParams();
        // Set the timeout in milliseconds until a connection is established.
        // The default value is zero, that means the timeout is not used.
        HttpConnectionParams.setConnectionTimeout(httpParameters,CONNECTION_TIMEOUT);
        // Set the default socket timeout (SO_TIMEOUT)
        // in milliseconds which is the timeout for waiting for data.
        HttpConnectionParams.setSoTimeout(httpParameters, SOCKET_TIMEOUT);
    
        return new DefaultHttpClient(httpParameters);
    }
    
    /**
     * Sends a SOAP request to the specified service endpoint. 
     * 
     * @param serviceEndpoint The service endpoint which will be hit
     * @param soapRequest The SOAP request
     * @return The string representing the response for the specified SOAP request. 
     */
    public static String send(String serviceEndpoint, String soapRequest){
        HttpPost httppost = new HttpPost(serviceEndpoint);          
        StringEntity se = null;
        try {
            se = new StringEntity(soapRequest,HTTP.UTF_8);
        } catch (UnsupportedEncodingException e) {
            Log.e(TAG,"send", e);
            return null;
        }
    
        se.setContentType("text/xml");  
        httppost.setHeader("Content-Type","application/soap+xml;charset=UTF-8");
        httppost.setEntity(se);  
        String result = null;
        HttpClient httpclient = getHttpClient();
        try {
            HttpResponse httpResponse = httpclient.execute(httppost);
            HttpEntity responseEntity = httpResponse.getEntity();
            if(null!= responseEntity){
                //if you have a huge chunk of data read it using a buffer
                result =EntityUtils.toString(responseEntity);
            }
        } catch (ClientProtocolException e) {
            Log.e(TAG,"send", e);
        } catch (IOException e) {
            Log.e(TAG,"send", e);
        } catch (Exception e){
            Log.e(TAG,"send", e);
        }
    
        return result;
    }
    
    }
    

    【讨论】:

      【解决方案2】:

      我认为您需要另一种方法来创建标头,它看起来像 jax-ws,所以我将使用几个月前所做的 jax ws 实现。

      首先你需要一个 HeaderHandler 类,它会创建soap头元素,它应该是这样的:


          import javax.xml.namespace.QName;
          import javax.xml.soap.SOAPElement;
          import javax.xml.soap.SOAPEnvelope;
          import javax.xml.soap.SOAPHeader;
          import javax.xml.ws.handler.MessageContext;
          import javax.xml.ws.handler.soap.SOAPHandler;
          import javax.xml.ws.handler.soap.SOAPMessageContext;
      
      
          public class HeaderHandler implements SOAPHandler<SOAPMessageContext> {
      
              public boolean handleMessage(SOAPMessageContext smc) {
                  Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
                  String AUTH_TK = "http://www.avectra.com/2005/";
                  String PREFIX="";//no prefix
                  String PREFIX_XMLNS="xmlns";
                  String value =  "123456";
                  if (outboundProperty.booleanValue()) {
                      try {
                          SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope();
                          SOAPHeader header = envelope.addHeader();
                          //<AuthorizationToken xmlns="http://www.avectra.com/2005/">
                          SOAPElement authorizationToken = header.addChildElement("AuthorizationToken", PREFIX_XMLNS, AUTH_TK);
                          //<Token>value</Token>
                          SOAPElement usernameToken =
                              authorizationToken.addChildElement("Token", PREFIX);
                              usernameToken.addTextNode(value);
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  }
                  return outboundProperty;
              }
      
      
              public Set<QName> getHeaders() {
                  return null;
              }
      
              public void close(MessageContext arg0) {
      
              }
      
              public boolean handleFault(SOAPMessageContext arg0) {
                  return false;
              }
          }
      

      之后,您创建一个 HeaderHandlerResolver 来处理标头创建并将其插入到处理程序链中:


          import java.util.ArrayList;
          import java.util.List;
          import javax.xml.ws.handler.Handler;
          import javax.xml.ws.handler.HandlerResolver;
          import javax.xml.ws.handler.PortInfo;
      
          public class HeaderHandlerResolver implements HandlerResolver {
      
          @SuppressWarnings("unchecked")
          public List<Handler> getHandlerChain(PortInfo portInfo) {
                List<Handler> handlerChain = new ArrayList<Handler>();
                HeaderHandler hh = new HeaderHandler();
                handlerChain.add(hh);
                return handlerChain;
             }
          }
      

      之后,添加客户端:


              try{
                  //new service instance (your service should be extending javax.xml.ws.Service;)
                  YourServiceProxy service = new YourServiceProxy();
                  //calls the header handler resolver ;)
                  service.setHandlerResolver(new HeaderHandlerResolver());
                  //get the service
                  YourService port = (YourService)service.getYourService();
                  //call the service 
                  port.yourMethod()   
              } catch (Exception e) {
                  e.printStackTrace();
              }
      

      顺便说一句,我没有测试这个特定的头文件,我修改了我以前的头文件处理程序,所以它可能不准确,但我认为它非常接近,我真的希望它对你有帮助,试试看告诉我们它是怎么来的,如果它仍然不起作用,我会尽力帮助你。

      【讨论】:

        【解决方案3】:

        您是否检查过 kSOAP 为parameters(即i:type="n1:parameters")和Parameter(即i:type="n1:Parameter")节点生成的类型是否正确(它们是在wsdl 中定义的)?

        尝试设置

        envelope.implicitTypes = true;
        

        还可以玩

        envelope.setAddAdornments(false);
        

        强制 kSOAP 不包含类型属性和名称空间。

        【讨论】:

          【解决方案4】:

          设置

           envelope.implicitTypes = true;
          

          并且不要设置

           envelope.setAddAdornments(false)
          

          这对我有用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-09-14
            • 2012-05-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多