Cette fois, je vais vous expliquer comment écrire des composants ajax légers (explications détaillées avec images et textes). Quelles sont les précautions pour écrire des composants ajax légers. Ce qui suit est un cas pratique, prenons un. regarder.
Grâce à l'introduction précédente, nous savons comment exécuter la méthode de l'objet page. Le cœur est la réflexion, qui est le processus d'obtention des paramètres de la requête et d'exécution de la méthode spécifiée. En fait, cela est très similaire à l'idée de base du framework ASP.NET MVC. Il analysera l'URL, en obtiendra le contrôleur et les noms d'action, puis activera l'objet contrôleur, obtiendra les paramètres d'action de la requête et exécuter l'action. Sur la plate-forme de formulaire Web, nous écrivons la méthode dans .aspx.cs. Ce que nous voulons réaliser est d'exécuter la méthode spécifiée avant la génération de l'objet de page, puis de renvoyer le résultat.
Examinons d'abord quelques exemples d'appels après implémentation. Ces fonctions peuvent également être utilisées en combinaison :
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
[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()
{
}
[AjaxMethod(SessionState = SessionState.ReadOnly)]
public
void Test6()
{
}
[AjaxMethod(SessionState = SessionState.ReadWrite)]
public
void Test7()
{
}
[AjaxMethod(IsAsync = true)]
public
void Test8()
{
}
Copier après la connexion
Nous connaissons déjà le processus d'exécution de base, et maintenant c'est parti. directement au sujet.
Convention Ajax
Habituellement, lorsque les navigateurs grand public utilisent ajax pour envoyer des requêtes asynchrones, l'en-tête de la requête portera une marque : X-Requested-With:XMLHttpRequest. Nous pouvons également utiliser directement cette balise pour déterminer s'il s'agit d'une requête ajax, mais d'autres composants peuvent être utiles dans le projet. Afin de ne pas s'influencer mutuellement, nous ajoutons un en-tête de requête personnalisé. Voici :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
internal
static
class
AjaxConfig
{
public
const
string Key =
"AjaxFlag"
;
public
const
string Value =
"XHR"
;
public
const
string MethodName =
""
;
}
Copier après la connexion
Cela signifie que si l'en-tête de la requête http contient un AjaxFlag: XHR, c'est ce que nous devons traiter. De plus, le MethodName de l’en-tête http indique le nom de la méthode que nous souhaitons exécuter.
Attribut mark AjaxMethodAttribute
L'attribut mark est destiné à la réflexion. Nous définissons ici certaines fonctions dont nous avons besoin. Nous espérons avoir :
1. Peut configurer l'état de la session
2. Prise en charge du gestionnaire asynchrone
3. Prise en charge du cache
4. Prise en charge du serveur - la mise en cache latérale
est définie comme suit. Le marquer avec AttributeUsag ne peut être utilisé que sur les méthodes.
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
[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;
public
SessionState SessionState
{
get {
return
_sessionState; }
set { _sessionState = value; }
}
public
int OutputCache
{
get {
return
_outputCache; }
set { _outputCache = value; }
}
public
int ServerCache
{
get {
return
_serverCache; }
set { _serverCache = value; }
}
public
ContentType ContentType
{
get {
return
_contentType; }
set { _contentType = value; }
}
public
bool IsAsync
{
get {
return
_isUseAsync; }
set { _isUseAsync = value; }
}
}
public
enum SessionState
{
None,
ReadOnly,
ReadWrite
}
public
enum ContentType
{
Plain,
Html,
XML,
Javascript,
JSON
}
Copier après la connexion
Divers gestionnaires et AjaxHandlerFactory
Selon l'article précédent, les gestionnaires spécifiques sont principalement divisés en deux catégories, asynchrones et non asynchrones, pour Session Il y en a trois ; types d'états : non pris en charge, uniquement pris en charge pour la lecture (implémentant l'interface IReadOnlySessionState) et prenant en charge la lecture et l'écriture (implémentant l'interface IRequiresSessionState). IReadOnlySessionState et IRequiresSessionState ne sont que des interfaces de marquage (sans aucune méthode, il est plus raisonnable d'utiliser des attributs de marquage pour les implémenter). Le gestionnaire asynchrone doit implémenter l'interface IHttpAsyncHandler, qui à son tour implémente IHttpHandler. La méthode ProcessRequest (ou BeginProcessRequest) de Handler est l'endroit où nous voulons exécuter la méthode. Il est défini comme suit :
Gestionnaire en état non asynchrone :
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
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);
}
}
}
internal
class
SyncAjaxSessionReadOnlyHandler : SyncAjaxHandler, IReadOnlySessionState
{
internal SyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)
: base(page, cacheMethodInfo)
{
}
}
internal
class
SyncAjaxSessionHandler : SyncAjaxHandler, IRequiresSessionState
{
internal SyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)
: base(page, cacheMethodInfo)
{
}
}
Copier après la connexion
Gestionnaire en état asynchrone :
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
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);
}
}
}
internal
class
ASyncAjaxSessionReadOnlyHandler : ASyncAjaxHandler, IReadOnlySessionState
{
internal ASyncAjaxSessionReadOnlyHandler(Page page, CacheMethodInfo cacheMethodInfo)
: base(page, cacheMethodInfo)
{
}
}
internal
class
ASyncAjaxSessionHandler : ASyncAjaxHandler, IRequiresSessionState
{
internal ASyncAjaxSessionHandler(Page page, CacheMethodInfo cacheMethodInfo)
: base(page, cacheMethodInfo)
{
}
}
Copier après la connexion
AjaxHandlerFactory implémente l'interface IHandlerFactory, qui est utilisé pour générer des données spécifiques basées sur le gestionnaire de requêtes, il doit être enregistré dans web.config. GetHandler d'AjaxHandlerFactory est notre première étape dans l'interception des requêtes. Utilisez AjaxFlag:XHR dans l'en-tête de la requête pour déterminer si nous devons le traiter. Si tel est le cas, créez un gestionnaire, sinon procédez de la manière normale. Puisque notre méthode est écrite en .aspx.cs, notre requête est du suffixe .aspx, qui est le type de page (Page, implémente IHttpHandler). La page est créée via PageHandlerFactory, qui implémente également l'interface IHandlerFactory, ce qui signifie qu'elle est utilisée pour. créer des gestionnaires. Nous devons donc utiliser PageHandlerFactory pour créer un IHttpHandler, mais le constructeur de PageHandlerFactory est de type interne protégé Nous ne pouvons pas en créer un nouveau directement, nous devons donc en hériter via un CommonPageHandlerFactory.
Après avoir obtenu la page via PageHandlerFactory, combinée avec le nom de la méthode, nous pouvons obtenir l'attribut de marque AjaxMethodAttribute par réflexion. Générez ensuite un gestionnaire spécifique en fonction de ses propriétés associées. Le code spécifique est le suivant :
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
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;
}
private
IHttpHandler GetHandler(Page page, string methodName, HttpContext context)
{
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);
}
}
Copier après la connexion
上面的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
sealed
class
CacheMethodInfo
{
public
string MethodName { get; set; }
public
Func<object, object[], object> Func { get; set; }
public
ParameterInfo[] Parameters { get; set; }
public
AjaxMethodAttribute AjaxMethodAttribute { get; set; }
}
Copier après la connexion
核心方法
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
internal
static
CacheMethodInfo GetDelegateInfo(Page page, string methodName)
{
if
(page == null)
{
throw
new
ArgumentNullException(
"page"
);
}
Type type = page.
GetType
();
Dictionary<string, CacheMethodInfo> dic = ajaxDelegateTable[type.AssemblyQualifiedName]
as
Dictionary<string, CacheMethodInfo>;
if
(dic == null)
{
dic =
new
Dictionary<string, CacheMethodInfo>();
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))
{
cacheMethodInfo.Func = ReflectionUtil.GetMethodDelegate(methodInfo);
dic.Add(name, cacheMethodInfo);
}
}
ajaxDelegateTable[type.AssemblyQualifiedName] = dic;
}
CacheMethodInfo currentMethodInfo = null;
dic.TryGetValue(methodName, out currentMethodInfo);
return
currentMethodInfo;
}
Copier après la connexion
获取方法的委托的是通过一个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
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));
Copier après la connexion
可以看到我们最终将其编译为一个具体类型的委托了。下面看我们真正用到的方法是如何实现的,代码如下:
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
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++)
{
BinaryExpression valueObj = Expression.ArrayIndex(paramExp, Expression.Constant(i));
UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);
paramExpList.Add(valueCast);
}
UnaryExpression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceExp, methodInfo.ReflectedType);
MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, paramExpList);
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();
}
}
Copier après la connexion
具体代码都有注释解释,最终我们获得了一个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);
}
Copier après la connexion
接着定义属性类型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
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; }
}
}
Copier après la connexion
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
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; }
}
}
Copier après la connexion
定义一个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
{
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;
}
}
Copier après la connexion
在前面我们定义的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
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)
{
}
catch
(Exception ex)
{
EndCurrentRequest(context, ex.Message);
}
}
Copier après la connexion
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
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;
}
Copier après la connexion
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
internal
static
class
ModelUtil
{
private
static
Hashtable constructorTable = Hashtable.Synchronized(
new
Hashtable());
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);
}
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;
}
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);
}
}
}
Copier après la connexion
如果是引用类型,需要通过构造函数创建对象 ,像前面用于,这里我们也用Expression来构建一个Func类型的委托来优化,它调用了ReflectionUtil.GetConstructorDelegate方法。实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
}
Copier après la connexion
最后再输出结果时,如果是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
();
}
Copier après la connexion
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
Ajax请求async有哪些方式?应该如何使用
FileUpload实现单个文件的上传
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
2018-06-11 11:57:34
2018-06-15 15:55:18
2018-06-15 15:49:00
2018-06-15 15:46:15
2018-06-15 15:42:38
2018-06-15 15:40:34
2018-06-15 15:39:32
2018-06-15 15:37:14
2018-06-15 15:34:21
2018-06-15 15:22:51
Recommandations populaires
Tutoriels populaires
Plus>
Recommandations populaires
Derniers téléchargements
Plus>