跳转到内容

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
华夏公益教科书