首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 使用 SOAP 調用 .NET WebService 完整指南:WSDL、參數封裝、編碼處理、異常捕獲

     編輯:PHP 相關知識

調用 .NET (ASMX/WCF) 發布的 SOAP WebService,PHP 內建 SoapClient 提供強大支援。關鍵在於**正確封裝參數結構、處理編碼差異、捕獲 SOAP Fault**。本文提供生產級封裝類與實戰避坑指南。

環境要求

  • PHP 安裝 soap 擴展:extension=soap (php.ini 中取消註釋)。
  • 開啟 openssl 擴展 (HTTPS WSDL/端點需要)。
  • 確認服務器能訪問目標 WSDL URL 及服務端點 (防火牆/代理)。

基礎調用示例

 'UTF-8',      // 請求編碼
    'soap_version'  => SOAP_1_1,     // .NET ASMX 通常用 1.1
    'trace'         => 1,            // 啟用調試:__getLastRequest/Response
    'exceptions'    => true,         // 拋出 SoapFault 異常
    'cache_wsdl'    => WSDL_CACHE_MEMORY, // 緩存 WSDL 提升性能
]);

// 參數結構:外層數組鍵對應方法名參數,內層為參數名值對
// .NET 方法: public DataTable GetArticle(string param1, string param2)
// WSDL 定義 message 部分對應
$params = [
    'param1' => '01',
    'param2' => '02',
];

try {
    // 調用方式:__soapCall 或魔術方法
    $result = $client->__soapCall('GetArticle', [$params]); // 參數需再包一層數組
    // 或 $result = $client->GetArticle($params); (若 WSDL 映射正確)
    
    // .NET 返回 DataTable 通常序列化為 XML 字符串或結構化對象
    // 需根據實際返回結構解析
    $data = $result->GetArticleResult; // 訪問返回對象屬性
    print_r($data);
    
} catch (SoapFault $fault) {
    // 捕獲 SOAP Fault
    error_log("SOAP Fault: [{$fault->faultcode}] {$fault->faultstring}");
    // 可進一步解析 $fault->detail
    throw new RuntimeException("WebService 調用失敗: {$fault->faultstring}");
}

關鍵避坑指南

  • print_r($result) 觀察結構
  • DataTable 常返回 <diffgr:diffgram>...</diffgr:diffgram> XML 字符串,需 simplexml_load_string 解析
  • DataSet 可能返回多表結構
  • 坑點現象解決方案
    參數結構層級 參數不生效/報錯 "Object reference not set" 參數必須再包一層數組:__soapCall('Method', [$params]),$params 為關聯陣列。單層數組會被解析為位置參數導致錯誤。
    編碼不匹配 中文亂碼 / "Invalid XML" 1. SoapClient 構造函數 'encoding'=>'UTF-8'
    2. .NET 服務端 <globalization requestEncoding="utf-8" responseEncoding="utf-8"/>
    3. 參數值確為 UTF-8 (非 GBK)
    返回值解析 返回 stdClass 對象、XML 字符串、DataSet/XMLNode 混雜
    WSDL 緩存導致修改不生效 服務端更新接口,客戶端仍報舊錯誤 'cache_wsdl' => WSDL_CACHE_NONE (開發) 或重啟 PHP-FPM/清除 /tmp/wsdl-* 文件
    HTTPS WSDL/端點證書驗證失敗 "Failed to load external entity" 1. 配置 'stream_context' => stream_context_create(['ssl'=>['verify_peer'=>false,'verify_peer_name'=>false]]) (測試)
    2. 生產:'stream_context' => stream_context_create(['ssl'=>['cafile'=>'/path/ca.pem']])
    命名空間前綴衝突 調用多個同名方法的不同服務 實例化多個 SoapClient,或使用 SoapVar 顯式指定命名空間

    生產級封裝類

    wsdl = $wsdl;
            $defaultOptions = [
                'encoding'      => 'UTF-8',
                'soap_version'  => SOAP_1_1,
                'trace'         => true,
                'exceptions'    => true,
                'cache_wsdl'    => WSDL_CACHE_MEMORY,
                'connection_timeout' => 10,
            ];
            $this->client = new SoapClient($wsdl, array_merge($defaultOptions, $options));
        }
    
        public function call(string $method, array $params = []) {
            try {
                // 統一參數結構:外層數組包裹
                $result = $this->client->__soapCall($method, [$params]);
                
                // 記錄調試信息
                $this->traceLog[] = [
                    'method'   => $method,
                    'request'  => $this->client->__getLastRequest(),
                    'response' => $this->client->__getLastResponse(),
                    'headers'  => $this->client->__getLastRequestHeaders(),
                ];
                
                return $this->normalizeResult($result);
            } catch (SoapFault $fault) {
                $this->traceLog[] = [
                    'method' => $method,
                    'fault'  => [
                        'code'    => $fault->faultcode,
                        'string'  => $fault->faultstring,
                        'detail'  => $fault->detail,
                    ],
                ];
                throw new RuntimeException("[$method] SOAP Fault: {$fault->faultstring}", $fault->faultcode);
            }
        }
    
        private function normalizeResult($result) {
            // .NET 常見返回模式標準化
            if (is_object($result)) {
                // 單一返回值對象: GetArticleResult 屬性
                foreach ($result as $key => $val) {
                    if (str_ends_with($key, 'Result')) {
                        return $this->parseDotNetData($val);
                    }
                }
                // 直接返回對象屬性陣列
                return (array)$result;
            }
            return $result;
        }
    
        private function parseDotNetData($data) {
            // DataTable/XML 字符串解析
            if (is_string($data) && str_starts_with(trim($data), '<')) {
                $xml = simplexml_load_string($data);
                if ($xml) {
                    $json = json_encode($xml);
                    return json_decode($json, true);
                }
            }
            return $data;
        }
    
        public function getTraceLog(): array {
            return $this->traceLog;
        }
    
        public function getLastRequest(): string {
            return $this->client->__getLastRequest();
        }
    }
    
    // 使用
    $svc = new DotNetSoapClient('https://example.net/Service.asmx?WSDL');
    try {
        $data = $svc->call('GetArticle', ['param1' => '01', 'param2' => '02']);
        print_r($data);
    } catch (Exception $e) {
        error_log($e->getMessage());
        // 可檢查 $svc->getTraceLog() 進行深度調試
    }

    .NET 端配置建議 (供對接參考)

    
    
      
        
          
          
          
        
      
      
    
    
    
    
      
      
        
          
        
      
    

    SOAP 1.2 請求/響應結構參考

    POST /SearchService.asmx HTTP/1.1
    Content-Type: application/soap+xml; charset=utf-8
    SOAPAction: "https://tempuri.org/GetArticle"
    
    
    
      
        
          01
          02
        
      
    

    總結

    PHP 調用 .NET WebService 核心在於:**SoapClient 正確配置 (編碼/版本/異常/追蹤)、參數雙層數組封裝、返回值結構適配 (DataTable XML 解析)、完善的異常捕獲與調試日誌** 封裝。掌握這些,跨平台 SOAP 集成將不再是痛點。

    1. 上一篇文章:
    2. 下一篇文章:
    Copyright © 程式碼之家 All Rights Reserved