WCF安全性認證:SoapHeader(二)使用HTTP Request呼叫

  • 3984
  • 0
  • WCF
  • 2022-11-24

WCF安全性認證:SoapHeader(二)使用HTTP Request呼叫

在前面第一篇介紹的WinFrom Client 端程式為了送出request時要產生SoapHeader﹐而另外撰寫了一個ClientHeader類別庫﹐並且必須在組態加入對應的設定﹐手續看起來有些複雜。實務上﹐有時會希望是以WebRequest的原生方式自行組合Soap 格式資料來進行資料的交換。

在開始之前需要先知道一些資料的規則﹐首先由wsdl規格中可以找到SOAPAction

soapaction

 

前面的介紹過程中在使用Fiddler觀察資料時﹐可以看到送出的標準Soap資料格式﹐這是現在在程式中要自行組合的字串﹐其中包含了SoapHeader﹐要呼叫的方法與傳遞的參數。

request_1
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tem=\"http://tempuri.org/\">    
<soapenv:Header>    
    <UserAccount xmlns=\"http://tempuri.org\">帳號</UserAccount>    
    <UserPassword xmlns=\"http://tempuri.org\">密碼</UserPassword>    
  </soapenv:Header>    
  <soapenv:Body>    
    <tem:SaySomething>    
      <tem:value>test</tem:value>    
    </tem:SaySomething>   
   </soapenv:Body>   
 </soapenv:Envelope>

有了SoapAction和Soap資料的格式﹐接下來就使用三種平台的方式來撰寫。

1. .Net 的呼叫方式

string wsdlUri = "http://kevin-h:905/basicHttpSoapHeader.host/MyProducts.svc?singleWsdl";    
string username = System.Configuration.ConfigurationManager.AppSettings["username"].ToString();    
string pwd = System.Configuration.ConfigurationManager.AppSettings["pwd"].ToString();    
     
private void btnCallSaySomething_Click(object sender, EventArgs e) {    
    try {    
        HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(wsdlUri);    
        webRequest.Method = "POST";    
        webRequest.ContentType = "text/xml;charset=UTF-8";   
                 webRequest.Headers.Add("SOAPAction:\"http://tempuri.org/IProductService/SaySomething\"");  //<--SOAPAction 別忘了   
         StringBuilder soapStr = new StringBuilder(Environment.NewLine);   
         soapStr.Append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tem=\"http://tempuri.org/\">" + Environment.NewLine);   
         soapStr.Append("  <soapenv:Header>" + Environment.NewLine);   
         soapStr.Append("    <UserAccount xmlns=\"http://tempuri.org\">"+username+"</UserAccount>" + Environment.NewLine);   
         soapStr.Append("    <UserPassword xmlns=\"http://tempuri.org\">"+pwd+"</UserPassword>" + Environment.NewLine);   
         soapStr.Append("  </soapenv:Header>" + Environment.NewLine);   
         soapStr.Append("  <soapenv:Body>" + Environment.NewLine);   
         soapStr.Append("    <tem:SaySomething>" + Environment.NewLine);   
         soapStr.Append("      <tem:value>test</tem:value>" + Environment.NewLine);   
         soapStr.Append("    </tem:SaySomething>" + Environment.NewLine);   
         soapStr.Append("  </soapenv:Body>" + Environment.NewLine);   
         soapStr.Append("</soapenv:Envelope>" + Environment.NewLine);   
     
         byte[] buffer = Encoding.UTF8.GetBytes(soapStr.ToString());   
         webRequest.ContentLength = buffer.Length;   
         Stream post = webRequest.GetRequestStream();   
         post.Write(buffer, 0, buffer.Length);   
         post.Close();   
     
         HttpWebResponse response;   
         response = (HttpWebResponse)webRequest.GetResponse();   
         Stream respostData = response.GetResponseStream();   
         StreamReader sr = new StreamReader(respostData);   
         string strResponseXml = sr.ReadToEnd();   
     
         txtOutputValue.Text = strResponseXml;   
     } catch (Exception er) {   
         if (er.InnerException != null) {   
             txtSaySomethingMsg.Text = "InnerException:" + er.InnerException.Message;   
         } else {   
             txtSaySomethingMsg.Text = "Exception:" + er.Message;   
         }   
     }   
 }

上述的程式執行之後所得到的結果為以下的XML格式資料﹐只要Parse這個資料就可以得到想要的東西。

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">    
<s:Body>    
    <SaySomethingResponse xmlns="http://tempuri.org/">    
        <SaySomethingResult>You say [test].</SaySomethingResult>    
    </SaySomethingResponse>    
</s:Body>    
</s:Envelope>

