Click here to Skip to main content
15,891,248 members
Articles / Web Development / ASP.NET

Assembly-based webservice and Javascript proxy using reflection

Rate me:
Please Sign up or sign in to vote.
4.88/5 (17 votes)
30 May 2007CPOL6 min read 68.1K   326   33  
ASP.NET and Ajax Webservices, not from a .asmx file, but from a compiled assembly with a little bit of reflection
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.Reflection;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.UI;
using System.Web.Script.Services;

namespace Devusion.CustomControls
{
    /// <summary>
    /// A web service handler factory that discovers what external assembly, file or factory to use.
    /// </summary>
    public class AutoDiscoveryWebServiceHandlerFactory : IHttpHandlerFactory
    {
        /// <summary>
        /// Searches all Services and tries to find a class with the specified name
        /// </summary>
        private Type GetServiceType(string TypeName)
        {
            // Todo: Caching mechanism for assembly checks
            foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                Type ClassType = loadedAssembly.GetType(TypeName);
                if (ClassType != null)
                    return ClassType;
            }
            return null;
        }


        /// <summary>
        /// Use one of the visible attributes in the assembly to get a reference to the "System.Web.Extentions" Library.
        /// </summary>
        Assembly _ajaxAssembly = null;
        Assembly AjaxAssembly
        {
            get
            {
                if (_ajaxAssembly == null) _ajaxAssembly = typeof(GenerateScriptTypeAttribute).Assembly;
                return _ajaxAssembly;
            }
        }

        // Used to remember which Factory has been used
        private IHttpHandlerFactory UsedHandlerFactory;

        /// <summary>
        /// Checks the request type and returns the corresponding httpd handler or factory
        /// </summary>
        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            IHttpHandler HttpHandler = null;

            try
            {
                // Request Hosting permissions
                new AspNetHostingPermission(AspNetHostingPermissionLevel.Minimal).Demand();

                // 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);
                }

                // [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 method
                // JavascriptHandlerfactory.IsRestRequest(context);
                System.Reflection.MethodInfo IsScriptRequestMethod = JavascriptHandlerFactory.GetType().GetMethod("IsRestRequest", BindingFlags.Static | BindingFlags.NonPublic);
                if ((bool)IsScriptRequestMethod.Invoke(null, new object[] { context }))
                {
                    // Remember the used factory for later in ReleaseHandler
                    UsedHandlerFactory = JavascriptHandlerFactory;

                    // 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)
                    {

                        // [REFLECTION] fetch the constructor for the WebServiceData Object
                        ConstructorInfo 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 WebServiceClientProxyGenerator
                        ConstructorInfo 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 : GetClientProxyScript
                        MethodInfo 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).WebServiceClientProxyGenerator.GetClientProxyScript(new WebServiceData(WebServiceType));
                        string Javascript = (string)GetClientProxyScriptMethod.Invoke(
                          WebServiceClientProxyGeneratorConstructor.Invoke(new Object[] { url, IsJavascriptDebug })
                        , new Object[] {
                            WebServiceDataConstructor.Invoke(new object[] { WebServiceType, false }) 
                            }
                        );

                        // The following caching code was copied from the original assembly, read with Reflector, comments were added manualy.
                        #region Caching

                        // Check the assembly modified time and use it as caching http header
                        DateTime AssemblyModifiedDate = GetAssemblyModifiedTime(WebServiceType.Assembly);

                        // See "if Modified since" was requested in the http headers, and check it with the assembly modified time
                        string s = context.Request.Headers["If-Modified-Since"];

                        DateTime TempDate;
                        if (((s != null) && DateTime.TryParse(s, out TempDate)) && (TempDate >= AssemblyModifiedDate))
                        {
                            context.Response.StatusCode = 0x130;
                            return null;
                        }

                        // Add HttpCaching data to the http headers
                        if (!IsJavascriptDebug && (AssemblyModifiedDate.ToUniversalTime() < DateTime.UtcNow))
                        {
                            HttpCachePolicy cache = context.Response.Cache;
                            cache.SetCacheability(HttpCacheability.Public);
                            cache.SetLastModified(AssemblyModifiedDate);
                        }
                        #endregion

                        // Set Add the javascript to a new custom handler and set it in HttpHandler.
                        HttpHandler = new JavascriptProxyHandler(Javascript);
                        return HttpHandler;
                    }
                    else
                    {
                        IHttpHandler JavascriptHandler = (IHttpHandler)System.Activator.CreateInstance(AjaxAssembly.GetType("System.Web.Script.Services.RestHandler"));

                        // [REFLECTION] fetch the constructor for the WebServiceData Object
                        ConstructorInfo 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.CreateHandler
                        MethodInfo 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;
                }
                else
                {
                    // Remember the used factory for later in ReleaseHandler
                    IHttpHandlerFactory WebServiceHandlerFactory = new WebServiceHandlerFactory();
                    UsedHandlerFactory = WebServiceHandlerFactory;

                    // [REFLECTION] Get the method CoreGetHandler
                    MethodInfo 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;
                }
            }
            // Because we are using Reflection, errors generated in reflection will be an InnerException, 
            // to get the real Exception we throw the InnerException it.
            catch (TargetInvocationException e)
            {
                throw e.InnerException;
            }
        }

        /// <summary>
        /// Release the handler in the used factory
        /// </summary>
        /// <param name="handler"></param>
        public void ReleaseHandler(IHttpHandler handler)
        {
            if (UsedHandlerFactory != null)
                UsedHandlerFactory.ReleaseHandler(handler);

        }

        private static DateTime GetAssemblyModifiedTime(Assembly assembly)
        {
            DateTime lastWriteTime = File.GetLastWriteTime(new Uri(assembly.GetName().CodeBase).LocalPath);
            return new DateTime(lastWriteTime.Year, lastWriteTime.Month, lastWriteTime.Day, lastWriteTime.Hour, lastWriteTime.Minute, 0);
        }


    }

    /// <summary>
    /// A custom handler to deliver the generated Javascript.
    /// </summary>
    internal class JavascriptProxyHandler : IHttpHandler
    {
        public JavascriptProxyHandler(string _javascript)
        {
            Javascript = _javascript;
        }

        string Javascript = "";

        #region IHttpHandler Members

        bool IHttpHandler.IsReusable { get { return false; } }

        void IHttpHandler.ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "application/x-javascript";
            context.Response.Write(this.Javascript);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Devusion
Netherlands Netherlands
Robertjan is an independent Developing and usability Consultant.

He has been a speaker at CodeCamp, TechDays and other venues. And blogs regularly on http://www.robertjantuit.nl

His specialaties are User Interfaces, Silverlight, WPF, Custom Controls and Software Architecture, Design and Agile development.

Comments and Discussions