Rumah > hujung hadapan web > tutorial js > 编写轻量ajax组件第三篇实现

编写轻量ajax组件第三篇实现

亚连
Lepaskan: 2018-05-24 10:38:23
asal
1609 orang telah melayarinya

这篇文章主要为大家详细介绍了轻量ajax组件编写第三篇实现,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

通过之前的介绍,我们知道要执行页面对象的方法,核心就是反射,是从请求获取参数并执行指定方法的过程。实际上这和asp.net mvc框架的核心思想很类似,它会解析url,从中获取controller和action名称,然后激活controller对象,从请求获取action参数并执action。在web form平台上,我们把方法写在.aspx.cs中,要实现的就是在页面对象还未生成的情况下,执行指定的方法,然后返回结果。

  我们先看实现后几个调用例子,这些功能也可以组合使用:      

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

[AjaxMethod]

   public void Test1(int index)

   {

     //简单调用

   }

 

   [AjaxMethod]

   public string Test2(Test test)

   {

     return "参数为一个Test实例";

   }

 

   [AjaxMethod(OutputCache = 20)]

   public string Test3(int index)

   {

     return "输出结果缓存20秒";

   }

 

   [AjaxMethod(ServerCache = 20)]

   public string Test4()

   {

     return "在服务端缓存20秒";

   }

 

   [AjaxMethod(SessionState=SessionState.None)]

   public void Test5()

   {

     //Session未被加载

   }

 

   [AjaxMethod(SessionState = SessionState.ReadOnly)]

   public void Test6()

   {

     //Session只能读不能写

   }

 

   [AjaxMethod(SessionState = SessionState.ReadWrite)]

   public void Test7()

   {

     //Session可以读写

   }

 

   [AjaxMethod(IsAsync = true)]

   public void Test8()

   {

     //异步调用

   }

Salin selepas log masuk

  前面我们已经熟悉基本的执行流程,现在直接进入主题。

Ajax约定

  通常现在主流浏览器在使用ajax发送异步请求时,请求头都会带上一个:X-Requested-With:XMLHttpRequest 的标记。我们也可以直接通过这个标记来判断是不是ajax请求,不过项目中可能有用其它的组件,为了不相互影响,我们加入一个自定义的请求头。这里为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

internal static class AjaxConfig

 {

  /// <summary>

  /// 请求头Ajax标记键

  /// </summary>

  public const string Key = "AjaxFlag";

 

  /// <summary>

  /// 请求头Ajax标记值

  /// </summary>

  public const string Value = "XHR";

 

  /// <summary>

  /// 请求头Ajax方法标记

  /// </summary>

  public const string MethodName = "";

 }

Salin selepas log masuk

  意思是如果http 的请求头包含一个 AjaxFlag : XHR,就是我们要处理的。另外http header的MethodName就表示我们要执行的方法的名称。

AjaxMethodAttribute标记属性

  标记属性是给反射用的,在这里定义我们需要的一些功能。我们希望有:

  1. 可以配置Session状态

  2. 支持异步Handler

  3. 支持Get缓存

  4. 支持服务端缓存

  定义如下,用AttributeUsag标记该标记只能用于方法上。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

/// <summary>

/// ajax方法标记属性

/// </summary>

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]

public class AjaxMethodAttribute : Attribute

{

  public AjaxMethodAttribute()

  {     

  }

 

  private SessionState _sessionState = SessionState.None;

  private int _outputCache = 0;

  private int _serverCache = 0;

  private ContentType _contentType = ContentType.Plain;

  private bool _isUseAsync = false;

 

  /// <summary>

  /// session状态

  /// </summary>

  public SessionState SessionState

  {

    get { return _sessionState; }

    set { _sessionState = value; }

  }

 

  /// <summary>

  /// 客户端缓存时间,以秒为单位。该标记只对get请求有效

  /// </summary>

  public int OutputCache

  {

    get { return _outputCache; }

    set { _outputCache = value; }

  }

 

  /// <summary>

  /// 服务端缓存时间,以秒为单位

  /// </summary>

  public int ServerCache

