刚刚差点把封装打成装疯......好吧,开始了,ASP.NET中ajax请求的目标大多是webservice和ashx,以及少量指向自己页面的,webservice客户端通用,但是性能差点,ashx挺完美的,性能最快,但这货没写一个业务,就得创建一个文件,这种我也是非常不爽。
ashx的的实现是基于IHttpHandler接口的,这个接口还是挺简单的。
AJAX请求数量会越来越多,我们用一个简单的工厂模式来分流。
public class AjaxHandlerFactory : IHttpHandlerFactory { /// <summary> /// 缓存,用于缓存请求的Ajax实例 /// </summary> private System.Web.Caching.Cache _cache = HttpContext.Current.Cache; /// <summary> /// 用参数类名(ClassName)返回ajax业务类 /// </summary> /// <param name="strUrlPath"></param> /// <returns></returns> private string GetClassName(string strClass) { return string.Format("EMS.WebUtility.HttpHandler.Ajax.{0}, EMS.WebUtility", strClass); } #region IHttpHandlerFactory 成员 public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) { string strClass = context.Request.Params["ClassName"]; if (string.IsNullOrEmpty(strClass)) return null; string strClassName = GetClassName(strClass); //优先从缓存中获取实例 object handle = _cache.Get(strClassName); if (handle != null) { return (IHttpHandler)handle; } Type t = Type.GetType(strClassName, false, true); if (t != null) { if (t.GetInterface("IHttpHandler", true) != null) { return (IHttpHandler)Activator.CreateInstance(t); } } return null; } public void ReleaseHandler(IHttpHandler handler) { //如果handle不为null,且设置了IsReusable为true,则缓存handle if (handler != null && handler.IsReusable) { string className = handler.GetType().FullName.ToUpper(); _cache.Insert(className, handler, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 10, 0)); } } #endregion }
这里通过参数ClassName来确定具体的业务类,通过反射实例化,同时这个地方做了缓存,
在写一个抽象的基类:
public abstract class AjaxHandler : IHttpHandler { /// <summary> /// You will need to configure this handler in the web.config file of your /// web and register it with IIS before being able to use it. For more information /// see the following link: http://go.microsoft.com/?linkid=8101007 /// </summary> #region IHttpHandler Members public bool IsReusable { // Return false in case your Managed Handler cannot be reused for another request. // Usually this would be false in case you have some state information preserved per request. get { return true; } } public virtual string ContengType { get { return "text/html"; } } /// <summary> /// 输出数据,请继承 /// </summary> /// <returns></returns> public abstract string GetBusinessData(); public void ProcessRequest(HttpContext context) { context.Response.ContentEncoding = Encoding.UTF8; context.Response.ContentType = ContengType; AjaxMappingAttribute.Bind(this, context.Request.Params); context.Response.Output.Write(GetBusinessData()); context.Response.End(); } #endregion }
GetBusinessData()是由具体业务类实现的,业务类需要继承上面这个类:
public class GetPctCode : AjaxHandler { [AjaxMapping("name", true)] public string StreetName { get; set; } [AjaxMapping("Number", true)] public string StreetNum { get; set; } [AjaxMapping("Quandrant", true)] public string Quandrant { get; set; } [AjaxMapping("StType", false)] public string StType { get; set; } public override string GetBusinessData() { string SubDivistion = BLLClasses.EMS_Street.GetStreetPCTCodeByStreetNameAndNum(StreetName, StreetNum, "", Quandrant, StType); return SubDivistion; } }
这里还用到了AjaxMapping特性,他的功能是映射参数的值到业务类的属性中:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class AjaxMappingAttribute : System.Attribute { /// <summary> /// 参数名称 /// </summary> public string ParamName { get; set; } /// <summary> /// 是否必须 /// </summary> public bool IsRequired { get; set; } //构造函数 public AjaxMappingAttribute() : this(null, false) { } /// <summary> /// 构造函数 /// </summary> /// <param name="paramName"></param> /// <param name="isRequired"></param> public AjaxMappingAttribute(string paramName, bool isRequired) { this.ParamName = paramName; this.IsRequired = isRequired; } /// <summary> /// 绑定参数 /// </summary> /// <param name="obj"></param> /// <param name="param"></param> public static void Bind(object obj, NameValueCollection param) { Mappinger.Mapping<AjaxMappingAttribute>(obj, (paramName, mt) => { return param[paramName]; }); } } /// <summary> /// 属性映射,接收的URL参数映射到相关类上标识为MappingAttribute的属性上 /// </summary> public class Mappinger { public static void Mapping<T>(object obj, Func<string, T, string> getParamValue) where T : AjaxMappingAttribute { Type t = obj.GetType(); foreach (PropertyInfo pi in t.GetProperties()) { object[] customerAttributes = pi.GetCustomAttributes(typeof(T), false); if (customerAttributes.Length == 1) { MethodInfo setMethod = pi.GetSetMethod(); if (setMethod != null) { T mapping = customerAttributes[0] as T; //参数名称,如果没有指定参数名称,则默认为属性名称 string paramName = string.IsNullOrEmpty(mapping.ParamName) ? pi.Name : mapping.ParamName; string paramValue = getParamValue(paramName, mapping); if (paramValue == null) { if (mapping.IsRequired) { throw new ArgumentNullException(string.Format("未提供参数{0}的值", paramName)); } else { continue; } } setMethod.Invoke(obj, new object[] { paramValue }); } else { throw new Exception(string.Format("未定义数据{0}的set构造器,无法进行赋值。", pi.Name)); } } } } }
以上内容大多抄袭了yangrong.g的这篇文章
yangrong.g的请求是用aspx去映射,我打算另一种办法
用ASP.NET Ajax库就知道会有个ScriptManager,客户端会渲染出一个Resources.axd?XXXXXX的请求,这个axd是在项目中不存在的,就是个URL路由,其实ashx也是这样的一个东西,那我在这里入手,把请求都统一到一个AjaxHandler.axd里:
<add verb="*" path="AjaxHandler.axd" type="EMS.WebUtility.HttpHandler.AjaxHandlerFactory, EMS.WebUtility"/>
客户端的请求只要:
$.post("/AjaxHandler.axd?ClassName=GetPctCode",....