asp.net mvc(九)
这篇我来讲如何实现自定义特性ajax,以及如何自动生成客户端js。
第一:所谓的自定义特性,就是我们自己定义的一个特性标签,在.net中,就有一些这样的特性,例如 .net内置的System.ObsoleteAttribute 。示例代码如下:
public void testObsolete()
{ }
public void testObsolete(int j)
{ }
public void testA()
{
this.testObsolete();
}
如果存在一个方法的多个重载,又不能删除某些重载,可以给它加上Obsolete,当用户调用这个方法时,编译器就会给出警告提示用户。这个类继承自Attribute,特性是一个对象,可以加载到程序集及程序集的对象中,包括程序集本身、模块、类、接口、结构、构造函数、方法、方法参数等,加载了特性的对象称作特性的目标。特性为程序添加元数据(描述数据的数据)的一种机制,它可以给编译器提供指示或者提供对数据的说明。
特性有以下三个非常实用的参数以及属性:[AttributeUsage(AttributeTargets.Class, AllowMutiple=true, Inherited=false)]
1:AttributeTargets代表特性应用的对象,类,接口,方法等。
2:AllowMutiple,是否允许用户在一个对象上多次使用特性标签。
3:Inherited:对于子类是否有效。
这里我们创建一个AjaxAttribute.cs类,包含三个简单属性。
public class AjaxAttribute:Attribute
{
public string Name { get; set; }
public bool Inherited{get;set;}
public AjaxReturnType ReturnType { get; set; }
}
第二:上篇文章(asp.net mvc(八))我们提到,MVC中实现ajax,可以返回JsonResult类型的值供客户端调用,但它返回的是查询出来的直接对象,如果对象为空,则不会有实际内容返回。这个对象提供的信息可以扩充下,创建一个AjaxResult对象,继承自JsonResult,有三个构造函数。 客户端在调用回调方法时可以根据这些新增加的值来处理。
1:可以在返回结果对象中增加一个操作状态值,一般可以是成功或者是失败(success)。
2:可以在返回结果对象中增加一些提示信息,例如失败的原因之类信息(message)。
3: 可以在返回结果对象中增加一个类型为object的属性,用来保存程序返回的数据(object value)。
{
public AjaxResult(bool isSuccess)
{
AjaxInfo info = new AjaxInfo();
info.success = isSuccess;
this.Data = info;
}
public AjaxResult(bool isSuccess, object data)
{
AjaxInfo info = new AjaxInfo();
info.success = isSuccess;
info.value = data;
this.Data = info;
}
public AjaxResult(bool isSuccess, object data, string messsage)
{
AjaxInfo info = new AjaxInfo();
info.success = isSuccess;
info.value = data;
info.message = messsage;
this.Data = info;
}
}
public class AjaxInfo
{
public bool success { get; set; }
public string message { get; set; }
public object value { get; set; }
}
第三:提供多种返回类型,之前的示例返回json格式,显然有局限性,为此可以创建一个返回值类型的枚举,以支持多种格式的数据。当返回类型不同时,异常调用方法的返回类型也需要跟着变化。
/// Ajax方法的返回类型
/// </summary>
public enum AjaxReturnType
{
/// <summary>
/// 返回一个可以由jQuery对象处理的XML文档
/// </summary>
Xml,
/// <summary>
/// 返回纯文本格式的HTML,包括求值后的脚本标记
/// </summary>
Html,
/// <summary>
/// 将响应作为JSON求值,并返回一个Javascript对象
/// </summary>
Json,
/// <summary>
/// 将响应作为Javascript语句求值,并返回纯文本
/// </summary>
Script
}
第四:利用反射以及结合我们创建的自定义特性AjaxAttribute,完成js代码的输出。这段代码比较多,大家可以自己去试试。
{
private string _Script;
private Dictionary<string, AjaxMethodInfo> _AjaxMethods;
private List<PropertyInfo> _AjaxProperties;
/// <summary>
/// 利用反射,创建js脚本
/// </summary>
/// <param name="type"></param>
public AjaxClass(Type type)
{
List<string> scripts = new List<string>();
Dictionary<string, AjaxMethodInfo> methodList = new Dictionary<string, AjaxMethodInfo>();
List<PropertyInfo> propertyList = new List<PropertyInfo>();
MethodInfo[] methods;
PropertyInfo[] properties;
string clientName;
object[] attrs = type.GetCustomAttributes(typeof(AjaxAttribute), false);
if (attrs.Length > 0)
{
AjaxAttribute attr = attrs[0] as AjaxAttribute;
clientName = string.IsNullOrEmpty(attr.Name) ? type.Name : attr.Name;
if (attr.Inherited)
{
methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty);
}
else
{
methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.DeclaredOnly);
}
}
else
{
clientName = type.Name;
methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.DeclaredOnly);
}
foreach (MethodInfo mi in methods)
{
attrs = mi.GetCustomAttributes(typeof(AjaxAttribute), false);
if (attrs.Length > 0)
{
AjaxReturnType returnType = AjaxReturnType.Html;
if (mi.ReturnType == typeof(int) || mi.ReturnType == typeof(System.Web.Mvc.JsonResult))
returnType = AjaxReturnType.Json;
//AjaxReturnType returnType = (attrs[0] as AjaxAttribute).ReturnType;
AjaxMethodInfo ami = new AjaxMethodInfo(mi, returnType);
methodList.Add(mi.Name, ami);
scripts.Add(BuildAjaxRequest(ami));
}
}
foreach (PropertyInfo pi in properties)
{
attrs = pi.GetCustomAttributes(typeof(AjaxAttribute), false);
if (attrs != null && attrs.Length > 0) propertyList.Add(pi);
}
if (methodList.Count > 0) _AjaxMethods = methodList;
if (propertyList.Count > 0) _AjaxProperties = propertyList;
BuildScript(clientName, scripts, propertyList);
}
/// <summary>
/// 输入所有属性的js脚本
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public string GetScript(object obj)
{
if (_AjaxProperties == null) return _Script;
string script = string.Empty;
foreach (PropertyInfo pi in _AjaxProperties)
{
if (script == string.Empty) script = BuildAjaxObject(obj, pi);
else script += ",\r\n " + BuildAjaxObject(obj, pi);
}
return _Script.Replace("{property}", script);
}
/// <summary>
/// 创建最终的js脚本
/// </summary>
/// <param name="typeName"></param>
/// <param name="scripts"></param>
/// <param name="propertyList"></param>
private void BuildScript(string typeName, List<string> scripts, List<PropertyInfo> propertyList)
{
if (scripts.Count > 0 || propertyList.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine(" <script type=\"text/javascript\">");
sb.AppendFormat(" var {0} = {{", typeName);
if (propertyList.Count > 0)
{
sb.AppendLine();
sb.Append(" {property}");
}
for (int i = 0; i < scripts.Count; i++)
{
if (i == 0 && propertyList.Count == 0) sb.AppendLine();
else sb.AppendLine(",");
sb.Append(" " + scripts[i]);
}
sb.AppendLine();
sb.AppendLine(" }");
sb.AppendLine(" </script>");
_Script = sb.ToString();
}
}
/// <summary>
/// jquery相关ajax方法的脚本构建
/// </summary>
/// <param name="ami"></param>
/// <returns></returns>
private string BuildAjaxRequest(AjaxMethodInfo ami)
{
string methodName = ami.MethodInfo.Name;
string url = "{url}" + methodName + "{querystring}";
ParameterInfo[] parameters = ami.MethodInfo.GetParameters();
AjaxReturnType returnType = ami.ReturnType;
string param, data;
if (parameters == null || parameters.Length == 0)
{
param = "callback";
data = string.Empty;
}
if (parameters.Length == 0)
{
return string.Format(@"{0}: function(callback)
{{
$.getJSON('{1}', callback);
}}",
methodName, url);
}
else
{
string[] paramArray = new string[parameters.Length + 1];
string[] dataArray = new string[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
paramArray[i] = parameters[i].Name;
dataArray[i] = string.Format("{0}:{0}", parameters[i].Name);
}
//paramArray[parameters.Length] = "callback";
param = string.Join(", ", paramArray);
param = param.Trim ().TrimEnd(',');
data = string.Join(", ", dataArray);
}
return string.Format(@"{0}: function({1},callback)
{{
$.getJSON('{2}',{{{3}}}, callback);
}}",
methodName,param, url,data);
}
private string BuildAjaxObject(object obj, PropertyInfo pi)
{
object value = pi.GetValue(obj, null);
object[] attrs = pi.GetCustomAttributes(typeof(AjaxAttribute), false);
if (attrs.Length > 0)
{
AjaxAttribute attr = attrs[0] as AjaxAttribute;
if (attr.ReturnType == AjaxReturnType.Json && value is string)
return string.Format(@"{0}: {1}", pi.Name, value);
}
StringBuilder sb = new StringBuilder();
JsonWriter jw = new JsonWriter(sb);
jw.Write(value);
return string.Format(@"{0}: {1}", pi.Name, sb.ToString());
}
}
第五:为了让所有页面都能利用js自动代码生成,我为所有Controller创建一个基类,在它的Initialize中初始化js脚本。
{
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
InitJavaScript();
}
/// <summary>
/// 初始化Ajax使用的JavaScript
/// </summary>
private void InitJavaScript()
{
string jsb = AjaxManager.GetAjaxScript(this);
string controller = Convert.ToString(this.RouteData.Values["controller"]);
if (!string.IsNullOrEmpty(jsb) && !string.IsNullOrEmpty(controller))
{
string str = System.Web.HttpContext.Current.Request.ApplicationPath;
str = str.Length > 1 ? str : string.Empty;
jsb = jsb.Replace("{url}", string.Format("{0}/{1}/", str, controller));
jsb = jsb.Replace("{querystring}", GetHttpQueryString());
}
ViewData["m_javablock"] = jsb;
}
private string GetHttpQueryString()
{
StringBuilder sb = new StringBuilder("?");
foreach (KeyValuePair<string, object> pair in this.RouteData.Values)
{
if (pair.Key != "controller" && pair.Key != "action")
sb.AppendFormat("{0}={1}&", pair.Key, (pair.Value!=null)?pair.Value.ToString():"");
}
sb.Append(System.Web.HttpContext.Current.Request.QueryString.ToString());
return sb.ToString();
}
}
public class AjaxManager
{
public static string GetAjaxScript(object obj)
{
AjaxClass ajaxClass = new AjaxClass(obj.GetType());
return ajaxClass.GetScript(obj);
}
}
第六:在HomeController类中,增加如下用于异步请求的代码。同时在方法上面增加特性标签Ajax。
public AjaxResult TestMVC(int i, int j)
{
int I = 0;
List<student> list = new List<student>();
for (int k = 0; k < 10; k++)
{
student sd = new student() { sname = "aaa" + k.ToString() + j.ToString(), ID = k, Grade = k * 10 };
list.Add(sd);
}
var stu = (from m in list
where m.ID == i
select m
).FirstOrDefault();
return new AjaxResult(true, stu);
}
第七:最终生成的客户端js如下:
var HomeController = {
TestMVC: function(i, j,callback)
{
$.getJSON('/Home/TestMVC?id=&',{i:i, j:j}, callback);
}
}
</script>
总结:上面的代码可能写的比较乱,但大概思路应该有了,这样我们可以在后台代码中为需要异步调用的方法增加Ajax特性标签,然后在客户端通过类型C#命令空间的方式调用。例如:
$("#divStudent").html(data.value.sname);
});
作者:姜敏
出处:http://www.cnblogs.com/aspnet2008/