  {

    get { return _serverCache; }

    set { _serverCache = value; }

  }   

 

  /// <summary>

  /// 输出类型(默认为text/plain)

  /// </summary>

  public ContentType ContentType

  {

    get { return _contentType; }

    set { _contentType = value; }

  }

 

  /// <summary>

  /// 使用启用异步处理

  /// </summary>

  public bool IsAsync

  {

    get { return _isUseAsync; }

    set { _isUseAsync = value; }

  }

}

 

/// <summary>

/// Session状态

/// </summary>

public enum SessionState

{

  None,

  ReadOnly,

  ReadWrite   

}

 

/// <summary>

/// 输出内容类型

/// </summary>

public enum ContentType

{

  Plain,

  Html,

  XML,

  Javascript,

  JSON

}

Salin selepas log masuk

各种处理程序和AjaxHandlerFactory

  按照上一篇的说法,具体的Handler主要分为两类,异步和非异步;这两类下,对于Session的状态又有3三种,不支持、只支持读(实现IReadOnlySessionState接口)、支持读写(实现IRequiresSessionState接口)。IReadOnlySessionState和IRequiresSessionState都只是标记接口(无任何方法,其实应该用标记属性实现比较合理)。异步的Handler需要实现IHttpAsyncHandler接口,该接口又实现了IHttpHandler。Handler的ProcessRequest方法(或BeginProcessRequest)就是我们要执行方法的地方。定义如下:

  非异步状态的Handler:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

//不支持Session

 internal class SyncAjaxHandler : IHttpHandler

 {

   private Page _page;

   private CacheMethodInfo _cacheMethodInfo;

 

   internal SyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo)

   {

     _page = page;

     _cacheMethodInfo = cacheMethodInfo;

   }

 

   public void ProcessRequest(HttpContext context)

   {

     //执行方法(下面详细介绍)

     Executor.Execute(_page, context, _cacheMethodInfo);

   }

 

   public bool IsReusable

   {

     get { return false; }

   }

 

   public static SyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state)

   {

     switch (state)

     {

       case SessionState.ReadOnly:

         return new SyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo);

       case SessionState.ReadWrite:

         return new SyncAjaxSessionHandler(page, cacheMethodInfo);

       default:

         return new SyncAjaxHandler(page, cacheMethodInfo);

     }

   }

 }

 

 //支持只读Session

 internal class SyncAjaxSessionReadOnlyHandler : SyncAjaxHandler, IReadOnlySessionState

 {

   internal SyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)

     : base(page, cacheMethodInfo)

   {

   }

 }

 

 //支持读写Session

 internal class SyncAjaxSessionHandler : SyncAjaxHandler, IRequiresSessionState

 {

   internal SyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)

     : base(page, cacheMethodInfo)

   {

   }

 }

Salin selepas log masuk

  异步状态的Handler:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

//不支持Session

 internal class ASyncAjaxHandler : IHttpAsyncHandler, IHttpHandler

 {

   private Page _page;

   private CacheMethodInfo _cacheMethodInfo;

 

   internal ASyncAjaxHandler(Page page, CacheMethodInfo cacheMethodInfo)

   {

     _page = page;

     _cacheMethodInfo = cacheMethodInfo;

   }

 

   public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)

   {

     //执行方法(下面详细介绍)

     Action<Page, HttpContext, CacheMethodInfo> action = new Action<Page, HttpContext, CacheMethodInfo>(Executor.Execute);

     IAsyncResult result = action.BeginInvoke(_page, context, _cacheMethodInfo, cb, action);

     return result;

   }

 

   public void EndProcessRequest(IAsyncResult result)

   {

     Action<Page, HttpContext, CacheMethodInfo> action = result.AsyncState as Action<Page, HttpContext, CacheMethodInfo>;

     action.EndInvoke(result);

   }

 

   public void ProcessRequest(HttpContext context)

   {

     throw new NotImplementedException();

   }

 

   public bool IsReusable

   {

     get { return false; }

   }

 

   public static ASyncAjaxHandler CreateHandler(Page page, CacheMethodInfo cacheMethodInfo, SessionState state)

   {

     switch (state)

     {

       case SessionState.ReadOnly:

         return new ASyncAjaxSessionReadOnlyHandler(page, cacheMethodInfo);

       case SessionState.ReadWrite:

         return new ASyncAjaxSessionHandler(page, cacheMethodInfo);

       default:

         return new ASyncAjaxHandler(page, cacheMethodInfo);

     }

   }

 }

 

 //支持只读Session

 internal class ASyncAjaxSessionReadOnlyHandler : ASyncAjaxHandler, IReadOnlySessionState

 {

   internal ASyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)

     : base(page, cacheMethodInfo)

   {

   }

 }

 

 //支持读写Session

 internal class ASyncAjaxSessionHandler : ASyncAjaxHandler, IRequiresSessionState

 {

   internal ASyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)

     : base(page, cacheMethodInfo)

   {

   }

 }

