跳轉到內容

WebObjects/Web 服務/問題

來自華夏公益教科書

本節描述使用WebObjects Web 服務時有時會遇到的一些問題和錯誤。

DirectToWebService 無法在 WSDL 中返回包含安全 HTTPS 引用

[編輯 | 編輯原始碼]
作者: Francis Labrie
受影響的產品: WebObjects 5.2.x、5.3.x
錯誤引用: rdar://3546304

DirectToWebService 定義的 Web 服務不會返回正確的 WSDL 文件,即使遵循了正確的過程(請參見安全 Web 服務)。因此,只有使用 com.webobjects.appserver.WOWebServiceRegistrar 類手動註冊的類面向的 Web 服務才能生成正確的 WSDL。

解決方案

[編輯 | 編輯原始碼]

來自 Apple 的 Darel Lee 告訴我,現在,動態 WSDL 生成對開發者不可用,因此目前沒有乾淨的解決方案來執行此操作。一種解決方法是為要使用安全 HTTPS 引用進行操作的每個操作,使用 serviceLocationURL 鍵硬編碼規則(com.webobjects.directtoweb.Assignment 型別)。例如

((operationName="anOperation") and (serviceName="Service")) -> 
 	serviceLocationURL="https://host.net/cgi-bin/Service.woa/ws/Service"

如果您需要所有操作都使用安全協議進行呼叫,您也可以定義如下更通用的規則

(serviceName="Service") -> 
	serviceLocationURL="https://host.net/cgi-bin/Service.woa/ws/Service"

使用 WOWebServiceRegistrar 類註冊的 SOAP 序列化器和反序列化器未顯示在 WSDL 架構中

[編輯 | 編輯原始碼]
作者: Francis Labrie
受影響的產品: WebObjects 5.2.x、5.3.x
錯誤引用: rdar://3546330

使用 com.webobjects.appserver.WOWebServiceRegistrar 類向 Web 服務註冊的自定義 SOAP 序列化器和反序列化器從未新增到 WSDL 的型別/架構定義中。顯示的唯一型別定義如下

  <types>
    <schema targetNamespace="http://lang.java/" xmlns:soapenc=
      "http://schemas.xmlsoap.org/soap/encoding/" xmlns=
      "http://www.w3.org/2001/XMLSchema">
      <complexType name="Class">
         <sequence/>
      </complexType>
      <complexType name="ArrayOf_xsd_any">
        <complexContent>
          <restriction base="soapenc:Array">
            <attribute ref="soapenc:arrayType" wsdl:arrayType=
              "xsd:any[]"/>
          </restriction>
        </complexContent>
      </complexType>
      <element name="ArrayOf_xsd_any" nillable="true" type=
        "lang:ArrayOf_xsd_any"/>            
    </schema>
    <schema targetNamespace="http://www.apple.com/webobjects/
      webservices/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/
      soap/encoding/" xmlns="http://www.w3.org/2001/XMLSchema">
      <complexType name="EOGlobalID">
        <element name="entityName" type="xsd:string"/>
        <element name="primaryKeys" type="lang:ArrayOf_xsd_any"/>
      </complexType>
      <element name="EOGlobalID" type="tns:EOGlobalID"/>                        
      <complexType name="EOEnterpriseObject">
        <element name="entityName" type="xsd:string"/>
        <element name="globalID" type="webobjects:EOGlobalID"/>
        <element name="properties" type="soapenc:Struct"/>
      </complexType>
    </schema>
  </types>

解決方案

[編輯 | 編輯原始碼]

我現在不知道任何動態解決方法... 但是可以將靜態完整 WSDL 透過直接操作共享。不過不太方便...

DirectToWebService 無法在 WSDL 中返回具有自定義名稱空間和定義名稱

[編輯 | 編輯原始碼]
作者: Francis Labrie
受影響的產品: WebObjects 5.2.x、5.3.x
錯誤引用

DirectToWebService 定義的 Web 服務無法返回具有自定義名稱空間和定義名稱屬性值的 WSDL。更糟糕的是,生成的名稱空間甚至可能包含 WebObjects 應用程式例項編號或錯誤的主機名。

解決方案

[編輯 | 編輯原始碼]

根據 Darel Lee 的提示,我在 com.webobjects.webservices.generation._private.WOWSDLTemplate 類中發現了一些從 user.d2wmodel DirectToWebService 規則檔案中讀取的額外部索引鍵定義。例如

  • serviceLocationURL:一個允許設定操作的定位 URL 的鍵。如果您需要使用安全 HTTPS 引用來訪問您的 Web 服務,這將非常有用;
  • WSDLDefinitionName:一個允許更改定義名稱的鍵。因此,您可以設定此值,而不是使用“ServiceNameDefinition”;
  • WSDLTargetNamespace:一個允許更改名稱空間的鍵。這最有用了:您可以避免使用 WebObjects HTTP 介面卡?的動態生成。

以下是以更改上述值的規則定義示例

(serviceName="Service") -> 
	WSDLTargetNamespace="https://host.net/cgi-bin/Service.woa/ws/Service"
(serviceName="Service") -> 
	WSDLDefinitionName="AnotherDefinition"
((operationName="anOperation") and (serviceName="Service")) -> 
	serviceLocationURL="https://host.net/cgi-bin/Service.woa/ws/Service"