2. Java 的呼叫方式

package wcf.com;    
     
import java.io.BufferedReader;    
import java.io.ByteArrayOutputStream;    
import java.io.InputStreamReader;    
import java.io.OutputStream;    
import java.net.HttpURLConnection;    
import java.net.URL;    
import java.net.URLConnection;   
     
 public class wcfClient {   
     public static void main(String[] args) {   
         String responseString = "";   
         String outputString = "";   
         String wsURL = "http://kevin-h:905/basicHttpSoapHeader.host/MyProducts.svc?wsdl";   
     
         try{   
             URL url = new URL(wsURL);   
             URLConnection connection = url.openConnection();   
             HttpURLConnection httpConn = (HttpURLConnection)connection;   
             ByteArrayOutputStream bout = new ByteArrayOutputStream();   
               
             StringBuilder xmlInput=new StringBuilder();   
             xmlInput.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tem=\"http://tempuri.org/\">\n");   
             xmlInput.append("  <soapenv:Header>\n");   
             xmlInput.append("    <UserAccount xmlns=\"http://tempuri.org\">testman</UserAccount>\n");   
             xmlInput.append("    <UserPassword xmlns=\"http://tempuri.org\">a123456</UserPassword>\n");   
             xmlInput.append("  </soapenv:Header>\n");   
             xmlInput.append("  <soapenv:Body>\n");   
             xmlInput.append("    <tem:SaySomething>\n");   
             xmlInput.append("      <tem:value>中文字測試</tem:value>\n");   
             xmlInput.append("    </tem:SaySomething>\n");   
             xmlInput.append("  </soapenv:Body>\n");   
             xmlInput.append("</soapenv:Envelope>\n");   
               
             byte[] buffer=new String(xmlInput).getBytes();   
             bout.write(buffer);   
             byte[] b = bout.toByteArray();   
             String SOAPAction ="http://tempuri.org/IProductService/SaySomething";   
               
             //設定適當的Http參數   
             httpConn.setRequestProperty("Content-Length",String.valueOf(b.length));   
             httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");   
             httpConn.setRequestProperty("SOAPAction", SOAPAction);   
             httpConn.setRequestMethod("POST");   
             httpConn.setDoOutput(true);   
             httpConn.setDoInput(true);   
             OutputStream out = httpConn.getOutputStream();   
             out.write(b);   
             out.close();   
     
             InputStreamReader isr = new InputStreamReader(httpConn.getInputStream());   
             BufferedReader in = new BufferedReader(isr);   
            
             while ((responseString = in.readLine()) != null) {   
                 outputString = outputString + responseString;   
             }   
     
               
             System.out.println(outputString);   
         }catch(Exception er){   
             er.printStackTrace();   
         }   
     }   
 }

執行後可以看到輸出的結果和前面.Net得到的結果相同﹐接下就要自行parse XML了。

Ps.在使用eclipse撰寫時﹐送出request並能正確的接回資料﹐但後來在傳遞的值改為中文後就一直失敗。直覺是編碼出了問題﹐查了許久才發現﹐eclipse在windows時檔案存檔時預設檔案格式編碼為MS950﹐如果將檔案改存為UTF-8則傳遞中文的問題就解決了。

 

3. Android 的呼叫方式

時下最流行的行動裝置﹐當然也必須要能呼叫WCF才行﹐以下的示範是以Android 4.2.2版本為例。

在Android 3.0之後在主執行緒上不能直接進行網路活動﹐否則將會得到NetworkOnMainThreadException錯誤訊息。因此在這裏先建立一個SoapObjec的class﹐在主執行緒上再以另一個Thread來執行。

 

SoapObject.java

package com.example.callwcftest;    
     
import java.io.IOException;    
     