Salin selepas log masuk

  AjaxHandlerFactory实现了IHandlerFactory接口,用来根据请求生成具体的Handler,它需要在web.config进行注册使用。AjaxHandlerFactory的GetHandler是我们拦截请求的第一步。通过请求头的AjaxFlag:XHR来判断是否需要我们处理,如果是,则创建一个Handler,否则按照普通的方式进行。由于我们的方法是写在.aspx.cs内的,我们的请求是.aspx后缀的,也就是页面(Page,实现了IHttpHandler)类型,Page是通过PageHandlerFactory创建的,PageHandlerFactory也实现了IHandlerFactory接口,表示它是用来创建处理程序的。所以我们需要用PageHandlerFactory来创建一个IHttpHandler,不过PageHandlerFactory的构造函数是protected internal类型的,我们无法直接new一个,所以需要通过一个CommonPageHandlerFactory继承它来实现。

  通过PageHandlerFactory获得Page后,结合方法名称,我们就可以反射获取AjaxMethodAttribute标记属性了。然后根据它的相关属性生成具体的Handler。具体代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

internal class CommonPageHandlerFactory : PageHandlerFactory { }

 

 internal class AjaxHandlerFactory : IHttpHandlerFactory

 {

   public void ReleaseHandler(IHttpHandler handler)

   {

   }

 

   public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)

   {

     HttpRequest request = context.Request;

     if (string.Compare(request.Headers[AjaxConfig.Key], AjaxConfig.Value, true) == 0)

     {

       //检查函数标记

       string methodName = request.Headers[AjaxConfig.MethodName];

       if (methodName.IsNullOrEmpty())

       {

         Executor.EndCurrentRequest(context, "方法名称未正确指定!");

         return null;

       }

       try

       {         

         CommonPageHandlerFactory ajaxPageHandler = new CommonPageHandlerFactory();

         IHttpHandler handler = ajaxPageHandler.GetHandler(context, requestType, url, pathTranslated);

         Page page = handler as Page;

         if (page == null)

         {

           Executor.EndCurrentRequest(context, "处理程序类型必须是aspx页面!");

           return null;

         }

         return GetHandler(page, methodName, context);

       }

       catch

       {

         Executor.EndCurrentRequest(context, url + " 不存在!");

         return null;

       }

     }

     if (url.EndsWith(".aspx", StringComparison.CurrentCultureIgnoreCase))

     {

       CommonPageHandlerFactory orgPageHandler = new CommonPageHandlerFactory();

       return orgPageHandler.GetHandler(context, requestType, url, pathTranslated);

     }

     return null;

   }

 

   /// <summary>

   /// 获取自定义处理程序

   /// </summary>

   /// <param name="page">处理页面</param>

   /// <param name="methodName">处理方法</param>

   /// <param name="context">当前请求</param>

   private IHttpHandler GetHandler(Page page, string methodName, HttpContext context)

   {

     //根据Page和MethodName进行反射,获取标记属性(下面详细介绍)

     CacheMethodInfo methodInfo = Executor.GetDelegateInfo(page, methodName);

     if (methodInfo == null)

     {

       Executor.EndCurrentRequest(context, "找不到指定的Ajax方法!");

       return null;

     }

     AjaxMethodAttribute attribute = methodInfo.AjaxMethodAttribute;

     if (attribute.ServerCache > 0)

     {

       //先查找缓存

       object data = CacheHelper.TryGetCache(context);

       if (data != null)

       {

         Executor.EndCurrentRequest(context, data);

         return null;

       }

     }

     if (attribute.IsAsync)

     {

       //异步处理程序

       return ASyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState);

     }

     return SyncAjaxHandler.CreateHandler(page, methodInfo, attribute.SessionState);

   }

 }