WOWebServiceClient 類無法連線到需要身份驗證的伺服器 (WO 5.2.x,錯誤 ID 3568441)

[編輯 | 編輯原始碼]
作者: Francis Labrie
受影響的產品: WebObjects 5.2.x、5.3.x
錯誤引用: rdar://3568441

com.webobjects.webservices.client.WOWebServiceClient 類無法連線到需要基本 HTTP 身份驗證的伺服器,儘管該類提供了註冊安全委託的方法(請參見 setSecurityDelegateForServiceNamed(Object, String) 例項方法)。

通常,processClientRequest(MessageContext) 委託方法(請參見 com.webobjects.webservices.support.WOSecurityDelegate 介面文件)將提供一種簡單的方法來將使用者名稱和密碼設定為訊息上下文。但是,類設計存在問題:要註冊安全委託,WOWebServiceClient 類必須獲取 Web 服務定義語言 (WSDL) XML 文件。但是要訪問此 WSDL,必須設定身份驗證標頭。這就是典型的先有雞還是先有蛋的問題...

解決方案

[編輯 | 編輯原始碼]

最好的做法是在 `WOWebServiceClient` 類中新增一個預設方法,在類獲取 WSDL 之前註冊一個與服務名稱無關的預設安全代理。但不幸的是,所有允許這種行為變化的關鍵方法都是私有的,所以子類化不是解決方案......

但是仍然可以採用一種變通方法

  1. 使用 `java.net.URL` 例項自行獲取 WSDL 文件並將其儲存到本地檔案系統,並設定 `java.net.URLConnection` 的基本 HTTP 身份驗證標頭欄位;
  2. 例項化另一個引用本地 WSDL 文件檔案的 `java.net.URL` 類;
  3. 使用檔案 URL 例項化 `com.webobjects.webservices.client.WOWebServiceClient` 類;
  4. 為每個服務設定一個安全代理,該代理將為基本 HTTP 身份驗證新增適當的憑據資訊。

就是這樣。看起來像一個很大的駭客,但它確實有效......

Web 服務無法返回帶有安全 HTTPS 引用且指定埠不為預設 443 的 WSDL

[編輯 | 編輯原始碼]
作者: Francis Labrie
受影響的產品: WebObjects 5.2.x、5.3.x
Bug 引用: rdar://4196417

HTTPS 協議引用可以在 Web 服務 WSDL 中釋出。但不幸的是,WebObjects 似乎忽略了除預設 443 之外的其他埠。

這個問題與 `com.webobjects.appserver.WORequest` 構建 URL 字首的方式有關:如果協議是安全的並且在呼叫 `_completeURLPrefix(StringBuffer,boolean,int)` 方法時未設定埠(即 0),則埠將始終為 443。不幸的是,Web 服務 `com.webobjects.appserver._private.WOWebService` 類似乎在沒有設定埠號的情況下呼叫了此方法。

解決方案

[編輯 | 編輯原始碼]

要解決此錯誤,您可以像這樣對 `com.webobjects.appserver.WORequest` 類進行子類化

package com.smoguli.appserver;

import com.webobjects.appserver.WORequest;
import com.webobjects.foundation.NSData;
import com.webobjects.foundation.NSDictionary;

/**
 * This class provide fixed {@link com.webobjects.appserver.WORequest} methods.
 * To use it, just overload the {@link com.webobjects.appserver.WOApplication.
 * createRequest(String,String,String,NSDictionary,NSData,NSDictionary)} method 
 * to instanciate this class instead.
 *
 * @author		Francis Labrie <francis.labrie at smoguli.com>
 */
public class WOFixedRequest extends WORequest {

	/**
	 * @see com.webobjects.appserver.WORequest#WORequest(String,String,String,
	 * NSDictionary,NSData,NSDictionary)
	 */
	public WOFixedRequest(String method, String url, String httpVersion, NSDictionary headers, NSData content, NSDictionary info) {
		    super(method, url, httpVersion, headers, content, info);
	} // WOFixedRequest

	/**
	 * This method builds the URL prefix into the <code>urlPrefix</code> buffer 
	 * with the appropriate protocol (<code>http</code> or <code>https</code>) 
	 * and the right TCP port. But unlike the {@link com.webobjects.appserver.
	 * WORequest#_completeURLPrefix(StringBuffer,boolean,int} method, it 
	 * supports secure HTTP protocol (<code>https</code>) with port other than 
	 * <code>443</code>, even if the <code>port</code> parameter is set 
	 * <code>0</code>.
	 *
	 * @param urlPrefix				the buffer that receives the contructed URL.
	 * @param isSecure				a flag indicating if the protocol is secure.
	 * @param port					the port number.
	 */
	public void _completeURLPrefix(StringBuffer urlPrefix, boolean isSecure, int port) {
		if(isSecure && (port == 0)) {
			String serverPort;

			serverPort = _serverPort();
			if((serverPort != null) && !serverPort.equals("443")) {
				try {
					port = Integer.parseInt(serverPort);
				} catch(NumberFormatException exception) {} // catch
			} // if
		} // if

		super._completeURLPrefix(urlPrefix, isSecure, port);
	} // _completeURLPrefix
} // WOFixedRequest
華夏公益教科書