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

ASP.NET Advanced Generic Handler ASHX

Rate me:
Please Sign up or sign in to vote.
4.74/5 (49 votes)
9 Jun 2013CPOL5 min read 320.7K   10.2K   138  
Take your Generic Handlers to the next level...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;
using System.Reflection;
using System.Web.SessionState;

namespace App.Utilities.Web.Handlers
{
	public abstract partial class BaseHandler : IHttpHandler, IRequiresSessionState
	{

		private HttpContext _context = null;
		public HttpContext context
		{
			get { return _context; }
			private set { _context = value; }
		}

		/// <summary>
		/// Method used to handle the request as a normal ASHX.
		/// To use this method just pass handlerequest=true on the request query string.
		/// </summary>
		/// <param name="context"></param>
		public virtual void HandleRequest() { }

		public void ProcessRequest(HttpContext context)
		{
			this.context = context;

			string handleRequest = context.Request["handlerequest"];
			if (!string.IsNullOrEmpty(handleRequest) && handleRequest.ToLower() == "true")
			{
				HandleRequest();
				return;
			}

			var ajaxCall = new AjaxCallSignature(context);

			context.Response.ContentType = string.Empty;
			if (!string.IsNullOrEmpty(ajaxCall.returnType))
			{
				switch (ajaxCall.returnType)
				{
					case "json":
						context.Response.ContentType = "application/json";
						break;
					case "xml":
						context.Response.ContentType = "application/xml";
						break;
					case "jpg":
					case "jpeg":
					case "image/jpg":
						context.Response.ContentType = "image/jpg";
						break;
					default:
						break;
				}
			}

			// call the requested method
			var result = ajaxCall.Invoke(this, context);

			// if neither on the arguments or the actual method the content type was set then make sure to use the default content type
			if (string.IsNullOrEmpty(context.Response.ContentType) && !SkipContentTypeEvaluation)
			{
				context.Response.ContentType = DefaultContentType();
			}

			// only skip transformations if the requestor explicitly said so
			if (result == null)
			{
				context.Response.Write(string.Empty);
			}
			else if (!SkipDefaultSerialization)
			{
				switch (context.Response.ContentType.ToLower())
				{
					case "application/json":
						JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
						string json = jsonSerializer.Serialize(result);
						context.Response.Write(json);
						break;
					case "application/xml":
						System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(result.GetType());
						StringBuilder xmlSb = new StringBuilder();
						System.Xml.XmlWriter xmlWriter = System.Xml.XmlWriter.Create(xmlSb);
						xmlSerializer.Serialize(xmlWriter, result);
						context.Response.Write(xmlSb.ToString());
						break;
					case "text/html":
						context.Response.Write(result);
						break;
					default:
						throw new Exception(string.Format("Unsuported content type [{0}]", context.Response.ContentType));
				}
			}
			else
			{
				context.Response.Write(result);
			}
		}

		public bool IsReusable
		{
			get
			{
				return false;
			}
		}

		/// <summary>
		/// Returns the default content type returned by the handler.
		/// </summary>
		/// <returns></returns>
		public virtual string DefaultContentType()
		{ 
			return "application/json";
		}

		/// <summary>
		/// Setting this to false will make the handler to respond with exacly what the called method returned.
		/// If true the handler will try to serialize the content based on the ContentType set.
		/// </summary>
		public bool SkipDefaultSerialization { get; set; }

		/// <summary>
		/// Setting this to true will avoid the handler to change the content type wither to its default value or to its specified value on the request.
		/// This is useful if you're handling the request yourself and need to specify it yourself.
		/// </summary>
		public bool SkipContentTypeEvaluation { get; set; }

		/// <summary>
		/// Prints an help page discribng the available methods on this handler.
		/// </summary>
		/// <returns></returns>
		public string Help()
		{
			context.Response.ContentType = "text/html";

			StringBuilder sb = new StringBuilder();

			sb.AppendLine("<style>");
			sb.AppendLine(".MainHeader { background-color: FFFFE0; border: 1px dashed red; padding: 0 10 0 10; }");
			sb.AppendLine("h3 { background-color: #DCDCDC; }");
			sb.AppendLine("ul { background-color: #FFFFFF; }");
			sb.AppendLine(".type { color: gray; }");
			sb.AppendLine("</style>");

			sb.AppendLine("<div class='MainHeader'><h2>Handler available methods</h2></div>");

			MethodInfo[] methods = this.GetType().GetMethods();	// All methods found on this type
			MethodInfo[] excludeMethods = this.GetType().BaseType.GetMethods();	// methods from the base class are not to be shown

			foreach (var m in methods)
			{
				// do nothing if the current method belongs to the base type.
				// I'm not supporting overrides here, I'm only searching by name, if mothe than one method with the same name exist they all will be ignored.
				if (excludeMethods.FirstOrDefault(c => c.Name == m.Name) != null)
					continue;

				ParameterInfo[] parameters = m.GetParameters();

				bool RequiresAuthentication = false;
				object[] attrs = m.GetCustomAttributes(typeof(RequireAuthenticationAttribute), true);
				if (attrs != null && attrs.Length > 0)
				{
					RequiresAuthentication = ((RequireAuthenticationAttribute)attrs[0]).RequireAuthentication;
				}

				sb.AppendLine("<h3>" + m.Name + (RequiresAuthentication ? " <span style=\"color:#f00\">[Requires Authentication]</span>" : string.Empty) + "</h3>");

				sb.AppendLine("<table><tr><td width=\"250px\">");
				sb.AppendLine("<table width=\"100%\">");
				foreach (var p in parameters)
				{
					sb.AppendLine("<tr><td>" + p.Name + "</td><td><span class='type'>[" + p.ParameterType.ToString() + "]</span></td></tr>");
				}

				sb.AppendLine("</table>");
			
				sb.AppendLine("</td><td style=\"border-left: 1px dashed #DCDCDC; padding-left: 8px;\">");

				string getJSONSample = "<pre>$.getJSON(\n\t'" + context.Request.Url.LocalPath + "', \n\t{method: \"" + m.Name + "\", returntype: \"json\", args: {";
				foreach (var p in m.GetParameters())
				{
					getJSONSample += " " + p.Name + ": \"\",";
				}
				getJSONSample = getJSONSample.TrimEnd(',') + " ";
				getJSONSample += "}}, \n\tfunction() { alert('Success!'); });</pre>";
				sb.AppendLine(getJSONSample);

				sb.AppendLine("</td>");
				sb.AppendLine("</tr></table>");
			}
			return sb.ToString();
		}

	}
}

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
Architect
Switzerland Switzerland
Senior IT Consultant working in Switzerland as Senior Software Engineer.

Find more at on my blog.

Comments and Discussions