Click here to Skip to main content
15,891,136 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 322.1K   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.Reflection;
using System.Web;
using System.Web.Script.Serialization;
using System.IO;
using System.ComponentModel;

namespace App.Utilities.Web.Handlers
{
	public class AjaxCallSignature
	{
		public AjaxCallSignature(HttpContext context)
		{
			args = new Dictionary<string, object>();
			method = string.Empty;
			string nullKeyParameter = context.Request.QueryString[null];
			if (string.IsNullOrEmpty(nullKeyParameter))
			{
				if (!string.IsNullOrEmpty(context.Request.QueryString.ToString()) && context.Request.QueryString.ToString().StartsWith("{"))
				{

				}
			}

			if (context.Request.RequestType.ToUpper() == "POST")
			{
				string[] requestParams = context.Request.Params.AllKeys;
				foreach (var item in requestParams)
				{
					if (item.ToLower() == "method")
					{
						method = context.Request.Params[item];
					}
					else if (item.ToLower().StartsWith("args["))
					{
						string key = item.Trim().TrimEnd(']').Substring(5);
						key = key.Trim().Replace("][", "+");

						string value = context.Request.Params[item];
						args.Add(key, value);
					}
					else
					{
						string key = item;
						string value = context.Request.Params[item];
						args.Add(key, value);
					}
				}
			}
			else if (context.Request.RequestType.ToUpper() == "GET")
			{
				// evaluate the data passed as json
				if (!string.IsNullOrEmpty(nullKeyParameter))
				{
					if (nullKeyParameter.ToLower() == "help")
					{
						method = "help";
						return;
					}
					else
					{
						object json = null;
						JavaScriptSerializer serializer = new JavaScriptSerializer();
						json = serializer.DeserializeObject(context.Request.QueryString[null]);

						try
						{
							Dictionary<string, Object> dict = (Dictionary<string, Object>)json;

							if (dict.ContainsKey("method"))
								method = dict["method"].ToString();
							else
								throw new Exception("Invalid BaseHandler call. MethodName parameter is mandatory in json object.");

							if (dict.ContainsKey("returntype"))
								returnType = dict["returntype"].ToString();

							if (dict.ContainsKey("args"))
								args = (Dictionary<string, Object>)dict["args"];
							else
								args = new Dictionary<string, object>();
						}
						catch
						{
							throw new InvalidCastException("Unable to cast json object to AjaxCallSignature");
						}
					}
				}

				// evaluate data passed as querystring params
				foreach (string key in context.Request.QueryString.Keys)
				{
					if (key == null)
						continue;

					if (key.ToLower() == "method")
					{
						if (string.IsNullOrEmpty(method))
						{
							method = context.Request.QueryString[key];
						}
						else
						{
							throw new Exception("Method name was already specified on the json data. Specify the method name only once, either on QueryString params or on the json data.");
						}
					}
					else if (key.ToLower() == "returncontenttype")
					{
						returnType = context.Request.QueryString[key];
					}
					if (key.ToLower().StartsWith("args["))
					{
						string _key = key.Trim().Substring(5).TrimEnd(']').Replace("][", "+");
						args.Add(_key, context.Request.QueryString[key]);
					}
					else
					{
						args.Add(key, context.Request.QueryString[key]);
					}
				}
			}
		}

		public string method { get; set; }
		public string returnType { get; set; }
		public Dictionary<string, object> args { get; set; }

		public object Invoke(object obj, HttpContext context)
		{
			MethodInfo m = obj.GetType().GetMethod(method);
			List<object> a = new List<object>();

			if (m == null)
			{
				if (method.ToLower() == "help")
				{
					m = obj.GetType().BaseType.GetMethod("Help");
				}
				else
				{
					throw new Exception(string.Format("Method {0} not found on Handler {1}.", method, this.GetType().ToString()));
				}
			}
			else
			{
				// security validation: Search for RequireAuthenticationAttribute on the method
				//		value=true the user must be authenticated (only supports FromsAuthentication for now
				//		value=false invoke the method
				object[] attrs = m.GetCustomAttributes(typeof(RequireAuthenticationAttribute), true);
				if (attrs != null && attrs.Length > 0)
				{

					if (!context.Request.IsAuthenticated && ((RequireAuthenticationAttribute)attrs[0]).RequireAuthentication)
					{
						throw new InvalidOperationException("Method [" + m.Name + "] Requires authentication");
					}
				}
			}

			foreach (var param in m.GetParameters())
			{
				a.Add(ProcessProperty(param.Name, param.ParameterType, 0));
			}

			return m.Invoke(obj, a.ToArray());
		}



		private object ProcessProperty(string propertyName, Type propertyType, int depth)
		{
			if (propertyType.IsClass && !propertyType.Equals(typeof(String)))
			{
				depth++;
				return HydrateClass(propertyName, propertyType, depth);
			}
			else
			{
				return HydrateValue(propertyName, propertyType);
			}
		}

		/// <summary>
		/// Hydrates CLR primitive types
		/// </summary>
		/// <param name="param"></param>
		/// <returns></returns>
		public object HydrateValue(string propertyName, Type propertyType)
		{


			if (args.Keys.Contains(propertyName))
			{
				// its usual to pass an empty json string property but casting it to certain types will throw an exception
				if (string.IsNullOrEmpty(args[propertyName].ToString()) || args[propertyName].ToString() == "null" || args[propertyName].ToString() == "undefined")
				{
					// handle numerics. convert null or empty input values to 0
					if (propertyType.Equals(typeof(System.Int16)) || propertyType.Equals(typeof(System.Int32)) ||
						propertyType.Equals(typeof(System.Int64)) || propertyType.Equals(typeof(System.Decimal)) ||
						propertyType.Equals(typeof(System.Double)) || propertyType.Equals(typeof(System.Byte)))
					{
						args[propertyName] = 0;
					}
					else if (propertyType.Equals(typeof(System.Guid)))
					{
						args[propertyName] = new Guid();
					}
					else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
					{
						args[propertyName] = null;
					}
				}

				// evaluate special types that are not directly casted from string
				TypeConverter conv = TypeDescriptor.GetConverter(propertyType);
				if (args[propertyName] == null || propertyType == args[propertyName].GetType())
				{
					return args[propertyName];
				}
				else
				{
					return conv.ConvertFromInvariantString(args[propertyName].ToString());
				}
			}
			else
			{
				return null;	// if there are missing arguments try passing null
			}
		}

		/// <summary>
		/// Hydrates complex types
		/// </summary>
		/// <param name="param"></param>
		/// <returns></returns>
		public object HydrateClass(string propertyName, Type propertyType, int depth)
		{
			var argumentObject = Activator.CreateInstance(propertyType);

			var objectProperties = args.Keys.ToList().FindAll(k => k.StartsWith(propertyName + "+"));
			foreach (var p in objectProperties)
			{
				string[] nestedProperties = p.Split('+');
				argumentObject.GetType().GetProperty(nestedProperties[depth]).SetValue(argumentObject, ProcessProperty(propertyName + "+" + nestedProperties[depth], argumentObject.GetType().GetProperty(nestedProperties[depth]).PropertyType, depth), null);

				foreach (var np in nestedProperties)
				{

					//argumentObject.GetType().GetProperty(np).SetValue(argumentObject, args[p], null);
				}
			}

			return argumentObject;
		}

	}
}

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