应该感谢那些指出你错误的人

借我三千虎骑,复我泱泱中华!

博客园 首页 新随笔 联系 订阅 管理

下载本文代码:XMLFiles0208.exe (49 KB)

问题为什么在 Microsoft .NET 框架的最终发布版本中 .vsdisco 文档不能继续使用?

回答在 .NET 框架的测试版本中,.vsdisco HTTP 处理程序是自动启用的,但在最终的发布版本中 .NET 小组决定在默认情况下禁用这项特性。 如果打开 machine.config(其路径大致是 c:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\CONFIG\machine.config)并在 /configuration/system.web/httpHandler 下搜索 .vsdisco,就可注意到其中注释掉了 .vsdisco HTTP 处理程序条目:

<!-- machine.config -->
???
<httpHandlers>
<!--
<add verb="*" path="*.vsdisco"
type="System.Web.Services.Discovery.DiscoveryRequestHandler,
System.Web.Services, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
-->
<add verb="*" path="*.aspx"
type="System.Web.UI.PageHandlerFactory"/>
???

如果打开 Microsoft Internet 信息服务 (IIS) 管理控制台并检查在 .NET 框架安装过程中所配置的默认应用程序映射关系,就可看到 .vsdisco 文档的应用程序映射依然存在(参见图 1)。 这意味着在默认情况下针对 .vsdisco 文档的 HTTP GET 请求将由静态文件处理程序 (System.Web.StaticFileHandler) 进行处理,该程序将返回 .vsdisco 文档的内容,而不是动态发现算法在给定的 vroot 内对 Web 服务说明进行搜索的结果。

图 1 应用程序映射

图 1 应用程序映射

要在整台计算机范围内支持动态发现特性,取消对 .vsdisco httpHandler 条目的注释。 如果想要在整台计算机的级别上保持禁用动态发现特性,您可以将 .vsdisco 添加到特定 vroot 中的 web.config 文件中,从而仅在 vroot 的级别上启用该特性,如下所示:

<!-- web.config -->
<configuration>
<system.web>
<httpHandlers>
path="*.vsdisco"
type="System.Web.Services.Discovery.DiscoveryRequestHandler,
System.Web.Services, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
pHandlers>
</system.web>
</configuration>

您只需将此 web.config 文件添加到任何需要动态发现特性的 vroot 中(假设该 vroot 尚未删除 .vsdisco 应用程序映射关系)。

在默认情况下禁用 .vsdisco 处理程序,这一点是合理的,因为它允许客户端检索一台计算机的 Web 服务配置的某些详细情况。 如果启用这个程序,开发人员就不得不在允许客户端检索这些信息之前作出是否允许检索的决定。

问题如果我想要管理 Web 服务的 WSDL 说明,如何才能让 .asmx 处理程序返回静态的 WSDL 文件?

回答即使从静态的 WSDL 文档生成一个 ASP.NET 页面,ASP.NET 基础结构在每次接收到 ?wsdl 查询字符串时还是动态地生成一个新的 WSDL 文档。 动态生成的 WSDL 文档将等效于原来用作类的起始点的静态 WSDL 文档,但可能不完全相同(其中将不包括可能已添加的注释或其他扩展信息)。

通过一些自定义的属性,您可以显式实现一个现有的 WSDL 绑定,以此要求 ASMX 处理程序导入静态 WSDL 文档。 首先,需要使用 WebServiceBinding 属性批注类声明,以便指定该类所实现的 WSDL 绑定的名称(这就是 WSDL 文档中该绑定的名称)并通过 Location 属性提供 WSDL 文档的位置。 然后,需要利用 SoapDocumentMethod 属性批注每个 WebMethod 并指定其为同一个绑定实现的一部分(通过 Binding 属性)。 有关完整示例,请参见图 2

现在,如果针对 .asmx 终结点发出一条 HTTP GET 请求,您将获得一个简单得多的动态生成的 WSDL 文档,而该文档仅通过 WSDL 导入元素来导入静态 WSDL 文档生成:

<import namespace="urn:geometry" location="GeometryService.wsdl" />

在生成代理的时候,WSDL 工具(例如 wsdl.exe)应该足够智能以便处理这种导入语句。

问题我对 ASP.NET 为 JIT 编译所提供的 codebehind 属性实在感到很迷惑。 它工作起来与我预期的不太一样。

回答JIT 编译语义带有三种 HTTP 终结点类型: .ashx、.asmx 和 .aspx(参见 图 3)。 每个类型都应在页面顶部具有相应的 ASP.NET 指令。 以下示例是一个 .asmx 页面中的 @WebService 指令:

<%@ WebService language="C#" class="GeometryServiceImpl" %>

