帮酷LOGO
  • 显示原文与译文双语对照的内容

介绍

任何一直在构建. NET 应用程序框架, 自定义控件或者只喜欢分离它的关注点的人都会遇到同样的问题。 我希望我能帮助你们,而不需要去做重复我所做的事情,但是, 让我从解释这个问题开始。 这是一个相当长的故事,所以在这篇文章中,这篇文章的层次会出现在很多不同领域。

问题

当你希望使用 Javascript call-back功能开发定制控件时,可以使用ICallBack和IScriptable接口。 这些接口实现了来自你的类的调用,并通过使用Javascript返回到你的类,而不必实现更新面板或者页面刷新。 但是,ICallback接口有一个问题: 它没有漂亮的JSON ( e ) 序列化。 它只限于每个组件一个函数,虽然实现更多的实现是可能的,但它并不十分容易管理。

它应该是怎样

如果你曾经在 ASP.NET 中使用过 web Webservices,你将会看到这种机制的威力。 尤其是与 ASP.NET Ajax 1.0结合使用的情况下,它为你生成了一个 Javascript 。 对于那些没有使用 ASP.NET web service的人,下面是一个简短的概述: 在项目中创建一个ASMX文件,用包含一些属性的类填充它,然后你就完成了 ! 没有SOAP创建,没有 JSON ( e ) 序列化,没有代理写入。 你仍然需要做的事情是使用你的<> 注册它,以便生成Javascript代理。 在我们的定制控件库中,这不是很好?

还有另一个问题

这里方法的最大问题在于,这里功能仅适用于web目录中的not-yet-compiled ASMX文件。 你没有"走出盒子"方法将这里功能指向已经编译的程序集。 为了解决这个问题,我们首先看看当前的功能是如何工作的。

默认情况下它的工作方式

ASMX文件通过HTTPHandler转换成一个web服务和Javascript代理。 这里HTTPHandler在 config.xml 文件中设置。

<httpHandlers><removeverb="*"path="*.asmx"/><addverb="*"path="*.asmx"validate="false"type="System.Web.Script.Services.ScriptHandlerFactory"/></httpHandlers>

你在这里看到的处理程序是默认情况下在 new"ASP.NET Ajax-enabled Web应用程序中创建的。"当对扩展名为ASMX的文件执行请求时(" *.asmx"),它将该请求转发到 ScriptHandlerFactory 。 然后,scripthandler工厂为你创建web服务。Javascript代理或者Javascript方法处理程序。 将web服务添加到 ScriptManager 时,将生成 <script src="Webserver.asmx/js"/> 在你的网页上。此类的问题是它将检查ASMX文件,而不检查其他文件。 这个类有一个更大的问题,因为几乎所有的东西都是 InternalPrivate 。 如果你不知道这意味着什么,那意味着扩展或者改变任何东西都是不可能的。

反射它

好吧可能是不可能。 所有的东西都编译成托管代码,通过反射,我们可以进入我们喜欢的每个小角落。 即使是私有构造器,也可以通过. NET 框架中最强大的组件之一。 这里有很多关于反射的教程,所以在本文中,我将假设你了解基本知识。

开始解决方案

,empty"ASP.NET Ajax的Web应用程序 start" ASP.NET Ajax 1.0. 然后,我们将第二个空 C# 类库项目添加为第二个项目,并在第一个项目中引用第二个项目。

AutoDiscoveryWebServiceHandlerFactory

为了实现我们自己的代码,我们将在CustomControl库中创建自己的HttpHandlerFactory 。 我们将它命名为 AutoDiscoveryWebServiceHandlerFactory,接口为 IHttpHandlerFactory,它实现:

public IHttpHandler GetHandler(HttpContext context, 
 string requestType, string url, string pathTranslated)

publicvoid ReleaseHandler

然后在Web应用程序中,将 config.xml 文件更改为将ASMX请求转发到我们自己的处理程序工厂。

<httpHandlers><removeverb="*"path="*.asmx"/><addverb="*"path="*.asmx"validate="false"type="Devusion. Services.AutoDiscoveryWebServiceHandlerFactory"/></httpHandlers>