Salin selepas log masuk

  上面的CacheMethodInfo是用于缓存调用方法的相关信息的,第一篇我们有提到过优化缓存的一些方法,其中就包括缓存+委托。但这里我们并不直接缓存方法的MethodInfo,因为缓存MethodInfo的话,需要通过Invoke去执行,这样的效率比较低。这里我缓存的是方法的委托,该委托的签名为:Func,该委托的返回值为object类型,表示可以返回任意的类型(我们可以在组件内部进行处理,例如如果是引用类型(非string),就将其序列化为json,但这里并没有实现)。该委托接收两个参数,第一个参数是方法所属的对象,如果是静态方法就是null;第二个参数是方法的参数,定义为object[]表示可以接收任意类型的参数。通过委托执行方法,与直接调用方法的效率差别就不是很大(对委托不熟悉的朋友可以参见:委托)。CacheMethodInfo的定义如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

/// <summary>

 /// 缓存方法信息 

 /// </summary>

 sealed class CacheMethodInfo

 {

   /// <summary>

   /// 方法名称

   /// </summary>

   public string MethodName { get; set; }

 

   /// <summary>

   /// 方法委托

   /// </summary>

   public Func<object, object[], object> Func { get; set; }

 

   /// <summary>

   /// 方法参数

   /// </summary>

   public ParameterInfo[] Parameters { get; set; }

 

   /// <summary>

   /// Ajax标记属性

   /// </summary>

   public AjaxMethodAttribute AjaxMethodAttribute { get; set; }

 }

Salin selepas log masuk

核心方法

1. Eexcutor.GetDelegateInfo 获取方法相关信息

  该方法用于遍历页面类,获取所有AjaxMethodAttribute标记的方法信息,生成一个CacheMethodInfo对象,包括标记信息、方法名称、参数信息,以及最重要的方法委托。该对象会缓存在一个哈希表中,下次获取时,直接从内存获得。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

/// <summary>

   /// 获取页面标记方法信息

   /// </summary>

   /// <param name="page">页面对象</param>

   /// <param name="methodName">方法名称</param>

   internal static CacheMethodInfo GetDelegateInfo(Page page, string methodName)

   {

     if (page == null)

     {

       throw new ArgumentNullException("page");

     }

     Type type = page.GetType();

     //ajaxDelegateTable是一个Hashtable

     Dictionary<string, CacheMethodInfo> dic = ajaxDelegateTable[type.AssemblyQualifiedName] as Dictionary<string, CacheMethodInfo>;

     if (dic == null)

     {

       dic = new Dictionary<string, CacheMethodInfo>();

       //遍历页面的所有MethodInfo

       IEnumerable<CacheMethodInfo> infos = (from m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)

                          let ca = m.GetCustomAttributes(typeof(AjaxMethodAttribute), false).FirstOrDefault()

                          where ca != null

                          select new CacheMethodInfo

                          {

                            //方法标记属性

                            AjaxMethodAttribute = ca as AjaxMethodAttribute,

                            //方法名称

                            MethodName = m.Name,

                            //方法参数信息

                            Parameters = m.GetParameters()

                          });

 

       if (infos.IsNullOrEmpty())

       {

         return null;

       }         

       for (int i = 0, length = infos.Count(); i < length; i++)

       {         

         CacheMethodInfo cacheMethodInfo = infos.ElementAt(i);         

         string name = cacheMethodInfo.MethodName;

         MethodInfo methodInfo = type.GetMethod(name);

         if (!dic.ContainsKey(name))

         {           

           //根据MethodInfo获取方法委托

           cacheMethodInfo.Func = ReflectionUtil.GetMethodDelegate(methodInfo);

           dic.Add(name, cacheMethodInfo);

         }

       }

       ajaxDelegateTable[type.AssemblyQualifiedName] = dic;

     }

     CacheMethodInfo currentMethodInfo = null;

     dic.TryGetValue(methodName, out currentMethodInfo);

     return currentMethodInfo;     

   }

