1 /*
2  * Hunt - A high-level D Programming Language Web framework that encourages rapid development and clean, pragmatic design.
3  *
4  * Copyright (C) 2015-2019, HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.framework.controller.Controller;
13 
14 import hunt.framework.application.Application;
15 import hunt.framework.auth;
16 import hunt.framework.breadcrumb.BreadcrumbsManager;
17 import hunt.framework.controller.RestController;
18 import hunt.framework.middleware.AuthMiddleware;
19 import hunt.framework.middleware.Middleware;
20 import hunt.framework.middleware.MiddlewareInfo;
21 import hunt.framework.middleware.MiddlewareInterface;
22 
23 import hunt.framework.http.Request;
24 import hunt.framework.http.Form;
25 import hunt.framework.i18n.I18n;
26 import hunt.framework.provider;
27 import hunt.framework.Simplify;
28 import hunt.framework.view;
29 
30 public import hunt.framework.http.Response;
31 public import hunt.http.server;
32 public import hunt.http.routing;
33 import hunt.http.HttpConnection;
34 
35 import hunt.cache;
36 import hunt.entity.EntityManagerFactory;
37 import hunt.logging.Logger;
38 import hunt.redis.Redis;
39 import hunt.redis.RedisPool;
40 import hunt.validation;
41 
42 import poodinis;
43 
44 import core.memory;
45 import core.thread;
46 
47 import std.algorithm;
48 import std.exception;
49 import std.string;
50 import std.traits;
51 import std.variant;
52 import std.meta : Filter;
53 
54 struct Action {
55 }
56 
57 private enum string TempVarName = "__var";
58 // private enum string IndentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";  // 16 tabs
59 private enum string IndentString = "                                ";  // 32 spaces
60 
61 string indent(size_t number) {
62     assert(number>0 && IndentString.length, "Out of range");
63     return IndentString[0..number];
64 }
65 
66 class ControllerBase(T) : Controller {
67     // mixin MakeController;
68     mixin HuntDynamicCallFun!(T, moduleName!T);
69 }
70 
71 /**
72  * 
73  */
74 abstract class Controller
75 {
76     private Request _request;
77     private Response _response;
78 
79     private MiddlewareInfo[] _allowedMiddlewares;
80     private MiddlewareInfo[] _skippedMiddlewares;
81 
82     protected
83     {
84         RoutingContext _routingContext;
85         View _view;
86         ///called before all actions
87         MiddlewareInterface[string] middlewares;
88     }
89 
90     this() {
91     }
92 
93     RoutingContext routingContext() {
94         if(_routingContext is null) {
95             throw new Exception("Can't call this method in the constructor.");
96         }
97         return _routingContext;
98     }
99 
100     Request request() {
101         return createRequest(false);
102     }
103 
104     protected Request createRequest(bool isRestful = false) {
105         if(_request is null) {
106             RoutingContext context = routingContext();
107 
108             Variant itemAtt = context.getAttribute(RouterContex.stringof);
109             RouterContex routeContex;
110             if(itemAtt.hasValue()) {
111                 routeContex = cast(RouterContex)itemAtt.get!(Object);
112             }
113 
114             HttpConnection httpConnection = context.httpConnection();
115 
116             _request = new Request(context.getRequest(), 
117                                     httpConnection.getRemoteAddress(),
118                                     routeContex);
119 
120             _request.isRestful = isRestful;
121         }
122         return _request;
123     }
124 
125     final @property Response response() {
126         if(_response is null) {
127             _response = new Response(routingContext().getResponse());
128         }
129         return _response;
130     }
131 
132     // reset to a new response
133     @property void response(Response r) {
134         assert(r !is null, "The response can't be null");
135         _response = r;
136         routingContext().response = r.httpResponse;
137     }
138 
139     /**
140      * Handle the auth status
141      */
142     Auth auth() {
143         return this.request().auth();
144     }
145 
146     /**
147      * Get the currently authenticated user.
148      */
149     Identity user() {
150         return this.request().auth().user();
151     }
152 
153     @property View view()
154     {
155         if (_view is null)
156         {
157             Request req = this.request();
158             _view = serviceContainer.resolve!View();
159             _view.setRouteGroup(routingContext().groupName());
160             _view.setLocale(req.locale());
161             _view.env().request = req;
162             
163             // _view.assign("input", req.input());
164         }
165 
166         return _view;
167     }
168 
169     private RouteConfigManager routeManager() {
170         return serviceContainer.resolve!(RouteConfigManager);
171     }
172 
173 
174     /// called before action  return true is continue false is finish
175     bool before()
176     {
177         return true;
178     }
179 
180     /// called after action  return true is continue false is finish
181     bool after()
182     {
183         return true;
184     }
185 
186     ///add middleware
187     ///return true is ok, the named middleware is already exist return false
188     bool addMiddleware(MiddlewareInterface m)
189     {
190         if(m is null || this.middlewares.get(m.name(), null) !is null)
191         {
192             return false;
193         }
194 
195         this.middlewares[m.name()]= m;
196         return true;
197     }
198 
199     /**
200      * 
201      */
202     void addAcceptedMiddleware(string fullName, string actionName, string controllerName, string moduleName) {
203         MiddlewareInfo info = new MiddlewareInfo(fullName, actionName, controllerName, moduleName);
204         _allowedMiddlewares ~= info;
205     }
206     
207     /**
208      * 
209      */
210     void addSkippedMiddleware(string fullName, string actionName, string controllerName, string moduleName) {
211         MiddlewareInfo info = new MiddlewareInfo(fullName, actionName, controllerName, moduleName);
212         _skippedMiddlewares ~= info;
213     }
214 
215     // All the middlewares defined in the route group
216     protected MiddlewareInterface[] getAcceptedMiddlewaresInRouteGroup(string routeGroup) {
217         MiddlewareInterface[] result;
218 
219         TypeInfo_Class[] routeMiddlewares;
220         routeMiddlewares = routeManager().group(routeGroup).allowedMiddlewares();
221         foreach(TypeInfo_Class info; routeMiddlewares) {
222             version(HUNT_AUTH_DEBUG) tracef("routeGroup: %s, fullName: %s", routeGroup, info.name);
223 
224             MiddlewareInterface middleware = cast(MiddlewareInterface)info.create();
225             if(middleware is null) {
226                 warningf("%s is not a MiddlewareInterface", info.name);
227             } else {              
228                 result ~= middleware;
229             }
230         }
231 
232         return result;
233     }
234 
235     // All the middlewares defined in the route item
236     protected MiddlewareInterface[] getAcceptedMiddlewaresInRouteItem(string routeGroup, string actionId) {
237         MiddlewareInterface[] result;
238         TypeInfo_Class[] routeMiddlewares;
239 
240         RouteItem routeItem = routeManager().get(routeGroup, actionId);
241             if(routeItem !is null) {
242             routeMiddlewares = routeItem.allowedMiddlewares();
243             foreach(TypeInfo_Class info; routeMiddlewares) {
244                 warningf("actionId: %s, fullName: %s", actionId, info.name);
245 
246                 MiddlewareInterface middleware = cast(MiddlewareInterface)info.create();
247                 if(middleware is null) {
248                     warningf("%s is not a MiddlewareInterface", info.name);
249                 } else {
250                     result ~= middleware;
251                 }
252             } 
253         }
254 
255         return result;
256     }
257 
258     // All the middlewares defined this Controller's action
259     protected MiddlewareInterface[] getAcceptedMiddlewaresInController(string actionName) {
260         MiddlewareInterface[] result;
261 
262         auto middlewares = _allowedMiddlewares.filter!( m => m.action == actionName);
263         // auto middlewares = _allowedMiddlewares.filter!( (m) { 
264         //     trace(m.action, " == ", name);
265         //     return m.action == name;
266         // });
267 
268         foreach(MiddlewareInfo info; middlewares) {
269             // warningf("fullName: %s, action: %s", info.fullName, info.action);
270 
271             MiddlewareInterface middleware = cast(MiddlewareInterface)Object.factory(info.fullName);
272             if(middleware is null) {
273                 warningf("%s is not a MiddlewareInterface", info.fullName);
274             } else {             
275                 result ~= middleware;
276             }
277         }
278 
279         return result;
280     }
281 
282     // All the middlewares defined in the route group
283     protected bool isSkippedMiddlewareInRouteGroup(string fullName, string routeGroup) {
284         TypeInfo_Class[] routeMiddlewares = routeManager().group(routeGroup).skippedMiddlewares();
285         foreach(TypeInfo_Class typeInfo; routeMiddlewares) {
286             if(typeInfo.name == fullName) return true;
287         }
288 
289         return false;
290     }
291     
292     // All the middlewares defined in the route item
293     protected bool isSkippedMiddlewareInRouteItem(string fullName, string routeGroup, string actionId) {
294         RouteItem routeItem = routeManager().getRoute(routeGroup, actionId);
295         if(routeItem !is null) {
296             TypeInfo_Class[] routeMiddlewares = routeItem.skippedMiddlewares();
297             foreach(TypeInfo_Class typeInfo; routeMiddlewares) {
298                 if(typeInfo.name == fullName) return true;
299             }
300         }
301 
302         return false;
303     }
304 
305     // All the middlewares defined this Controller's action
306     protected bool isSkippedMiddlewareInControllerAction(string actionName, string middlewareName) {
307         bool r = _skippedMiddlewares.canFind!(m => m.fullName == middlewareName && m.action == actionName);
308         return r;
309     }
310 
311     // get all middleware
312     MiddlewareInterface[string] getMiddlewares()
313     {
314         return this.middlewares;
315     }
316 
317     protected final Response handleMiddlewares(string actionName) {
318         Request req = this.request();
319         string actionId = req.actionId();
320         string routeGroup = req.routeGroup();
321         
322         version (HUNT_DEBUG) {
323             infof("middlware: routeGroup=%s, path=%s, method=%s, actionId=%s, actionName=%s", 
324                routeGroup, req.path(),  req.method, actionId, actionName);
325         }
326 
327         /////////////
328         // Checking all the allowed middlewares.
329         /////////////
330 
331         // Allowed middlewares in Controller's Action
332         MiddlewareInterface[] allowedMiddlewares = getAcceptedMiddlewaresInController(actionName);
333         
334         // Allowed middlewares in RouteItem
335         allowedMiddlewares ~= getAcceptedMiddlewaresInRouteItem(routeGroup, actionId);
336 
337         foreach(MiddlewareInterface m; allowedMiddlewares) {
338             string name = m.name();
339             version (HUNT_DEBUG) tracef("The %s is processing ...", name);
340 
341             auto response = m.onProcess(req, this.response);
342             if (response is null) {
343                 continue;
344             }
345 
346             version (HUNT_DEBUG) infof("The access is blocked by %s.", name);
347             return response;
348         }
349         
350         // Allowed middlewares in RouteGroup
351         allowedMiddlewares = getAcceptedMiddlewaresInRouteGroup(routeGroup);
352         foreach(MiddlewareInterface m; allowedMiddlewares) {
353             string name = m.name();
354             version (HUNT_DEBUG) tracef("The %s is processing ...", name);
355 
356             if(isSkippedMiddlewareInControllerAction(actionName, name)) {
357                 version (HUNT_DEBUG) infof("A middleware [%s] is skipped ...", name);
358                 return null;
359             }
360 
361             if(isSkippedMiddlewareInRouteItem(name, routeGroup, actionId)) {
362                 version (HUNT_DEBUG) infof("A middleware [%s] is skipped ...", name);
363                 return null;
364             }
365 
366             auto response = m.onProcess(req, this.response);
367             if (response is null) {
368                 continue;
369             }
370 
371             version (HUNT_DEBUG) infof("The access is blocked by %s.", m.name);
372             return response;
373         }
374 
375         /////////////
376         // Checking all the directly registed middlewares in Controller.
377         /////////////
378 
379         foreach (m; middlewares) {
380             string name = m.name();
381             version (HUNT_DEBUG) tracef("The %s is processing ...", name);
382             if(isSkippedMiddlewareInControllerAction(actionName, name)) {
383                 version (HUNT_DEBUG) infof("A middleware [%s] is skipped ...", name);
384                 return null;
385             }
386 
387             if(isSkippedMiddlewareInRouteItem(name, routeGroup, actionId)) {
388                 version (HUNT_DEBUG) infof("A middleware [%s] is skipped ...", name);
389                 return null;
390             }
391 
392             auto response = m.onProcess(req, this.response);
393             if (response is null) {
394                 continue;
395             }
396 
397             version (HUNT_DEBUG) tracef("The access is blocked by %s.", name);
398             return response;
399         }
400 
401         return null;
402     }
403 
404     Response processResponse(Response res)
405     {
406         // TODO: Tasks pending completion -@zhangxueping at 2020-01-06T14:01:43+08:00
407         // 
408         // have ResponseHandler binding?
409         // if (res.httpResponse() is null)
410         // {
411         //     res.setHttpResponse(request.responseHandler());
412         // }
413 
414         return res;
415     }
416 
417     ConstraintValidatorContext validate() {
418         if(_context is null) {
419             // assert(!_currentActionName.empty(), "No currentActionName found!");
420             _context = new DefaultConstraintValidatorContext();
421 
422             auto itemPtr = _currentActionName in _actionValidators;
423             // assert(itemPtr !is null, format("No handler found for action: %s!", _currentActionName));
424             if(itemPtr is null) {
425                 warning(format("No validator found for action: %s.", _currentActionName));
426             } else {
427                 try {
428                     (*itemPtr)(_context);  
429                 } catch(Exception ex) {
430                     warning(ex.msg);
431                     version(HUNT_DEBUG) warning(ex);
432                 }
433             }          
434         }
435 
436         return _context;
437     }
438     private ConstraintValidatorContext _context;
439     protected string _currentActionName;
440     protected QueryParameterValidator[string] _actionValidators;
441 
442     protected void raiseError(Response response) {
443         this.response = onError(response);
444     }
445 
446     protected Response onError(Response response) {
447         return response;
448     }
449 
450     protected void done() {
451         Request req = request();
452         Response resp = response();
453         HttpSession session = req.session(false);
454         if (session !is null ) // && session.isNewSession()
455         {
456             resp.withCookie(new Cookie(DefaultSessionIdName, session.getId(), session.getMaxInactiveInterval(), 
457                     "/", null, false, false));
458 
459             // session.reflash();
460             session.save();
461         }
462         req.flush(); // assure the sessiondata flushed;
463 
464         resp.header("Date", date("Y-m-d H:i:s"));
465         resp.header(HttpHeader.X_POWERED_BY, HUNT_X_POWERED_BY);
466         resp.header(HttpHeader.SERVER, HUNT_FRAMEWORK_SERVER);
467 
468         if(!resp.getFields().contains(HttpHeader.CONTENT_TYPE)) {
469             resp.header(HttpHeader.CONTENT_TYPE, MimeType.TEXT_HTML_VALUE);
470         }
471 
472         handleCors();
473         handleAuthResponse();
474     }
475 
476     protected void handleCors() {
477         /**
478         CORS support
479         http://www.cnblogs.com/feihong84/p/5678895.html
480         https://stackoverflow.com/questions/10093053/add-header-in-ajax-request-with-jquery
481         */
482         ApplicationConfig.HttpConf httpConf = config().http;
483         if(httpConf.enableCors) {
484             response.setHeader("Access-Control-Allow-Origin", httpConf.allowOrigin);
485             response.setHeader("Access-Control-Allow-Methods", httpConf.allowMethods);
486             response.setHeader("Access-Control-Allow-Headers", httpConf.allowHeaders);
487         }        
488     }
489 
490     protected void handleAuthResponse() {
491         Request req = request();
492         Auth auth = req.auth();
493 
494         version(HUNT_AUTH_DEBUG) {
495             tracef("Path: %s, isAuthEnabled: %s", 
496                 req.path,  auth.isEnabled());
497         }
498 
499         if(!auth.isEnabled()) 
500             return;
501 
502         AuthenticationScheme authScheme = auth.scheme();
503         string tokenCookieName = auth.tokenCookieName;
504         version(HUNT_AUTH_DEBUG) {
505             warningf("tokenCookieName: %s, authScheme: %s, isAuthenticated: %s, isLogout: %s", 
506                 tokenCookieName, authScheme, auth.user().isAuthenticated, auth.isLogout());
507         }
508 
509         Cookie tokenCookie;
510 
511         if(auth.canRememberMe() || auth.isTokenRefreshed()) {
512             ApplicationConfig appConfig = app().config();
513             int tokenExpiration = appConfig.auth.tokenExpiration;
514 
515             if(authScheme != AuthenticationScheme.None) {
516                 string authToken = auth.token();
517                 tokenCookie = new Cookie(tokenCookieName, authToken, tokenExpiration);
518                 auth.touchSession();
519             } 
520 
521         } else if(auth.isLogout()) {
522             if(authScheme != AuthenticationScheme.None) {
523                 tokenCookie = new Cookie(tokenCookieName, "", 0);
524             }
525         } else if(authScheme != AuthenticationScheme.None) {
526             ApplicationConfig appConfig = app().config();
527             int tokenExpiration = appConfig.auth.tokenExpiration;
528             string authToken = auth.token();
529             if(authToken.empty()) {
530                 version(HUNT_AUTH_DEBUG) warning("The auth token is empty!");
531             } else {
532                 tokenCookie = new Cookie(tokenCookieName, authToken, tokenExpiration);
533                 auth.touchSession();
534             }
535         }
536 
537         if(tokenCookie !is null) {
538             response().withCookie(tokenCookie);
539         }
540     }
541 
542     void dispose() {
543         version(HUNT_FM_DEBUG) trace("Do nothing");
544     }
545 }
546 
547 mixin template MakeController(string moduleName = __MODULE__)
548 {
549     mixin HuntDynamicCallFun!(typeof(this), moduleName);
550 }
551 
552 mixin template HuntDynamicCallFun(T, string moduleName) // if(is(T : Controller))
553 {
554 public:
555 
556     // Middleware
557     // pragma(msg, handleMiddlewareAnnotation!(T, moduleName));
558 
559     mixin(handleMiddlewareAnnotation!(T, moduleName));
560 
561     // Actions
562     // enum allActions = __createCallActionMethod!(T, moduleName);
563     // version (HUNT_DEBUG) 
564     // pragma(msg, __createCallActionMethod!(T, moduleName));
565 
566     mixin(__createCallActionMethod!(T, moduleName));
567     
568     shared static this()
569     {
570         enum routemap = __createRouteMap!(T, moduleName);
571         // pragma(msg, routemap);
572         mixin(routemap);
573     }
574 }
575 
576 private
577 {
578     // Predefined characteristic name for a default Action method.
579     enum actionName = "Action";
580     enum actionNameLength = actionName.length;
581 
582     bool isActionMember(string name)
583     {
584         return name.length > actionNameLength && name[$ - actionNameLength .. $] == actionName;
585     }
586 }
587 
588 
589 /// 
590 string handleMiddlewareAnnotation(T, string moduleName)() {
591     import std.traits;
592     import std.format;
593     import std.string;
594     import std.conv;
595     import hunt.framework.middleware.MiddlewareInterface;
596 
597     string str = `
598     void initializeMiddlewares() {
599     `;
600     
601     foreach (memberName; __traits(allMembers, T)) {
602         alias currentMember = __traits(getMember, T, memberName);
603         enum _isActionMember = isActionMember(memberName);
604 
605         static if(isFunction!(currentMember)) {
606 
607             static if (hasUDA!(currentMember, Action) || _isActionMember) {
608                 static if(hasUDA!(currentMember, Middleware)) {
609                     enum middlewareUDAs = getUDAs!(currentMember, Middleware);
610 
611                     foreach(uda; middlewareUDAs) {
612                         foreach(middlewareName; uda.names) {
613                             str ~= indent(4) ~ generateAcceptedMiddleware(middlewareName, memberName, T.stringof, moduleName);
614                             // str ~= indent(4) ~ format(`this.addAcceptedMiddleware("%s", "%s", "%s", "%s");`, 
615                             //     middlewareName, memberName, T.stringof, moduleName) ~ "\n";
616                         }
617                     }
618                 } 
619                 
620                 static if(hasUDA!(currentMember, WithoutMiddleware)) {
621                     enum skippedMiddlewareUDAs = getUDAs!(currentMember, WithoutMiddleware);
622                     foreach(uda; skippedMiddlewareUDAs) {
623                         foreach(middlewareName; uda.names) {
624                             str ~= indent(4) ~ generateAddSkippedMiddleware(middlewareName, memberName, T.stringof, moduleName);
625                             // str ~= indent(4) ~ format(`this.addSkippedMiddleware("%s", "%s", "%s", "%s");`, 
626                             //     middlewareName, memberName, T.stringof, moduleName) ~ "\n";
627                         }
628                     }
629                 }
630             }
631         }
632     }
633     
634     str ~= `
635     }    
636     `;
637 
638     return str;
639 }
640 
641 private string generateAcceptedMiddleware(string name, string actionName, string controllerName, string moduleName) {
642     string str;
643 
644     str = `
645         try {
646             TypeInfo_Class typeInfo = MiddlewareInterface.get("%s");
647             string fullName = typeInfo.name;
648             this.addAcceptedMiddleware(fullName, "%s", "%s", "%s");
649         } catch(Exception ex) {
650             warning(ex.msg);
651         }
652     `;
653 
654     str = format(str, name, actionName, controllerName, moduleName);
655     return str;
656 }
657 
658 private string generateAddSkippedMiddleware(string name, string actionName, string controllerName, string moduleName) {
659     string str;
660 
661     str = `
662         try {
663             TypeInfo_Class typeInfo = MiddlewareInterface.get("%s");
664             string fullName = typeInfo.name;
665             this.addSkippedMiddleware(fullName, "%s", "%s", "%s");
666         } catch(Exception ex) {
667             warning(ex.msg);
668         }
669     `;
670 
671     str = format(str, name, actionName, controllerName, moduleName);
672     return str;
673 }
674 
675 string __createCallActionMethod(T, string moduleName)()
676 {
677     import std.traits;
678     import std.format;
679     import std.string;
680     import std.conv;
681     
682 
683     string str = `
684         import hunt.http.server.HttpServerRequest;
685         import hunt.http.server.HttpServerResponse;
686         import hunt.http.routing.RoutingContext;
687         import hunt.http.HttpBody;
688         import hunt.logging;
689         import hunt.validation.ConstraintValidatorContext;
690         import hunt.framework.middleware.MiddlewareInterface;
691         import std.demangle;
692 
693         void callActionMethod(string methodName, RoutingContext context) {
694             _routingContext = context;
695             HttpBody rb;
696             version (HUNT_FM_DEBUG) logDebug("methodName=", methodName);
697             import std.conv;
698 
699             switch(methodName){
700     `;
701 
702     foreach (memberName; __traits(allMembers, T))
703     {
704         // TODO: Tasks pending completion -@zhangxueping at 2019-09-24T11:47:45+08:00
705         // Can't detect the error: void test(error);
706         // pragma(msg, "memberName: ", memberName);
707         static if (is(typeof(__traits(getMember, T, memberName)) == function))
708         {
709             // pragma(msg, "got: ", memberName);
710 
711             enum _isActionMember = isActionMember(memberName);
712             static foreach (currentMethod; __traits(getOverloads, T, memberName))
713             {
714                 // alias RT = ReturnType!(t);
715 
716                 //alias pars = ParameterTypeTuple!(t);
717                 static if (hasUDA!(currentMethod, Action) || _isActionMember) {
718                     str ~= indent(2) ~ "case \"" ~ memberName ~ "\": {\n";
719                     str ~= indent(4) ~ "_currentActionName = \"" ~ currentMethod.mangleof ~ "\";";
720 
721                     // middleware
722                     str ~= `auto middleResponse = this.handleMiddlewares("`~ memberName ~ `");`;
723 
724                     //before
725                     str ~= q{
726                         if (middleResponse !is null) {
727                             // _routingContext.response = response.httpResponse;
728                             response = middleResponse;
729                             return;
730                         }
731 
732                         if (!this.before()) {
733                             // _routingContext.response = response.httpResponse;
734                             // response = middleResponse;
735                             return;
736                         }
737                     };
738 
739                     // Action parameters
740                     auto params = ParameterIdentifierTuple!currentMethod;
741                     string paramString = "";
742 
743                     static if (params.length > 0) {
744                         import std.conv : to;
745 
746                         string varName = "";
747                         alias paramsType = Parameters!currentMethod;
748                         alias paramsDefaults = ParameterDefaults!currentMethod;
749 
750                         static foreach (int i; 0..params.length)
751                         {{
752                             varName = TempVarName ~ i.to!string;
753                             enum currentParamType = paramsType[i].stringof;
754                             
755                             alias paramsUDAs = ParameterUDAs!(currentMethod, AliasField, i);
756                             static if(paramsUDAs.length > 0) {
757                                 enum actualParameter = paramsUDAs[0].name;
758                             } else {
759                                 alias actualParameter = params[i];
760                             }
761 
762                             static if (is(paramsType[i] == string)) {
763                                 static if(is(paramsDefaults[i] == void)) {
764                                     str ~= indent(3) ~ "string " ~ varName ~ " = request.get(\"" ~ actualParameter ~ "\");\n";
765                                 } else {
766                                     str ~= indent(3) ~ "string " ~ varName ~ " = request.get(\"" ~ actualParameter ~ 
767                                         "\", " ~ paramsDefaults[i].stringof ~ ");\n";
768                                 }
769                             } else static if(is(paramsType[i] : Form)) {
770                                 str ~= indent(3) ~ "auto " ~ varName ~ " = request.bindForm!" ~ currentParamType ~ "();\n";
771                             } else {
772                                 static if(is(paramsDefaults[i] == void)) {
773                                     str ~= indent(3) ~ "auto " ~ varName ~ " = request.get!(" ~ 
774                                             currentParamType ~ ")(\"" ~ actualParameter ~ "\");\n";
775                                 } else {
776                                     str ~= indent(3) ~ "auto " ~ varName ~ " = request.get!(" ~ 
777                                             currentParamType ~ ")(\"" ~ actualParameter ~ 
778                                             "\", " ~ paramsDefaults[i].stringof ~ ");\n";
779                                 }
780                             }
781 
782                             paramString ~= i == 0 ? varName : ", " ~ varName;
783                             // varName = "";
784                         }}
785                     }
786 
787                     // Parameters validation
788                     // https://forum.dlang.org/post/bbgwqvvausncrkukzpui@forum.dlang.org
789                     str ~= indent(3) ~ `_actionValidators["` ~ currentMethod.mangleof ~ 
790                         `"] = (ConstraintValidatorContext context) {` ~ "\n";
791 
792                     static if(is(typeof(currentMethod) allParams == __parameters)) {
793                         str ~= indent(4) ~ "version(HUNT_DEBUG) info(`Validating in " ~  memberName ~ 
794                             ", the prototype is " ~ typeof(currentMethod).stringof ~ ". `); " ~ "\n";
795                         // str ~= indent(4) ~ `version(HUNT_DEBUG) infof("Validating in %s", demangle(_currentActionName)); ` ~ "\n";                        
796 
797                         static foreach(i, _; allParams) {{
798                             alias thisParameter = allParams[i .. i + 1]; 
799                             alias udas =  __traits(getAttributes, thisParameter);
800                             // enum ident = __traits(identifier, thisParameter);
801                             
802                             alias paramsUDAs = Filter!(isDesiredUDA!AliasField, udas); 
803                             static if(paramsUDAs.length > 0) {
804                                 enum actualParameter = paramsUDAs[0].name;
805                             } else {
806                                 enum actualParameter = __traits(identifier, thisParameter);
807                             }
808 
809                             str ~= "\n" ~ makeParameterValidation!(TempVarName ~ i.to!string, actualParameter, 
810                                 thisParameter, udas) ~ "\n"; 
811                          }}
812                     }
813 
814                     str ~= indent(3) ~ "};\n";
815 
816                     // Call the Action
817                     static if (is(ReturnType!currentMethod == void)) {
818                         str ~= "\t\tthis." ~ memberName ~ "(" ~ paramString ~ ");\n";
819                     } else {
820                         str ~= "\t\t" ~ ReturnType!currentMethod.stringof ~ " result = this." ~ 
821                                 memberName ~ "(" ~ paramString ~ ");\n";
822 
823                         static if (is(ReturnType!currentMethod : Response)) {
824                             str ~= "\t\t this.response = result;\n";
825                         } else {
826                             static if(is(T : RestController)) {
827                                 str ~="\t\tthis.response.setRestContent(result);";
828                             } else {
829                                 str ~="\t\tthis.response.setContent(result);";
830                             }
831                         }
832                     }
833 
834                     static if(hasUDA!(currentMethod, Action) || _isActionMember) {
835                         str ~= "\n\t\tthis.after();\n";
836                     }
837 
838                     str ~= "\n\t\tbreak;\n\t}\n";
839                 }
840             }
841         }
842     }
843 
844     str ~= "\tdefault:\n\tbreak;\n\t}\n\n";
845     str ~= "}";
846     return str;
847 }
848 
849 
850 template isDesiredUDA(alias attribute)
851 {
852     template isDesiredUDA(alias toCheck)
853     {
854         static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
855         {
856             static if (__traits(compiles, toCheck == attribute))
857                 enum isDesiredUDA = toCheck == attribute;
858             else
859                 enum isDesiredUDA = false;
860         }
861         else static if (is(typeof(toCheck)))
862         {
863             static if (__traits(isTemplate, attribute))
864                 enum isDesiredUDA =  isInstanceOf!(attribute, typeof(toCheck));
865             else
866                 enum isDesiredUDA = is(typeof(toCheck) == attribute);
867         }
868         else static if (__traits(isTemplate, attribute))
869             enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
870         else
871             enum isDesiredUDA = is(toCheck == attribute);
872     }
873 }
874 
875 
876 template ParameterUDAs(alias func, A, int i=0) {
877     static if(is(FunctionTypeOf!func PT == __parameters)) {
878         alias thisParameter = PT[i .. i + 1]; 
879         alias udas =  __traits(getAttributes, thisParameter);
880 
881         alias ParameterUDAs = Filter!(isDesiredUDA!A, udas);
882     } else {
883         static assert(0, func.stringof ~ " is not a function");
884     }
885 }
886 
887 
888 string makeParameterValidation(string varName, string paraName, paraType, UDAs ...)() {
889     string str;
890     // = "\ninfof(\"" ~ symbol.stringof ~ "\");";
891 
892     version(HUNT_FM_DEBUG) {
893         str ~= `
894             tracef("varName=`~ varName ~`, paraName=` ~ paraName ~ `");
895         `;
896     }
897 
898     static foreach(uda; UDAs) {
899         static if(is(typeof(uda) == Max)) {
900             str ~= `{
901                 MaxValidator validator = new MaxValidator();
902                 validator.initialize(` ~ uda.stringof ~ `);
903                 validator.setPropertyName("` ~ paraName ~ `");
904                 validator.isValid(`~ varName ~`, context);
905             }`;
906         }
907 
908         static if(is(typeof(uda) == Min)) {
909             str ~= `{
910                 MinValidator validator = new MinValidator();
911                 validator.initialize(` ~ uda.stringof ~ `);
912                 validator.setPropertyName("` ~ paraName ~ `");
913                 validator.isValid(`~ varName ~`, context);
914             }`;
915         }
916 
917         static if(is(typeof(uda) == AssertFalse)) {
918             str ~= `{
919                 AssertFalseValidator validator = new AssertFalseValidator();
920                 validator.initialize(` ~ uda.stringof ~ `);
921                 validator.setPropertyName("` ~ paraName ~ `");
922                 validator.isValid(`~ varName ~`, context);
923             }`;
924         }
925 
926         static if(is(typeof(uda) == AssertTrue)) {
927             str ~= `{
928                 AssertTrueValidator validator = new AssertTrueValidator();
929                 validator.initialize(` ~ uda.stringof ~ `);
930                 validator.setPropertyName("` ~ paraName ~ `");
931                 validator.isValid(`~ varName ~`, context);
932             }`;
933         }
934 
935         static if(is(typeof(uda) == Email)) {
936             str ~= `{
937                 EmailValidator validator = new EmailValidator();
938                 validator.initialize(` ~ uda.stringof ~ `);
939                 validator.setPropertyName("` ~ paraName ~ `");
940                 validator.isValid(`~ varName ~`, context);
941             }`;
942         }
943 
944         static if(is(typeof(uda) == Length)) {
945             str ~= `{
946                 LengthValidator validator = new LengthValidator();
947                 validator.initialize(` ~ uda.stringof ~ `);
948                 validator.setPropertyName("` ~ paraName ~ `");
949                 validator.isValid(`~ varName ~`, context);
950             }`;
951         }
952 
953         static if(is(typeof(uda) == NotBlank)) {
954             str ~= `{
955                 NotBlankValidator validator = new NotBlankValidator();
956                 validator.initialize(` ~ uda.stringof ~ `);
957                 validator.setPropertyName("` ~ paraName ~ `");
958                 validator.isValid(`~ varName ~`, context);
959             }`;
960         }
961 
962         static if(is(typeof(uda) == NotEmpty)) {
963             str ~= `{
964                 auto validator = new NotEmptyValidator!` ~ paraType.stringof ~`();
965                 validator.initialize(` ~ uda.stringof ~ `);
966                 validator.setPropertyName("` ~ paraName ~ `");
967                 validator.isValid(`~ varName ~`, context);
968             }`;
969         }
970 
971         static if(is(typeof(uda) == Pattern)) {
972             str ~= `{
973                 PatternValidator validator = new PatternValidator();
974                 validator.initialize(` ~ uda.stringof ~ `);
975                 validator.setPropertyName("` ~ paraName ~ `");
976                 validator.isValid(`~ varName ~`, context);
977             }`;
978         }
979 
980         static if(is(typeof(uda) == Size)) {
981             str ~= `{
982                 SizeValidator validator = new SizeValidator();
983                 validator.initialize(` ~ uda.stringof ~ `);
984                 validator.setPropertyName("` ~ paraName ~ `");
985                 validator.isValid(`~ varName ~`, context);
986             }`;
987         }
988 
989         static if(is(typeof(uda) == Range)) {
990             str ~= `{
991                 RangeValidator validator = new RangeValidator();
992                 validator.initialize(` ~ uda.stringof ~ `);
993                 validator.setPropertyName("` ~ paraName ~ `");
994                 validator.isValid(`~ varName ~`, context);
995             }`;
996         }
997     }
998 
999     return str;
1000 }
1001 
1002 alias QueryParameterValidator = void delegate(ConstraintValidatorContext);
1003 
1004 string __createRouteMap(T, string moduleName)()
1005 {
1006 
1007     enum len = "Controller".length;
1008     enum controllerName = moduleName[0..$-len];
1009 
1010     // The format: 
1011     // 1) app.controller.[{group}.]{name}controller
1012     //      app.controller.admin.IndexController
1013     //      app.controller.IndexController
1014     // 
1015     // 2) app.component.{component-name}.controller.{group}.{name}controller
1016     //      app.component.system.controller.admin.DashboardController
1017     enum string[] parts = moduleName.split(".");
1018     // string groupName = "default";
1019 
1020     static if(parts.length == 4) {
1021         // app.controller.admin.DashboardController
1022         enum GroupName = parts[2];
1023     } else static if(parts.length == 6) {
1024         // app.component.system.controller.admin.DashboardController
1025         enum GroupName = parts[4];
1026     } else {
1027         enum GroupName = "default";
1028     }
1029 
1030     string str = "";
1031     foreach (memberName; __traits(allMembers, T))
1032     {
1033         // pragma(msg, "memberName: ", memberName);
1034 
1035         static if (is(typeof(__traits(getMember, T, memberName)) == function)) {
1036             foreach (t; __traits(getOverloads, T, memberName)) {
1037                 static if (hasUDA!(t, Action)) {
1038                     enum string MemberName = memberName;
1039                 } else static if (isActionMember(memberName)) {
1040                     enum string MemberName = memberName[0 .. $ - actionNameLength];
1041                 } else {
1042                     enum string MemberName = "";
1043                 }
1044 
1045                 static if(MemberName.length > 0) {
1046                     str ~= "\n\tregisterRouteHandler(\"" ~ controllerName ~ "." ~ T.stringof ~ "." ~ MemberName
1047                         ~ "\", (context) { 
1048                             context.groupName = \"" ~ GroupName ~ "\";
1049                             callHandler!(" ~ T.stringof ~ ",\"" ~ memberName ~ "\")(context);
1050                     });\n";
1051                 }
1052             }
1053         }
1054     }
1055 
1056     return str;
1057 }
1058 
1059 void callHandler(T, string method)(RoutingContext context)
1060         if (is(T == class) || (is(T == struct) && hasMember!(T, "__CALLACTION__")))
1061 {
1062     // req.action = method;
1063     // auto req = context.getRequest();
1064     // warningf("group name: %s, Threads: %d", context.groupName(), Thread.getAll().length);
1065 
1066     T controller = new T();
1067 
1068     scope(exit) {
1069         import hunt.util.ResoureManager;
1070         ApplicationConfig appConfig = app().config();
1071         if(appConfig.http.workerThreads == 0) {
1072             collectResoure();
1073         }
1074         controller.dispose();
1075         // HUNT_THREAD_DEBUG
1076         version(HUNT_DEBUG) {
1077             warningf("Threads: %d, allocatedInCurrentThread: %d bytes", 
1078                 Thread.getAll().length, GC.stats().allocatedInCurrentThread);
1079         }
1080         // GC.collect();
1081     }
1082 
1083     try {
1084         controller.initializeMiddlewares();
1085         controller.callActionMethod(method, context);
1086         controller.done();
1087     } catch (Throwable t) {
1088         error(t);
1089         app().logger().error(t);
1090         Response errorRes = new Response();
1091         errorRes.doError(HttpStatus.INTERNAL_SERVER_ERROR_500, t);
1092         controller.raiseError(errorRes); 
1093     }
1094     
1095     context.end();
1096 }
1097 
1098 RoutingHandler getRouteHandler(string str)
1099 {
1100     return _actions.get(str, null);
1101 }
1102 
1103 void registerRouteHandler(string str, RoutingHandler method)
1104 {
1105     // key: app.controller.Index.IndexController.showString
1106     version (HUNT_FM_DEBUG) trace("Add route handler: ", str);
1107     _actions[str.toLower] = method;
1108 }
1109 
1110 __gshared RoutingHandler[string] _actions;