自定义网络服务

接下来,我们创建自己的web服务,而不是普通的ASMX文件,而是普通的C# 类文件。

namespace Devusion.CustomControls
{
 [ScriptService]
 [WebService(Namespace = "<a href="%22http://www.devusion.nl/xmlns/customcontrols%22">http://www.devusion.nl/xmlns/customcontrols</a>")]publicclass Service : WebService
 {
 [WebMethod]
 [ScriptMethod(UseHttpGet = true)]
 publicstring HelloWorldYou(string name)
 {
 return DateTime.Now.ToLongTimeString()+ ": Hello" + name;
 }
 }
}

WebServiceHandlerFactory

现在让我们编写可以调用/Devusion. CustomControls.Service.asmx的代码。 这将调用我们的AutoDiscoveryWebServiceHandlerFactory.GetHandler 函数。 这里我们必须将URL转换为类的类型。 我们通过循环加载已经加载的程序集并检查web服务属性来执行这里操作。 为了更好的性能,应该进行优化。 如果我们找不到匹配类型,我们将返回 null 。

private Type GetServiceType(string TypeName)
{
 // TODO: Caching mechanism for assembly checksforeach (
 Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
 {
 Type ClassType = loadedAssembly.GetType(TypeName);
 if (ClassType!= null)
 return ClassType;
 }
 returnnull;
}

在Gethandler中,我们编写:

// Try to get the type associated with the request (On a name to type basis)Type WebServiceType = 
 this.GetServiceType(Path.GetFileNameWithoutExtension(pathTranslated));// if we did not find any send it on to the // original ajax script service handler.if (WebServiceType == null)
{
 // [REFLECTION] Get the internal class // System.Web.Script.Services.ScriptHandlerFactory create it IHttpHandlerFactory ScriptHandlerFactory = 
 (IHttpHandlerFactory)System.Activator.CreateInstance(
 AjaxAssembly.GetType(
 "System.Web.Script.Services.ScriptHandlerFactory"));
 UsedHandlerFactory = ScriptHandlerFactory;
 return ScriptHandlerFactory.GetHandler(
 context, requestType, url, pathTranslated);
}

这里的代码将得到 WebServiceType 。 如果没有找到类型,它将把请求转发到默认的Ajax框架 ScriptHandlerFactory,使用反射,因为它是内部类。 通常,处理web服务和Javascript代理的ScriptHandlerFactory的所有功能都是私有的或者内部的。 注意,在这里使用的大多数功能都是使用 Reflector.net 工具找到的。 可以使用 3个不同的请求访问web服务处理程序:

  • 正常的web服务方法请求(/devusion 。CustomControls 。services 。asmx。)?
  • Javascript代理生成请求 (/Devusion.CustomControls.Service.asmx/js 或者/jsdebug )
  • Javascript方法调用(/Devusion. CustomControls 。service 。asmx/方法)

首先,检查普通请求或者Javascript请求:

// [REFLECTION] get the Handlerfactory : RestHandlerFactory// (Handles Javascript proxy Generation and actions)IHttpHandlerFactory JavascriptHandlerFactory = 
 (IHttpHandlerFactory)System.Activator.CreateInstance(
 AjaxAssembly.GetType("System.Web.Script.Services.RestHandlerFactory"));// [REFLECTION] Check if the current request is a Javasacript request// JavascriptHandlerfactory.IsRestRequest(context);System.Reflection.MethodInfo IsScriptRequestMethod = 
 JavascriptHandlerFactory.GetType().GetMethod("IsRestRequest", 
 BindingFlags.Static | BindingFlags.NonPublic);if ((bool)IsScriptRequestMethod.Invoke(null, new object[] { context }))
{
 // Javascript Request}else{
 // Normal Request}

然后对于Javascript请求,我们检查代理请求或者方法请求:

// Check and see if it is a Javascript Request or a request for a // Javascript Proxy.bool IsJavascriptDebug = 
 string.Equals(context.Request.PathInfo, "/jsdebug", 
 StringComparison.OrdinalIgnoreCase);bool IsJavascript = string.Equals(context.Request.PathInfo, "/js", 
 StringComparison.OrdinalIgnoreCase);if (IsJavascript || IsJavascriptDebug)
{
 // Proxy Request}else{
 // Method Request}

对于 3请求类型中的操作。

JavaScript代理请求

这里我们做了更多的反射;每个反射都在注释中解释。 我们使用WebServiceType创建一个 WebServiceData,它将包含创建web服务所需的所有信息。 我们直接在GetClientProxyScript方法中使用这个对象,它是对象WebServiceClientProxyGenerator的一种方法。 然后返回Javascript代理。

// [REFLECTION] fetch the constructor for the WebServiceData ObjectConstructorInfo WebServiceDataConstructor = AjaxAssembly.GetType(
 "System.Web.Script.Services.WebServiceData").GetConstructor(
 BindingFlags.NonPublic | BindingFlags.Instance, null, 
 new Type[] { typeof(Type), typeof(bool) }, null);// [REFLECTION] fetch the constructor for the WebServiceClientProxyGeneratorConstructorInfo WebServiceClientProxyGeneratorConstructor = 
 AjaxAssembly.GetType(
 "System.Web.Script.Services.WebServiceClientProxyGenerator").GetConstructor(
 BindingFlags.NonPublic | BindingFlags.Instance, null, 
 new Type[] { typeof(string), typeof(bool) }, null);// [REFLECTION] get the method from WebServiceClientProxy to // create the Javascript : GetClientProxyScriptMethodInfo GetClientProxyScriptMethod = 
 AjaxAssembly.GetType(
 "System.Web.Script.Services.ClientProxyGenerator").GetMethod(
 "GetClientProxyScript", BindingFlags.NonPublic | BindingFlags.Instance, 
 null, new Type[] { AjaxAssembly.GetType(
 "System.Web.Script.Services.WebServiceData") }, null);// [REFLECTION] We invoke : // new WebServiceClientProxyGenerator(url,false).GetClientProxyScript(new WebServiceData(WebServiceType));string Javascript = (string)GetClientProxyScriptMethod.Invoke(
 WebServiceClientProxyGeneratorConstructor.Invoke(
 new Object[] { url, IsJavascriptDebug }), new Object[] 
 {
 WebServiceDataConstructor.Invoke(
 new object[] { WebServiceType, false })
 }
);

然后,对于一个小的缓存,这几乎是从原来的单位带反射器。

// Check the assembly modified time and use it as caching http headerDateTime AssemblyModifiedDate = GetAssemblyModifiedTime(
 WebServiceType.Assembly);// See"if Modified since" was requested in the http headers, // and check it with the assembly modified timestring s = context.Request.Headers["If-Modified-Since"];
DateTime TempDate;if (((s!= null) && DateTime.TryParse(s, out TempDate)) && (
 TempDate >= AssemblyModifiedDate))
{
 context.Response.StatusCode = 0x130;
 returnnull;
}// Add HttpCaching data to the http headersif (!IsJavascriptDebug && (
 AssemblyModifiedDate.ToUniversalTime() < DateTime.UtcNow))
{
 HttpCachePolicy cache = context.Response.Cache;
 cache.SetCacheability(HttpCacheability.Public);
 cache.SetLastModified(AssemblyModifiedDate);
}