import org.apache.http.HttpEntity;    
import org.apache.http.HttpResponse;    
import org.apache.http.client.ClientProtocolException;    
import org.apache.http.client.ResponseHandler;    
import org.apache.http.client.methods.HttpPost;   
 import org.apache.http.entity.ByteArrayEntity;   
 import org.apache.http.impl.client.DefaultHttpClient;   
 import org.apache.http.params.HttpConnectionParams;   
 import org.apache.http.params.HttpParams;   
 import org.apache.http.params.HttpProtocolParams;   
 import org.apache.http.util.EntityUtils;   
     
 import android.util.Log;   
     
 public class SoapObject {   
     private String wsURL="";   
     private String soapAction="";   
     private String soapBody="";   
       
     public void setWsURL(String wsURL){   
         this.wsURL=wsURL;   
     }   
       
     public void setSoapAction(String soapAction){   
         this.soapAction=soapAction;   
     }   
       
     public void setSoapBody(String soapBody){   
         this.soapBody=soapBody;   
     }   
     
     public String sendRequest(){   
         String responseString="";   
         final DefaultHttpClient httpClient=new DefaultHttpClient();   
         // request 參數   
           HttpParams params = httpClient.getParams();   
           HttpConnectionParams.setConnectionTimeout(params, 10000);   
           HttpConnectionParams.setSoTimeout(params, 15000);   
         // set parameter   
           HttpProtocolParams.setUseExpectContinue(httpClient.getParams(), true);   
     
           // POST the envelope   
           HttpPost httppost = new HttpPost(this.wsURL);   
           // add headers   
           httppost.setHeader("soapaction", this.soapAction);   
           httppost.setHeader("Content-Type", "text/xml; charset=utf-8");   
             
         try {   
             // the entity holds the request   
             // HttpEntity entity = new StringEntity(envelope);   
             HttpEntity entity = new ByteArrayEntity(this.soapBody.getBytes("UTF-8"));   
             httppost.setEntity(entity);   
     
             // Response handler   
             ResponseHandler<String> rh = new ResponseHandler<String>() {   
                 // invoked when client receives response   
                 public String handleResponse(HttpResponse response)   
                         throws ClientProtocolException, IOException {   
                     // get response entity   
                     HttpEntity entity = response.getEntity();   
     
                     // read the response as byte array   
                     StringBuffer out = new StringBuffer();   
                     byte[] b = EntityUtils.toByteArray(entity);   
     
                     // write the response byte array to a string buffer   
                     out.append(new String(b, 0, b.length));   
                     return out.toString();   
                 }   
             };   
     
             responseString = httpClient.execute(httppost, rh);   
         } catch (Exception e) {   
             Log.v("exception", e.toString());   
         }   
     
         // close the connection   
         httpClient.getConnectionManager().shutdown();   
           
         return responseString;   
     }   
 }

接著撰寫主執行緒的部分

package com.example.callwcftest;    
     
import android.app.Activity;    
import android.os.Bundle;    
import android.util.Log;    
import android.view.Menu;    
import android.view.View;    
import android.widget.Button;    
import android.widget.TextView;   
     
 public class MainActivity extends Activity {   
     
     Button btnCallWcf;   
     TextView txtMsg;   
     @Override   
     protected void onCreate(Bundle savedInstanceState) {   
         super.onCreate(savedInstanceState);   
         setContentView(R.layout.activity_main);   
           
         btnCallWcf=(Button)this.findViewById(R.id.btnCallWcf);   
         txtMsg=(TextView)this.findViewById(R.id.txtMsg);   
           
         btnCallWcf.setOnClickListener(WcfCall);   
     }   
       
     private Button.OnClickListener WcfCall=new Button.OnClickListener(){   
     
         @Override   
         public void onClick(View arg0) {   
             runOnUiThread(new Runnable() {   
                 @Override   
                 public void run() {   
                     try{   
                         String wsURL="http://xxx.xxx.xxx.xxx:905/basicHttpSoapHeader.host/MyProducts.svc?singleWsdl";   
                         String soapAction="http://tempuri.org/IProductService/SaySomething";   
                         StringBuilder soapBody=new StringBuilder();   
                         soapBody.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:tem=\"http://tempuri.org/\">\n");   
                         soapBody.append("  <soapenv:Header>\n");   
                         soapBody.append("    <UserAccount xmlns=\"http://tempuri.org\">testman</UserAccount>\n");   
                         soapBody.append("    <UserPassword xmlns=\"http://tempuri.org\">a123456</UserPassword>\n");   
                         soapBody.append("  </soapenv:Header>\n");   
                         soapBody.append("  <soapenv:Body>\n");   
                         soapBody.append("    <tem:SaySomething>\n");   
                         soapBody.append("      <tem:value>中文 &amp; english 測試</tem:value>\n");   
                         soapBody.append("    </tem:SaySomething>\n");   
                         soapBody.append("  </soapenv:Body>\n");   
                         soapBody.append("</soapenv:Envelope>\n");   
                           
                         SoapObject client=new SoapObject();   
                         client.setWsURL(wsURL);   
                         client.setSoapAction(soapAction);   
                         client.setSoapBody(soapBody.toString());   
                           
                         txtMsg.setText(client.sendRequest());   
                         Log.v("success", txtMsg.getText().toString());   
                         }catch(Exception er){   
                             Log.d("MAIN err", er.getMessage());   
                         }   
                       
                 }   
             });   
               
         }   
           
     };   
     
 }

執行的結果

android_2

 

有了這些做法﹐應該可以應付很多狀況了。