这些指令通常要求用户指定目标类以及实现语言,您可看到,在默认情况下,类的代码应该与相应指令位于同一个文件中。 如果满足此条件,则类就会如您所料地进行 JIT 编译。 否则,所进行的操作将取决于终结点的类型。

对于 .ashx 和 .asmx 文件,处理程序将在 vroot 的 bin 目录或包含所引用类的全局程序集缓存 (GAC) 中查找程序集。 对于这些文件类型,您不能既使代码和指令分离,同时又利用 JIT 编译功能。 相反,在测试 .ashx/.asmx 终结点之前您必须手工编译该程序集。

对于 .aspx 文件,处理程序将检查 @Page 指令上的 Src 属性,如下所示:

<%@ Page Src="code.cs" ... %>

如果找到,它将用 JIT 编译引用的文件。 否则,它就假定您将手工编译从 Page 派生出的类。 在这种情况下,您必须通过 Inherits 属性指出类的名称,如下所示:

<%@ Page Inherits="MyPageDerivedClass" ... %>

请注意,只有 .aspx 页面才支持 Src 属性,因此只有这种文件类型才支持对单独的源文件进行 JIT 编译。

就这点而言,codebehind 属性与 JIT 编译毫不相关,甚至跟 ASP.NET 都没有关系。 codebehind 只不过是 Visual Studio .NET IDE 特有的一种属性,用于将 .aspx 或 .asmx 文件与其底层的代码关联起来。 例如,当在 Visual Studio .NET 中创建一个 Web 应用程序项目时,解决方案资源管理器使其看起来只创建了单个 WebForm1.aspx 文件。 这只是一种假象。 如果按解决方案资源管理器工具栏上的“显示所有文件”按钮(参见图 4),您将看到其中有一个名为 WebForm1.aspx.cs 的隐藏的源代码文件。在记事本中打开该文件即可看到如下 @Page 指令:

<%@ Page language="c#" codebehind="WebForm1.aspx.cs"
AutoEventWireup="false"
Inherits="WebApplication1.WebForm1" %>

请注意,其中使用了 codebehind 属性以找出哪个源文件与此 .aspx 页面相关。 此外,它还使用 Inherits 属性(而非 Src 属性)。 在默认情况下,Visual Studio .NET 从不使用 Src 属性,因此,在测试之前您必须始终编译 Visual Studio .NET Web 应用程序。要在整台计算机范围内支持动态发现特性,取消对 .vsdisco httpHandler 条目的注释。 如果想要在整台计算机的级别上保持禁用动态发现特性,您可以将 .vsdisco 添加到特定 vroot 中的 web.config 文件中,从而仅在 vroot 的级别上启用该特性,如下所示:

<!-- web.config -->
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="*.vsdisco"
type="System.Web.Services.Discovery.DiscoveryRequestHandler,
System.Web.Services, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
</httpHandlers>
</system.web>
</configuration>

您只需将此 web.config 文件添加到任何需要动态发现特性的 vroot 中(假设该 vroot 尚未删除 .vsdisco 应用程序映射关系)。

在默认情况下禁用 .vsdisco 处理程序,这一点是合理的,因为它允许客户端检索一台计算机的 Web 服务配置的某些详细情况。 如果启用这个程序,开发人员就不得不在允许客户端检索这些信息之前作出是否允许检索的决定。

问题如果我想要管理 Web 服务的 WSDL 说明,如何才能让 .asmx 处理程序返回静态的 WSDL 文件?

回答即使从静态的 WSDL 文档生成一个 ASP.NET 页面,ASP.NET 基础结构在每次接收到 ?wsdl 查询字符串时还是动态地生成一个新的 WSDL 文档。 动态生成的 WSDL 文档将等效于原来用作类的起始点的静态 WSDL 文档,但可能不完全相同(其中将不包括可能已添加的注释或其他扩展信息)。

通过一些自定义的属性,您可以显式实现一个现有的 WSDL 绑定,以此要求 ASMX 处理程序导入静态 WSDL 文档。 首先,需要使用 WebServiceBinding 属性批注类声明,以便指定该类所实现的 WSDL 绑定的名称(这就是 WSDL 文档中该绑定的名称)并通过 Location 属性提供 WSDL 文档的位置。 然后,需要利用 SoapDocumentMethod 属性批注每个 WebMethod 并指定其为同一个绑定实现的一部分(通过 Binding 属性)。 有关完整示例,请参见图 2

现在,如果针对 .asmx 终结点发出一条 HTTP GET 请求,您将获得一个简单得多的动态生成的 WSDL 文档,而该文档仅通过 WSDL 导入元素来导入静态 WSDL 文档生成:

<import namespace="urn:geometry" location="GeometryService.wsdl" />