然后我们需要通过HTTPHandler将Javascript代理字符串返回到客户端。

internalclass JavascriptProxyHandler : IHttpHandler
{
 string Javascript = "";
 public JavascriptProxyHandler(string _Javascript)
 {
 Javascript = _Javascript;
 }
 bool IHttpHandler.IsReusable { get { returnfalse; } }
 void IHttpHandler.ProcessRequest(HttpContext context)
 {
 context.Response.ContentType = "application/x-Javascript";
 context.Response.Write(this.Javascript);
 }
}

然后在GetHandler代码中创建并返回:

HttpHandler = new JavascriptProxyHandler(Javascript);return HttpHandler;

这就是Javascript代理。 让我们继续另一个简单的。

JavaScript方法请求

当你使用的方法生成javascript代理,它将访问web服务与/methodname. 这个处理程序的原因是不同于正常的web服务处理程序( soap/xml ), 就是这个接口需要翻译我们 C# Javascriptjson方法之间。

IHttpHandler JavascriptHandler = 
 (IHttpHandler)System.Activator.CreateInstance(
 AjaxAssembly.GetType("System.Web.Script.Services.RestHandler"));// [REFLECTION] fetch the constructor for the WebServiceData ObjectConstructorInfo WebServiceDataConstructor = AjaxAssembly.GetType(
 "System.Web.Script.Services.WebServiceData").GetConstructor(
 BindingFlags.NonPublic | BindingFlags.Instance, null, 
 new Type[] { typeof(Type), typeof(bool) }, null);// [REFLECTION] get method : JavaScriptHandler.CreateHandlerMethodInfo CreateHandlerMethod = JavascriptHandler.GetType().GetMethod(
 "CreateHandler", BindingFlags.NonPublic | BindingFlags.Static, null, 
 new Type[] { AjaxAssembly.GetType(
 "System.Web.Script.Services.WebServiceData"), typeof(string) }, null);// [REFLECTION] Invoke CreateHandlerMethod :// HttpHandler = JavaScriptHandler.CreateHandler(WebServiceType,false);HttpHandler = (IHttpHandler)CreateHandlerMethod.Invoke(
 JavascriptHandler, new Object[]
 {
 WebServiceDataConstructor.Invoke(
 new object[] { WebServiceType, false }), 
 context.Request.PathInfo.Substring(1)
 }
);return HttpHandler;