Salin selepas log masuk

  获取方法的委托的是通过一个ReflectionUtil获得的,该类主要用来优化反射,它通过Expression,可以将MethodInfo编译成Func委托,为Type编译一个Func委托,用于创建实例对象。

  通过Expression优化反射

  Expression(表达式树)允许我们将代码逻辑以表达式的形式存储在树状结构里,然后在运行时去动态解析,实现动态编辑和执行代码。熟悉ORM框架的朋友对Expression肯定很熟悉,因为大部分方法都有一个Expression类型的参数。访问关系型数据库的本质还是sql语句,orm的工作就是为开发人员屏蔽这个过程,以面向对象的方式去读写数据库,而不是自己编写sql语句。例如,Users.Where(u => u.Age > 18) 就可查询年龄大于18的用户。这里不对应用在orm的过程进行详解,下面我们介绍如何用Expression并利用它来生成委托。

  .net定义了许多表达式类型,这些类型都派生自Expression,Expression是一个抽象类,而且是一个工厂类,所有类型的表达式都通过它来创建。如图:

  先看一个 1 * 2 + 2 例子,我们用表达树来描述来描述它:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

/*

       * a * b + 2

       */

 

      /*

      直接操作

      int a = 1, b = 2;

      int result = a * 2 + 2;

      */

 

      /*

      通过委托调用

      Func<int, int, int> func = new Func<int, int, int>((a, b) => { return a * b + 2; });

      func(1, 2);

      */

 

      /*通过Expression调用*/

 

      //定义两个参数

      ParameterExpression pe1 = Expression.Parameter(typeof(int), "a");

      ParameterExpression pe2 = Expression.Parameter(typeof(int), "b");

      //定义一个常量

      ConstantExpression constExpression = Expression.Constant(2);     

 

      //参数数组

      ParameterExpression[] parametersExpression = new ParameterExpression[]{pe1,pe2};

 

      //一个乘法运算

      BinaryExpression multiplyExpression = Expression.Multiply(pe1, pe2);

 

      //一个加法运算

      BinaryExpression unaryExpression = Expression.Add(multiplyExpression, constExpression);

 

      //将上面的表达式转换为一个委托表达式

      LambdaExpression lambdaExpression = Expression.Lambda<Func<int, int, int>>(unaryExpression, parametersExpression);

 

      //将委托编译成可执行代码

      Func<int,int,int> func = lambdaExpression.Compile() as Func<int,int,int>;

      Console.WriteLine(func(1, 2));

Salin selepas log masuk

  可以看到我们最终将其编译为一个具体类型的委托了。下面看我们真正用到的方法是如何实现的,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

public static Func<object, object[], object> GetMethodDelegate(MethodInfo methodInfo)

   {

     if (methodInfo == null)

     {

       throw new ArgumentNullException("methodInfo");

     }

     //定义参数表达式,它表示委托的第一个参数

     ParameterExpression instanceExp = Expression.Parameter(typeof(object), "instance");

 

     //定义参数表达式,它表示委托的第二个参数

     ParameterExpression paramExp = Expression.Parameter(typeof(object[]), "parameters");

 

     //获取方法的参数信息数组

     ParameterInfo[] paramInfos = methodInfo.GetParameters();

 

     //参数表达式集合

     List<Expression> paramExpList = new List<Expression>();

 

     int length = paramInfos.Length;

     for (int i = 0; i < length; i++)

     {

       //获取paramExp参数数组的第i个元素

       BinaryExpression valueObj = Expression.ArrayIndex(paramExp, Expression.Constant(i));

 

       //将其转换为与参数类型一致的类型

       UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);

 

       //添加到参数集合

       paramExpList.Add(valueCast);

     

 

     //方法所属的实例的表达式,如果为静态则为null

     UnaryExpression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceExp, methodInfo.ReflectedType);

 

     //表示调用方法的表达式

     MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, paramExpList);

 

     //将表达式目录描述的lambda编译为可执行代码(委托)

     if (methodCall.Type == typeof(void))

     {

       Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceExp, paramExp);

       Action<object, object[]> action = lambda.Compile();

       return (instance, parameters) =>

       {

         action(instance, parameters);

         return null;

       };

     }

     else

     {

       UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));

       Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceExp, paramExp);

       return lambda.Compile();

     }

   }