在生成代理的时候,WSDL 工具(例如 wsdl.exe)应该足够智能以便处理这种导入语句。

问题我对 ASP.NET 为 JIT 编译所提供的 codebehind 属性实在感到很迷惑。 它工作起来与我预期的不太一样。

回答JIT 编译语义带有三种 HTTP 终结点类型: .ashx、.asmx 和 .aspx(参见 图 3)。 每个类型都应在页面顶部具有相应的 ASP.NET 指令。 以下示例是一个 .asmx 页面中的 @WebService 指令:

<%@ WebService language="C#" class="GeometryServiceImpl" %>

这些指令通常要求用户指定目标类以及实现语言,您可看到,在默认情况下,类的代码应该与相应指令位于同一个文件中。 如果满足此条件,则类就会如您所料地进行 JIT 编译。 否则,所进行的操作将取决于终结点的类型。

对于 .ashx 和 .asmx 文件,处理程序将在 vroot 的 bin 目录或包含所引用类的全局程序集缓存 (GAC) 中查找程序集。 对于这些文件类型,您不能既使代码和指令分离,同时又利用 JIT 编译功能。 相反,在测试 .ashx/.asmx 终结点之前您必须手工编译该程序集。

对于 .aspx 文件,处理程序将检查 @Page 指令上的 Src 属性,如下所示:

.<%@ Page Src="code.cs" ... %>

如果找到,它将用 JIT 编译引用的文件。 否则,它就假定您将手工编译从 Page 派生出的类。 在这种情况下,您必须通过 Inherits 属性指出类的名称,如下所示:

<%@ Page Inherits="MyPageDerivedClass" ... %>

请注意,只有 .aspx 页面才支持 Src 属性,因此只有这种文件类型才支持对单独的源文件进行 JIT 编译。

就这点而言,codebehind 属性与 JIT 编译毫不相关,甚至跟 ASP.NET 都没有关系。 codebehind 只不过是 Visual Studio .NET IDE 特有的一种属性,用于将 .aspx 或 .asmx 文件与其底层的代码关联起来。 例如,当在 Visual Studio .NET 中创建一个 Web 应用程序项目时,解决方案资源管理器使其看起来只创建了单个 WebForm1.aspx 文件。 这只是一种假象。 如果按解决方案资源管理器工具栏上的“显示所有文件”按钮(参见图 4),您将看到其中有一个名为 WebForm1.aspx.cs 的隐藏的源代码文件。在记事本中打开该文件即可看到如下 @Page 指令:

<%@ Page language="c#" codebehind="WebForm1.aspx.cs"
AutoEventWireup="false"
Inherits="WebApplication1.WebForm1" %>

请注意,其中使用了 codebehind 属性以找出哪个源文件与此 .aspx 页面相关。 此外,它还使用 Inherits 属性(而非 Src 属性)。 在默认情况下,Visual Studio .NET 从不使用 Src 属性,因此,在测试之前您必须始终编译 Visual Studio .NET Web 应用程序。

图 4 显示所有文件

图 4 显示所有文件

对于 Visual Studio .NET Web 服务项目而言,情况是类似的。 在创建新 Web 服务项目之后,Visual Studio .NET 实际创建了两个文件(确保按下了“显示所有文件”按钮): Service1.asmx 和 Service1.asmx.cs。打开 Service1.asmx 即可看到以下代码:

<%@ WebService Language="c#" codebehind="Service1.asmx.cs"
Class="WebService1.Service1" %>

同样,这意味着在试图使用 .asmx 终结点之前您必须编译该 Web 服务。

问题ASP.NET Web 服务类必须派生自 System.Web.Services.WebService 吗?

回答不。从 System.Web.Services.WebService 中派生是为了便于使用某些 HttpContext 属性。 例如,以下类并不是从 WebService 中派生出来的,但仍然可用作一个 .asmx 终结点:

public class MathService {
[System.Web.Services.WebMethod]
public int Add(int n1, int n2) {
return n1 + n2;
}
}

由于 .asmx 绑定的类实际上是对底层 HTTP 处理程序的一种抽象,您的确能够访问当前 HTTP 请求的 HttpContext 对象。 您可通过 HttpContext.Current 静态属性来访问当前的对象。 一旦获得 HttpContext 对象,您就能够访问其所有属性,如 Application、Session、Server 和 User。

using System.Web;
using System.Web.Services;
public class MathService {
[WebMethod]
public int Add(int n1, int n2) {
int sum = n1 + n2;
HttpContext.Current.Application["last_sum"] = sum;
return sum;
}
}

从 System.Web.Services.WebService 中派生出类只是更便于您访问这些属性:

using System.Web;
using System.Web.Services;
public class MathService : WebService {
[WebMethod]
public int Add(int n1, int n2) {
int sum = n1 + n2;
Application["last_sum"] = sum;
return sum;
}
}