我们创建 JavascriptHandler,比如在前面的处理程序中,WebServiceData 。 然后我们用WebServiceData作为参数调用 CreateHandler,它返回正确的HTTPHandler 。

正常的web服务请求

普通请求更简单。 类是 public,只有接收WebServiceData而不是文件的函数是私有的。 我们使用这个函数来获得另一个HttpHandler并返回它。

// Remember the used factory for later in ReleaseHandlerIHttpHandlerFactory WebServiceHandlerFactory = new WebServiceHandlerFactory();
UsedHandlerFactory = WebServiceHandlerFactory;// [REFLECTION] Get the method CoreGetHandlerMethodInfo CoreGetHandlerMethod = UsedHandlerFactory.GetType().GetMethod(
 "CoreGetHandler", BindingFlags.NonPublic | BindingFlags.Instance);// [REFLECTION] Invoke the method CoreGetHandler :// WebServiceHandlerFactory.CoreGetHandler(WebServiceType,context, context.Request, context.Response);
HttpHandler = (IHttpHandler)CoreGetHandlerMethod.Invoke(UsedHandlerFactory, 
 new object[]
{
 WebServiceType, context, context.Request, context.Response
}
);return HttpHandler;

Default.aspx

现在让我们来看看。 首先,我们向ScriptManager添加一个web服务引用:

<asp:ScriptManagerID="ScriptManager1"runat="server"><Services><asp:ServiceReferencePath="Devusion.CustomControls.asmx"/></Services></asp:ScriptManager>

在下面,我们将:

<scripttype="text/Javascript">function RunHelloWorld(name)
 {
 name = prompt("Please enter your name :","John Doe");
 Devusion.CustomControls.Service.HelloWorldYou(name,
 HelloWorldDone,WebServiceCallFailed,'');
 }
 function HelloWorldDone(result,context,senderFunction)
 {
 alert(result);
 }
 function WebServiceCallFailed(error,context,senderFunction)
 {
 alert(error.get_message());
 }</script><inputtype="button"onclick="RunHelloWorld();"value="Click Here"/>

现在可以将一个断点放在web服务类中的HelloWorldYou方法中,该断点位于 service.cs 文件中。 只要运行页面,让反射的魔力做剩下的事情 ! 在本例中,我没有实现实际的自定义控件,也没有实现所有Javascript的首选script#实现。 我将在以后的文章中透露。

演示代码

附加的ZIP是一个完整的演示解决方案。 它应该直接打开和构建,所以如果有问题,请告诉我。 代码是使用 Visual Studio 2005 Professional和 ASP.NET Ajax 1.0创建的。

历史记录

  • 30 May, 2007 - 已经发布的原始版本



Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语