前言前端
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期动态代理实现程序功能的中统一处理业务逻辑的一种技术,比较常见的场景是:日志记录,错误捕获、性能监控等web
AOP的本质是经过代理对象来间接执行真实对象,在代理类中每每会添加装饰一些额外的业务代码,好比以下代码:编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class
RealA
{
public
virtual
string
Pro {
get
;
set
; }
public
virtual
void
ShowHello(
string
name)
{
Console.WriteLine($
"Hello!{name},Welcome!"
);
}
}
//调用:
var a =
new
RealA();
a.Pro =
"测试"
;
a.ShowHello(
"梦在旅途"
);
|
这段代码很简单,只是NEW一个对象,而后设置属性及调用方法,但若是我想在设置属性先后及调用方法先后或报错都能收集日志信息,该如何作呢?可能你们会想到,在设置属性及调用方法先后都加上记录日志的代码不就能够了,虽然这样是能够,但若是不少地方都要用到这个类的时候,那重复的代码是否太多了一些吧,因此咱们应该使用代理模式或装饰模式,将原有的真实类RealA委托给代理类ProxyRealA来执行,代理类中在设置属性及调用方法时,再添加记录日志的代码就能够了,这样能够保证代码的干净整洁,也便于代码的后期维护。(注意,在C#中若需被子类重写,父类必需是虚方法或虚属性virtual)数组
以下代码:app
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
|
class
ProxyRealA : RealA
{
public
override
string
Pro
{
get
{
return
base
.Pro;
}
set
{
ShowLog(
"设置Pro属性前日志信息"
);
base
.Pro = value;
ShowLog($
"设置Pro属性后日志信息:{value}"
);
}
}
public
override
void
ShowHello(
string
name)
{
try
{
ShowLog(
"ShowHello执行前日志信息"
);
base
.ShowHello(name);
ShowLog(
"ShowHello执行后日志信息"
);
}
catch
(Exception ex)
{
ShowLog($
"ShowHello执行出错日志信息:{ex.Message}"
);
}
}
private
void
ShowLog(
string
log)
{
Console.WriteLine($
"{DateTime.Now.ToString()}-{log}"
);
}
}
//调用:
var aa =
new
ProxyRealA();
aa.Pro =
"测试2"
;
aa.ShowHello(
"zuowenjun.cn"
);
|
这段代码一样很简单,就是ProxyRealA继承自RealA类,便可当作是ProxyRealA代理RealA,由ProxyRealA提供各类属性及方法调用。这样在ProxyRealA类内部属性及方法执行先后都有统一记录日志的代码,不论在哪里用这个RealA类,均可以直接用ProxyRealA类代替,由于里氏替换原则,父类能够被子类替换,并且后续若想更改日志记录代码方式,只须要在ProxyRealA中更改就好了,这样全部用到的ProxyRealA类的日志都会改变,是否是很爽。ide
上述执行结果以下图示:性能
以上经过定义代理类的方式可以实如今方法中统一进行各类执行点的拦截代码逻辑处理,拦截点(或者称为:横切面,切面点)通常主要为:执行前,执行后,发生错误,虽然解决了以前直接调用真实类RealA时,须要重复增长各类逻辑代码的问题,但随之而来的新问题又来了,那就是当一个系统中的类很是多的时候,若是咱们针对每一个类都定义一个代理类,那么系统的类的个数会成倍增长,并且不一样的代理类中可能某些拦截业务逻辑代码都是相同的,这种状况一样是不能容许的,那有没有什么好的办法呢?答案是确定的,如下是我结合网上资源及我的总结的以下几种常见的实现AOP的方式,各位能够参考学习。学习
第一种:静态织入,即:在编译时,就将各类涉及AOP拦截的代码注入到符合必定规则的类中,编译后的代码与咱们直接在RealA调用属性或方法先后增长代码是相同的,只是这个工做交由编译器来完成。测试
PostSharp:PostSharp的Aspect是使用Attribute实现的,咱们只需事先经过继承自OnMethodBoundaryAspect,而后重写几个常见的方法便可,如:OnEntry,OnExit等,最后只须要在须要进行AOP拦截的属性或方法上加上AOP拦截特性类便可。因为PostSharp是静态织入的,因此相比其它的经过反射或EMIT反射来讲效率是最高的,但PostSharp是收费版本的,并且网上的教程比较多,我就不在此重复说明了。优化
第二种:EMIT反射,即:经过Emit反射动态生成代理类,以下Castle.DynamicProxy
的AOP实现方式,代码也仍是比较简单的,效率相对第一种要慢一点,但对于普通的反射来讲又高一些,代码实现以下:
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
85
86
|
using
Castle.Core.Interceptor;
using
Castle.DynamicProxy;
using
NLog;
using
NLog.Config;
using
NLog.Win32.Targets;
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Threading.Tasks;
namespace
ConsoleApp
{
class
Program
{
static
void
Main(
string
[] args)
{
ProxyGenerator generator =
new
ProxyGenerator();
var test = generator.CreateClassProxy<TestA>(
new
TestInterceptor());
Console.WriteLine($
"GetResult:{test.GetResult(Console.ReadLine())}"
);
test.GetResult2(
"test"
);
Console.ReadKey();
}
}
public
class
TestInterceptor : StandardInterceptor
{
private
static
NLog.Logger logger;
protected
override
void
PreProceed(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name +
"执行前,入参:"
+
string
.Join(
","
, invocation.Arguments));
}
protected
override
void
PerformProceed(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name +
"执行中"
);
try
{
base
.PerformProceed(invocation);
}
catch
(Exception ex)
{
HandleException(ex);
}
}
protected
override
void
PostProceed(IInvocation invocation)
{
Console.WriteLine(invocation.Method.Name +
"执行后,返回值:"
+ invocation.ReturnValue);
}
private
void
HandleException(Exception ex)
{
if
(logger ==
null
)
{
LoggingConfiguration config =
new
LoggingConfiguration();
ColoredConsoleTarget consoleTarget =
new
ColoredConsoleTarget();
consoleTarget.Layout =
"${date:format=HH\\:MM\\:ss} ${logger} ${message}"
;
config.AddTarget(
"console"
, consoleTarget);
LoggingRule rule1 =
new
LoggingRule(
"*"
, LogLevel.Debug, consoleTarget);
config.LoggingRules.Add(rule1);
LogManager.Configuration = config;
logger = LogManager.GetCurrentClassLogger();
//new NLog.LogFactory().GetCurrentClassLogger();
}
logger.ErrorException(
"error"
,ex);
}
}
public
class
TestA
{
public
virtual
string
GetResult(
string
msg)
{
string
str = $
"{DateTime.Now.ToString("
yyyy-mm-dd HH:mm:ss
")}---{msg}"
;
return
str;
}
public
virtual
string
GetResult2(
string
msg)
{
throw
new
Exception(
"throw Exception!"
);
}
}
}
|
简要说明一下代码原理,先建立ProxyGenerator类实例,从名字就看得出来,是代理类生成器,而后实例化一个基于继承自StandardInterceptor的TestInterceptor,这个TestInterceptor是一个自定义的拦截器,最后经过generator.CreateClassProxy<TestA>(new TestInterceptor())
动态建立了一个继承自TestA的动态代理类,这个代理类只有在运行时才会生成的,后面就能够如代码所示,直接用动态代理类对象实例Test操做TestA的全部属性与方法,固然这里须要注意,若须要被动态代理类所代理并拦截,则父类的属性或方法必需是virtual,这点与我上面说的直接写一个代理类相同。
上述代码运行效果以下:
第三种:普通反射+利用Remoting的远程访问对象时的直实代理类来实现,代码以下,这个可能相比以上两种稍微复杂一点:
以上代码实现步骤说明:
1.这里定义的一个真实类AopClass必需继承自ContextBoundObject类,而ContextBoundObject类又直接继承自MarshalByRefObject类,代表该类是上下文绑定对象,容许在支持远程处理的应用程序中跨应用程序域边界访问对象,说白了就是能够获取这个真实类的全部信息,以即可以被生成动态代理。
2.定义继承自ProxyAttribute的代理特性标识类AopAttribute,以代表哪些类能够被代理,同时注意重写CreateInstance方法,在CreateInstance方法里实现经过委托与生成透明代理类的过程,realProxy.GetTransparentProxy()
很是重要,目的就是根据定义的AopProxy代理类获取生成透明代理类对象实例。
3.实现通用的AopProxy代理类,代理类必需继承自RealProxy类,在这个代理类里面重写Invoke方法,该方法是统一执行被代理的真实类的全部方法、属性、字段的出入口,咱们只须要在该方法中根据传入的IMessage进行判断并实现相应的拦截代码便可。
4.最后在须要进行Aop拦截的类上标注AopAttribute便可(注意:被标识的类必需是如第1条说明的继承自ContextBoundObject类),在实际调用的过程当中是感知不到任何的变化。且AopAttribute能够被子类继承,也就意味着全部子类均可以被代理并拦截。
如上代码运行效果以下:
这里顺便分享微软官方若是利用RealProxy类实现AOP的,详见地址:https://msdn.microsoft.com/zh-cn/library/dn574804.aspx
第四种:反射+ 经过定义统一的出入口,并运用一些特性实现AOP的效果,好比:常见的MVC、WEB API中的过滤器特性 ,我这里根据MVC的思路,实现了相似的MVC过滤器的AOP效果,只是中间用到了反射,可能性能不佳,但效果仍是成功实现了各类拦截,正如MVC同样,既支持过滤器特性,也支持Controller中的Action执行前,执行后,错误等方法实现拦截
实现思路以下:
A.过滤器及Controller特定方法拦截实现原理:
1.获取程序集中全部继承自Controller的类型;
2.根据Controller的名称找到第1步中的对应的Controller的类型:FindControllerType
3.根据找到的Controller类型及Action的名称找到对应的方法:FindAction
4.建立Controller类型的实例;
5.根据Action方法找到定义在方法上的全部过滤器特性(包含:执行前、执行后、错误)
6.执行Controller中的OnActionExecuting方法,随后执行执行前的过滤器特性列表,如:ActionExecutingFilter
7.执行Action方法,得到结果;
8.执行Controller中的OnActionExecuted方法,随后执行执行后的过滤器特性列表,如:ActionExecutedFilter
9.经过try catch在catch中执行Controller中的OnActionError方法,随后执行错误过滤器特性列表,如:ActionErrorFilter
10.最后返回结果;
B.实现执行路由配置效果原理:
1.增长可设置路由模板列表方法:AddExecRouteTemplate,在方法中验证controller、action,并获取模板中的占位符数组,最后保存到类全局对象中routeTemplates;
2.增长根据执行路由执行对应的Controller中的Action方法的效果:Run,在该方法中主要遍历全部路由模板,而后与实行执行的请求路由信息经过正则匹配,若匹配OK,并能正确找到Controller及Action,则说明正确,并最终统一调用:Process方法,执行A中的全部步骤最终返回结果。
须要说明该模拟MVC方案并无实现Action方法参数的的绑定功能,由于ModelBinding自己就是比较复杂的机制,因此这里只是为了搞清楚AOP的实现原理,故不做这方面的研究,你们若是有空能够实现,最终实现MVC不只是ASP.NET MVC,还能够是Console MVC,甚至是Winform MVC等。
如下是实现的所有代码,代码中我已进行了一些基本的优化,能够直接使用:
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
|
public
abstract
class
Controller
{
public
virtual
void
OnActionExecuting(MethodInfo action)
{
}
public
virtual
void
OnActionExecuted(MethodInfo action)
{
}
public
virtual
void
OnActionError(MethodInfo action, Exception ex)
{
}
}
public
abstract
class
FilterAttribute : Attribute
{
public
abstract
string
FilterType {
get
; }
public
abstract
void
Execute(Controller ctrller,
object
extData);
}
public
class
ActionExecutingFilter : FilterAttribute
{
public
override
string
FilterType =>
"BEFORE"
;
public
override
void
Execute(Controller ctrller,
object
extData)
{
Console.WriteLine($
"我是在{ctrller.GetType().Name}.ActionExecutingFilter中拦截发出的消息!-{DateTime.Now.ToString()}"
);
}
}
public
class
ActionExecutedFilter : FilterAttribute
{
public
override
string
FilterType =>
"AFTER"
;
public
override
void
Execute(Controller ctrller,
object
extData)
{
Console.WriteLine($
"我是在{ctrller.GetType().Name}.ActionExecutedFilter中拦截发出的消息!-{DateTime.Now.ToString()}"
);
}
}
public
class
ActionErrorFilter : FilterAttribute
{
public
override
string
FilterType =>
"EXCEPTION"
;
public
override
void
Execute(Controller ctrller,
object
extData)
{
Console.WriteLine($
"我是在{ctrller.GetType().Name}.ActionErrorFilter中拦截发出的消息!-{DateTime.Now.ToString()}-Error Msg:{(extData as Exception).Message}"
);
}
}
public
class
AppContext
{
private
static
readonly
Type ControllerType =
typeof
(Controller);
private
static
readonly
Dictionary<
string
, Type> matchedControllerTypes =
new
Dictionary<
string
, Type>();
private
static
readonly
Dictionary<
string
, MethodInfo> matchedControllerActions =
new
Dictionary<
string
, MethodInfo>();
private
Dictionary<
string
,
string
[]> routeTemplates =
new
Dictionary<
string
,
string
[]>();
public
void
AddExecRouteTemplate(
string
execRouteTemplate)
{
if
(!Regex.IsMatch(execRouteTemplate,
"{controller}"
, RegexOptions.IgnoreCase))
{
throw
new
ArgumentException(
"执行路由模板不正确,缺乏{controller}"
);
}
if
(!Regex.IsMatch(execRouteTemplate,
"{action}"
, RegexOptions.IgnoreCase))
{
throw
new
ArgumentException(
"执行路由模板不正确,缺乏{action}"
);
}
string
[] keys = Regex.Matches(execRouteTemplate,
@"(?<={)\w+(?=})"
, RegexOptions.IgnoreCase).Cast<Match>().Select(c => c.Value.ToLower()).ToArray();
routeTemplates.Add(execRouteTemplate,keys);
}
public
object
Run(
string
execRoute)
{
//{controller}/{action}/{id}
string
ctrller =
null
;
string
actionName =
null
;
ArrayList args =
null
;
Type controllerType =
null
;
bool
findResult =
false
;
foreach
(var r
in
routeTemplates)
{
string
[] keys = r.Value;
string
execRoutePattern = Regex.Replace(r.Key,
@"{(?<key>\w+)}"
, (m) =>
string
.Format(
@"(?<{0}>.[^/\\]+)"
, m.Groups[
"key"
].Value.ToLower()), RegexOptions.IgnoreCase);
args =
new
ArrayList();
if
(Regex.IsMatch(execRoute, execRoutePattern))
{
var match = Regex.Match(execRoute, execRoutePattern);
for
(
int
i = 0; i < keys.Length; i++)
{
if
(
"controller"
.Equals(keys[i], StringComparison.OrdinalIgnoreCase))
{
ctrller = match.Groups[
"controller"
].Value;
}
else
if
(
"action"
.Equals(keys[i], StringComparison.OrdinalIgnoreCase))
{
actionName = match.Groups[
"action"
].Value;
}
else
{
args.Add(match.Groups[keys[i]].Value);
}
}
if
((controllerType = FindControllerType(ctrller)) !=
null
&& FindAction(controllerType, actionName, args.ToArray()) !=
null
)
{
findResult =
true
;
break
;
}
}
}
if
(findResult)
{
return
Process(ctrller, actionName, args.ToArray());
}
else
{
throw
new
Exception($
"在已配置的路由模板列表中未找到与该执行路由相匹配的路由信息:{execRoute}"
);
}
}
public
object
Process(
string
ctrller,
string
actionName,
params
object
[] args)
{
Type matchedControllerType = FindControllerType(ctrller);
if
(matchedControllerType ==
null
)
{
throw
new
ArgumentException($
"未找到类型为{ctrller}的Controller类型"
);
}
object
execResult =
null
;
if
(matchedControllerType !=
null
)
{
var matchedController = (Controller)Activator.CreateInstance(matchedControllerType);
MethodInfo action = FindAction(matchedControllerType, actionName, args);
if
(action ==
null
)
{
throw
new
ArgumentException($
"在{matchedControllerType.FullName}中未找到与方法名:{actionName}及参数个数:{args.Count()}相匹配的方法"
);
}
var filters = action.GetCustomAttributes<FilterAttribute>(
true
);
List<FilterAttribute> execBeforeFilters =
new
List<FilterAttribute>();
List<FilterAttribute> execAfterFilters =
new
List<FilterAttribute>();
List<FilterAttribute> exceptionFilters =
new
List<FilterAttribute>();
if
(filters !=
null
&& filters.Count() > 0)
{
execBeforeFilters = filters.Where(f => f.FilterType ==
"BEFORE"
).ToList();
execAfterFilters = filters.Where(f => f.FilterType ==
"AFTER"
).ToList();
exceptionFilters = filters.Where(f => f.FilterType ==
"EXCEPTION"
).ToList();
}
try
{
matchedController.OnActionExecuting(action);
if
(execBeforeFilters !=
null
&& execBeforeFilters.Count > 0)
{
execBeforeFilters.ForEach(f => f.Execute(matchedController,
null
));
}
var mParams = action.GetParameters();
object
[] newArgs =
new
object
[args.Length];
for
(
int
i = 0; i < mParams.Length; i++)
{
newArgs[i] = Convert.ChangeType(args[i], mParams[i].ParameterType);
}
execResult = action.Invoke(matchedController, newArgs);
matchedController.OnActionExecuted(action);
if
(execBeforeFilters !=
null
&& execBeforeFilters.Count > 0)
{
execAfterFilters.ForEach(f => f.Execute(matchedController,
null
));
}
}
catch
(Exception ex)
{
matchedController.OnActionError(action, ex);
if
(exceptionFilters !=
null
&& exceptionFilters.Count > 0)
{
exceptionFilters.ForEach(f => f.Execute(matchedController, ex));
}
}
}
return
execResult;
}
private
Type FindControllerType(
string
ctrller)
{
Type matchedControllerType =
null
;
if
(!matchedControllerTypes.ContainsKey(ctrller))
{
var assy = Assembly.GetAssembly(
typeof
(Controller));
foreach
(var m
in
assy.GetModules(
false
))
{
foreach
(var t
in
m.GetTypes())
{
if
(ControllerType.IsAssignableFrom(t) && !t.IsAbstract)
{
if
(t.Name.Equals(ctrller, StringComparison.OrdinalIgnoreCase) || t.Name.Equals($
"{ctrller}Controller"
, StringComparison.OrdinalIgnoreCase))
{
matchedControllerType = t;
matchedControllerTypes[ctrller] = matchedControllerType;
break
;
}
}
}
}
}
else
{
matchedControllerType = matchedControllerTypes[ctrller];
}
return
matchedControllerType;
}
private
MethodInfo FindAction(Type matchedControllerType,
string
actionName,
object
[] args)
{
string
ctrlerWithActionKey = $
"{matchedControllerType.FullName}.{actionName}"
;
MethodInfo action =
null
;
if
(!matchedControllerActions.ContainsKey(ctrlerWithActionKey))
{
if
(args ==
null
) args =
new
object
[0];
foreach
(var m
in
matchedControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public))
{
if
(m.Name.Equals(actionName, StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == args.Length)
{
action = m;
matchedControllerActions[ctrlerWithActionKey] = action;
break
;
}
}
}
else
{
action = matchedControllerActions[ctrlerWithActionKey];
}
return
action;
}
}
|
使用前,先定义一个继承自Controller的类,如:TestController,并重写相应的方法,或在指定的方法上加上所需的过滤器特性,以下代码所示:
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
|
public
class
TestController : Controller
{
public
override
void
OnActionExecuting(MethodInfo action)
{
Console.WriteLine($
"{action.Name}执行前,OnActionExecuting---{DateTime.Now.ToString()}"
);
}
public
override
void
OnActionExecuted(MethodInfo action)
{
Console.WriteLine($
"{action.Name}执行后,OnActionExecuted--{DateTime.Now.ToString()}"
);
}
public
override
void
OnActionError(MethodInfo action, Exception ex)
{
Console.WriteLine($
"{action.Name}执行,OnActionError--{DateTime.Now.ToString()}:{ex.Message}"
);
}
[ActionExecutingFilter]
[ActionExecutedFilter]
public
string
HelloWorld(
string
name)
{
return
($
"Hello World!->{name}"
);
}
[ActionExecutingFilter]
[ActionExecutedFilter]
[ActionErrorFilter]
public
string
TestError(
string
name)
{
throw
new
Exception(
"这是测试抛出的错误信息!"
);
}
[ActionExecutingFilter]
[ActionExecutedFilter]
public
int
Add(
int
a,
int
b)
{
return
a + b;
}
}
|
最后前端实际调用就很是简单了,代码以下:
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
|
class
MVCProgram
{
static
void
Main(
string
[] args)
{
try
{
var appContext =
new
AppContext();
object
rs = appContext.Process(
"Test"
,
"HelloWorld"
,
"梦在旅途"
);
Console.WriteLine($
"Process执行的结果1:{rs}"
);
Console.WriteLine(
"="
.PadRight(50,
'='
));
appContext.AddExecRouteTemplate(
"{controller}/{action}/{name}"
);
appContext.AddExecRouteTemplate(
"{action}/{controller}/{name}"
);
object
result1 = appContext.Run(
"HelloWorld/Test/梦在旅途-zuowenjun.cn"
);
Console.WriteLine($
"执行的结果1:{result1}"
);
Console.WriteLine(
"="
.PadRight(50,
'='
));
object
result2 = appContext.Run(
"Test/HelloWorld/梦在旅途-zuowenjun.cn"
);
Console.WriteLine($
"执行的结果2:{result2}"
);
Console.WriteLine(
"="
.PadRight(50,
'='
));
appContext.AddExecRouteTemplate(
"{action}/{controller}/{a}/{b}"
);
object
result3 = appContext.Run(
"Add/Test/500/20"
);
Console.WriteLine($
"执行的结果3:{result3}"
);
object
result4 = appContext.Run(
"Test/TestError/梦在旅途-zuowenjun.cn"
);
Console.WriteLine($
"执行的结果4:{result4}"
);
}
catch
(Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($
"发生错误:{ex.Message}"
);
Console.ResetColor();
}
Console.ReadKey();
}
}
|
能够看到,与ASP.NET MVC有点相似,只是ASP.NET MVC是经过URL访问,而这里是经过AppContext.Run 执行路由URL 或Process方法,直接指定Controller、Action、参数来执行。
经过以上调用代码能够看出路由配置仍是比较灵活的,固然参数配置除外。若是你们有更好的想法也能够在下方评论交流,谢谢!
MVC代码执行效果以下:
总结
以上就是这篇文章的所有内容了,但愿本文的内容对你们的学习或者工做具备必定的参考学习价值,若是有疑问你们能够留言交流,谢谢你们对脚本之家的支持。