问题我想要使 ASP.NET Web 服务仅支持 SOAP 协议,而不支持 HTTP GET 和 POST。 我如何才能禁用这些协议?

回答ASP.NET 在默认情况下支持多种 Web 服务协议。 您可以在 XML 路径 /configuration/system.web/webServices/protocols 下的 machine.config 中找到这些协议。 在默认情况下,其中列出了四种: HttpSoap、HttpGet、HttpPost 和 Documentation。 只需注释掉相应的 add 项即可在整台计算机上禁用这些协议:

<!-- machine.config -->
<configuration>
<system.web>
<webServices>
<protocols>
<add name="HttpSoap"/>
<!-- disable for entire machine
<add name="HttpPost"/>
<add name="HttpGet"/>
-->
<add name="Documentation"/>
???
</configuration>

如果想要保持在默认情况下启用这些协议而仅在某个 vroot 下禁用,请在该 vroot 的 web.config 中删除相应元素:

<!-- web.config -->
<configuration>
<system.web>
<webServices>
<protocols>
<remove name="HttpPost"/>
<remove name="HttpGet"/>
???
</configuration>

HttpGet 和 HttpPost 协议的设计旨在便于基于浏览器的 Web 服务客户端使用(二者都需要基于 HTML FORM 的输入)。 出于安全考虑,除非明确需要这种支持,否则最好禁用这些协议。 对于想要进行破坏活动的 Web 黑客来说,可以使用的 HTTP 欺诈伎俩简直太多了。

问题有没有可能自定义在定位到某个 .asmx 终结点时所创建的文档页面?

回答有。 默认的 .asmx 文档是从 .aspx 页面中生成的,该页面与其他框架 .config 文件位于同一位置 (c:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\CONFIG)。 文件名为 DefaultWsdlHelpGenerator.aspx。观察该文件的 Page_Load 事件,以开始了解其如何工作。 如果只想改变该页面的外观并添加一些说明性文本,您只需修改该页面中的 HTML 模板而不用触及代码。 但是如果您真的雄心勃勃,您也可以完全从头开始编写自己的文档生成器。

您可以指定哪个 .aspx 文件供文档处理程序使用。 在 /configuration/system.web/webServices 下的 machine.config 中,您将找到 wsdlHelpGenerator 元素,该元素允许您指定 .aspx 页面。 您可以修改整台计算机的 href 属性,或者您也可以在 web.config 中针对特定的 vroot 而重写它的值,如下所示:

<!-- web.config -->
<configuration>
<system.web>
<webServices>
<wsdlHelpGenerator href="MyDocs.aspx" />
</webServices>
</system.web>
</configuration>

问题有没有简单的方法来测试调用基于 SOAP 的 Web 服务?

回答您必须发出一个 HTTP POST 请求才能测试基于 SOAP 的 Web 服务。 HttpGet 协议易于进行快速的基于浏览器的测试,但是要测试 SOAP 协议,唯一的方法就是通过一个显式 SOAP POST。 您可以编写代码通过程序发出请求(参见 System.Net),或者您也可以使用一个提供有该功能的实用工具。 我一直使用一个名为 post.js 的简单 JScript 实用工具来进行这类测试;它需要 MSXML 3.0 并提供以下功能:

usage: post uri [options]
-f inputFile
-s inputString
-o outputFile
-h headerName headerValue

请注意,这个工具允许您在命令行中指定任意的 HTTP 标头,并且您可以发送一个字符串或者整个文件的内容。 此命令行调用一个 .asmx 终结点:

C:\temp>post http://localhost/geo/geometryservice.asmx
-h Content-Type "text/xml"
-h SOAPAction "urn:geometry#CalcDistance"
-f req.xml

对于给定的操作,req.xml 文件中包含适当的 SOAP 请求消息。 请注意,当测试 .asmx 终结点时,您必须提供正确的 SOAPAction 标头以及一个 text/xml 的内容类型标头。 您可与本专栏的示例代码一起下载 post.js,或者从我的 Web 站点下载。

问题要在 ASP.NET WebMethod 内生成一个 SOAP 错误,最简单的方法是什么?

回答实际上,您不必进行任何操作。 WebMethod 基础结构自动将未处理的异常转换为 SOAP 响应中的错误元素。 想要显式生成一个 SOAP 错误时,请使用 SoapException 类:

public class MathService {
[WebMethod]
public int Divide(int n1, int n2) {
if (n2 == 0)
throw new SoapException("Cannot divide by 0",
SoapException.ClientFaultcode);
return n1 + n2;
}
}
posted on 2006-04-26 08:53  落拓孤鸿  阅读(873)  评论(0编辑  收藏  举报