Salin selepas log masuk

  具体代码都有注释解释,最终我们获得了一个Func类型的委托,它会作为CacheMethodInfo的属性进行缓存。有兴趣测试反射性能的朋友,也不妨去测试对比一下这几种方式执行的效率差别:1.直接执行方法 2.Emit 3. 缓存+委托 4.Delegate.DynamicInvoke。

2. Executor.Execute 执行委托

  在执行委托前,我们需要先从请求获取参数,映射到方法。参数可以是简单的类型,如 string Test(int i,int j); 也可以是一个对象,如 string Test(User user); 如果是 string Test(User user1, User user2) 也行,提交参数时只需要加上 user1或 user2 前缀即可,例如 user1.Name,user2.Name。这里没有支持更多的匹配方式,像mvc,它还支持嵌套类型等等,这些可以自己去实现。如果参数是一个对象,我们可能需要为它的字段进行赋值,也可能为它的属性进行赋值。这里我们定义一个DataMember,用来表示字段或属性的父类。如:

1

2

3

4

5

6

7

internal abstract class DataMember

{

  public abstract string Name { get; }

  public abstract Type MemberType { get; }

  public abstract void SetValue(object instance,object value);

  public abstract object GetValue(object instance);

}

Salin selepas log masuk

  接着定义属性类型PropertyMember和字段类型FieldMember,分别继承了DataMember。

  PropertyMember定义:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

internal class PropertyMember : DataMember

{

  private PropertyInfo property;

  public PropertyMember(PropertyInfo property)

  {

    if (property == null)

    {

      throw new ArgumentNullException("property");

    }

    this.property = property;

  }

 

  public override void SetValue(object instance, object value)

  {

    if (instance == null)

    {

      throw new ArgumentNullException("instance");

    }

    this.property.SetValue(instance, value, null);

  }

 

  public override object GetValue(object instance)

  {

    if (instance == null)

    {

      throw new ArgumentNullException("instance");

    }

    return this.property.GetValue(instance,null);

  }

 

  public override string Name

  {

    get { return this.property.Name; }

  }

 

  public override Type MemberType

  {

    get { return this.property.PropertyType; }

  }

}

Salin selepas log masuk

  FieldMember定义:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

internal class FieldMember : DataMember

 {

   private FieldInfo field;

   public FieldMember(FieldInfo field)

   {

     if (field == null)

     {

       throw new ArgumentNullException("field");

     }

     this.field = field;

   }

 

   public override void SetValue(object instance, object value)

   {

     if (instance == null)

     {

       throw new ArgumentNullException("instance");

     }

     this.field.SetValue(instance, value);

   }

 

   public override object GetValue(object instance)

   {

     if (instance == null)

     {

       throw new ArgumentNullException("instance");

     }

     return this.field.GetValue(instance);

   }

 

   public override string Name

   {

     get { return this.field.Name;}

   }

 

   public override Type MemberType

   {

     get { return this.field.FieldType; }

   }

 }

Salin selepas log masuk

  定义一个DataMemberManager,用来遍历Type,获取所有字段和属性的,实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

