帮酷LOGO
  • 显示原文与译文双语对照的内容
文章标签:JAVA  WEB  Assembly  Proxy  Javascript  BASE  REF  


介紹

任何一直在構建. 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 - 已經發布的原始版本


文章标签:WEB  JAVA  Javascript  BASE  REF  Proxy  Assembly  

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