internal static class DataMemberManager

 {

   /// <summary>

   /// 获取实例字段/属性集合

   /// </summary>

   /// <param name="type">类型</param>

   /// <returns></returns>

   public static List<DataMember> GetDataMember(Type type)

   {

     if (type == null)

     {

       throw new ArgumentNullException("type");

     }

     IEnumerable<PropertyMember> propertyMembers = from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)

                        select new PropertyMember(property);

     IEnumerable<FieldMember> fieldMembers = from field in type.GetFields(BindingFlags.Instance | BindingFlags.Public)

                      select new FieldMember(field);

     List<DataMember> members = new List<DataMember>();

     foreach(var property in propertyMembers)

     {

       members.Add(property);

     }

     foreach (var field in fieldMembers)

     {

       members.Add(field);

     }

     return members;

   }

 }

Salin selepas log masuk

  在前面我们定义的Handler的ProcessRequest方法中,我们调用了Executor.Execute,该方法用于执行委托,实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

/// <summary>

   /// 核心函数,执行Handler的方法

   /// </summary>

   /// <param name="page">页面对象</param>

   /// <param name="context">请求上下文</param>

   /// <param name="cacheMethodInfo">缓存方法原数据</param>

   internal static void Execute(Page page, HttpContext context, CacheMethodInfo methodInfo)

   {

     if (page == null)

     {

       throw new ArgumentNullException("page");

     }

     try

     {

       if (methodInfo != null)

       {

         HttpRequest request = context.Request;

         object[] parameters = GetParametersFromRequest(request, methodInfo.Parameters);

         object data = methodInfo.Func(page, parameters);

         int serverCache = methodInfo.AjaxMethodAttribute.ServerCache;

         if (serverCache > 0)

         {

           CacheHelper.Insert(context, methodInfo.AjaxMethodAttribute.ServerCache, data);

         }

         EndCurrentRequest(context, data, methodInfo.AjaxMethodAttribute.OutputCache);

       }

       else

       {

         EndCurrentRequest(context, "找不到合适的Ajax方法!");

       }

     }

     catch (FormatException)

     {

       EndCurrentRequest(context, "调用方法匹配到无效的参数!");

     }

     catch (InvalidCastException)

     {

       EndCurrentRequest(context, "参数转换出错!");

     }

     catch (System.Threading.ThreadAbortException)

     {

       //do nothing

     }

     catch (Exception ex)

     {

       EndCurrentRequest(context, ex.Message);

     }

   }

Salin selepas log masuk

  CacheMethodInfo我们已经获得了,现在只要获得参数我们就可以执行方法。

  GetParameterFromRequest用于从请求获取object[]参数数组。根据上面所说的,如果参数是一个简单类型,那么直接进行转换;如果是实例对象,那么我们先要创建new一个实例对象,然后为其字段或属性赋值。实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

/// <summary>

   /// 从请求获取参参数

   /// </summary>

   /// <param name="request">HttpRequest</param>

   ///<param name="parameters">参数信息</param>

   /// <returns>参数数组</returns>

   private static object[] GetParametersFromRequest(HttpRequest request, ParameterInfo[] parameters)

   {

     if (parameters.IsNullOrEmpty())

     {

       return null;

     }

     int length = parameters.Length;

     object[] realParameters = new object[length];

     for (int i = 0; i < length; i++)

     {

       ParameterInfo pi = parameters[i];

       Type piType = pi.ParameterType.GetRealType();

       object value = null;

       if (piType.IsValueType())

       {

         //值类型

         value = ModelUtil.GetValue(request, pi.Name, piType);

         value = value ?? Activator.CreateInstance(piType);

       }

       else if (piType.IsClass)

       {

         //引用类型

         object model = ModelUtil.CreateModel(piType);

         ModelUtil.FillModelByRequest(request, pi.Name, piType, model);

         value = model;

       }

       else

       {

         throw new NotSupportedException(pi.Name + " 参数不被支持");

       }

       realParameters[i] = value;

     }

     return realParameters;

   }

Salin selepas log masuk

  ModelUtil会从Http Request获取参数,并进行类型转换处理:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

internal static class ModelUtil

 {

   /// <summary>

   /// 缓存构造函数

   /// </summary>

   private static Hashtable constructorTable = Hashtable.Synchronized(new Hashtable());

 

   /// <summary>

   /// 根据名称从HttpRequest获取值

   /// </summary>

   /// <param name="request">HttpRequest</param>

   /// <param name="name">键名称</param>

   /// <param name="type">参数类型</param>

   /// <returns></returns>

   public static object GetValue(HttpRequest request, string name, Type type)

   {

     string[] values = null;

     if (string.Compare(request.RequestType, "POST", true) == 0)

     {

       values = request.Form.GetValues(name);

     }

     else

     {

       values = request.QueryString.GetValues(name);

     }

     if (values.IsNullOrEmpty())

     {

       return null;

     }

     string data = values.Length == 1 ? values[0] : string.Join(",", values);

     return Convert.ChangeType(data, type);

   }

 

   /// <summary>

   /// 创建实例对象

   /// </summary>

   /// <param name="type">实例类型</param>

   /// <returns></returns>

   public static object CreateModel(Type type)

   {

     if (type == null)

     {

       throw new ArgumentNullException("type");

     }

     Func<object> func = constructorTable[type.AssemblyQualifiedName] as Func<object>;

     if (func == null)

     

       func = ReflectionUtil.GetConstructorDelegate(type);

       constructorTable[type.AssemblyQualifiedName] = func;

     }

     if (func != null)

     {

       return func();

     }

     return null;

   }

 

   /// <summary>

   /// 填充模型

   /// </summary>

   /// <param name="request">HttpRequest</param>

   /// <param name="name">键名称</param>

   /// <param name="prefix">参数类型</param>

   /// <parparam name="model">实例对象</parparam>

   public static void FillModelByRequest(HttpRequest request, string name, Type type, object model)

   {

     if (model == null)

     {

       return;

     }

     IEnumerable<DataMember> members = DataMemberManager.GetDataMember(type);

     if (members.IsNullOrEmpty())

     {

       return;

     }

     object value = null;

     foreach (DataMember member in members)

     {

       value = GetValue(request, string.Format("{0}.{1}", name, member.Name), member.MemberType);

       value = value ?? GetValue(request, member.Name, member.MemberType);

       member.SetValue(model, value);

     }

   }

 }

Salin selepas log masuk

  如果是引用类型,需要通过构造函数创建对象,像前面用于,这里我们也用Expression来构建一个Func类型的委托来优化,它调用了ReflectionUtil.GetConstructorDelegate方法。实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/// <summary>

  /// 获取构造函数委托

  /// </summary>

  /// <param name="type">实例类型</param>

  /// <returns></returns>

  public static Func<object> GetConstructorDelegate(Type type)

  {

    if (type == null)

    {

      throw new ArgumentNullException("type");

    }

    ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);

    if (ci == null)

    {

      throw new MissingMemberException("类型必须有一个无参public构造函数!");

    }

    NewExpression newExp = Expression.New(type);

    Expression<Func<object>> lambda = Expression.Lambda<Func<object>>(newExp);

    return lambda.Compile();

  }

Salin selepas log masuk

  最后再输出结果时,如果是Get请求,并且需要缓存,我们还需要设置一下Response.Cache。如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

private static void EndRequest(HttpContext context, object data, int outPutCache, ContentType contentType)

{

  HttpResponse response = context.Response;

  if (outPutCache != 0)

  {

    if (string.Compare(context.Request.HttpMethod, "GET", true) == 0)

    {

      if (outPutCache > 0)

      {

        response.Cache.SetCacheability(HttpCacheability.Public);

        response.Cache.SetMaxAge(new TimeSpan(0, 0, outPutCache));

        response.Cache.SetExpires(DateTime.Now.AddSeconds(outPutCache));

      }

      else

      {

        response.Cache.SetCacheability(HttpCacheability.NoCache);

        response.Cache.SetNoStore();

      }

    }

  }

  response.ContentType = GetContentType(contentType);

  response.ContentEncoding = System.Text.Encoding.UTF8;

  if (data != null)

  {

    response.Write(data);

  }

  response.End();

}

Salin selepas log masuk

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

ajax返回object Object的快速解决方法

jQuery中ajax的4种常用请求方式介绍

使用原生ajax处理json字符串的方法

Atas ialah kandungan terperinci 编写轻量ajax组件第三篇实现. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan