1 /** 2 * Модуль контроллера генерирующий обработчики в compile time на основе интерфейсов 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-08-01 8 */ 9 10 module dango.web.controllers.generic; 11 12 public 13 { 14 import dango.web.controller; 15 } 16 17 private 18 { 19 import std.functional : toDelegate; 20 import std.traits; 21 import std.meta; 22 23 import bolts : FilterMembersOf, protectionLevel, ProtectionLevel; 24 25 import dango.inject.provider : ClassProvider; 26 import dango.system.logging : logError; 27 import dango.web.server : joinInetPath; 28 } 29 30 31 /** 32 * Аннотация для обозначение объекта контроллера 33 */ 34 struct ControllerAttribute(alias W) 35 { 36 string prefix; 37 } 38 39 40 /** 41 * Аннотация для обозначение объекта контроллера 42 * Params: 43 * 44 * prefix = Префикс для всех путей 45 */ 46 ControllerAttribute!W Controller(alias W = defaultHandler)(string prefix = "") 47 { 48 return ControllerAttribute!W(prefix); 49 } 50 51 52 /** 53 * Аннотация для обозначения метода для обработки входящих запросов 54 * Params: 55 * 56 * path = Путь 57 * method = Метод 58 */ 59 struct Handler(HTTPMethod M = HTTPMethod.GET) 60 { 61 string path; 62 enum method = M; 63 } 64 65 alias Get = Handler!(HTTPMethod.GET); 66 alias Post = Handler!(HTTPMethod.POST); 67 alias Put = Handler!(HTTPMethod.PUT); 68 alias Delete = Handler!(HTTPMethod.DELETE); 69 70 @("Should work Hander") 71 @safe unittest 72 { 73 auto hdl = Post("/"); 74 assert (hdl.method == HTTPMethod.POST); 75 76 @Post("/") 77 void handler() {} 78 assert (hasUDA!(handler, Handler)); 79 80 void noHandler() {} 81 assert (!hasUDA!(noHandler, Handler)); 82 83 @Handler!(HTTPMethod.HEAD)("/h") 84 void customHandler() {} 85 86 enum udas = getUDAs!(customHandler, Handler); 87 assert (udas.length); 88 assert (udas[0].method == HTTPMethod.HEAD); 89 assert (udas[0].path == "/h"); 90 } 91 92 93 /** 94 * Обертка над обработчиками по умочлчанию 95 */ 96 HTTPServerRequestDelegate defaultHandler(C, HType, alias M)(C controller, HType hdl) @safe 97 { 98 return hdl; 99 } 100 101 102 /** 103 * Базовый класс web контроллера 104 * Params: 105 * CType = Объект с определенными в нем обработчиками 106 */ 107 class GenericWebController(CType) : WebController 108 if (is(CType == class)) 109 { 110 private 111 { 112 alias CTLs = GetControllerUDAs!CType; 113 static assert(CTLs.length, 114 "Class '" ~ CType.stringof ~ "' is not controller"); 115 enum CTL = CTLs[0]; 116 alias WRAP = TemplateArgsOf!(typeof(CTL))[0]; 117 enum __errorMsg = "The handler creation function must match '" ~ 118 WRAP.stringof ~ "'"; 119 CType _controller; 120 } 121 122 /** 123 * Main constructor 124 */ 125 this(CType controller) @safe 126 { 127 this._controller = controller; 128 } 129 130 /** 131 * Регистрация цепочек маршрутов контроллера 132 */ 133 void registerChains(RegisterChainCallback dg) @safe 134 { 135 alias Handlers = GetWebControllerHandlers!CType; 136 static assert(Handlers.length, "The controller '" ~ CType.stringof 137 ~ "' must contain handlers"); 138 139 foreach(MemberName; Handlers) 140 { 141 alias Member = Alias!(__traits(getMember, CType, MemberName)); 142 enum hdlUDA = getUDAs!(Member, Handler)[0]; 143 auto HDL = &__traits(getMember, _controller, MemberName); 144 alias MemberType = typeof(toDelegate(HDL)); 145 enum ident = __traits(identifier, Member); 146 147 alias __wrap = WRAP!(CType, MemberType, Member); 148 static assert(is(ReturnType!__wrap == HTTPServerRequestDelegate), 149 "Handler must return HTTPServerRequestDelegate"); 150 alias __P = Parameters!__wrap; 151 static assert(__P.length == 2, __errorMsg); 152 static assert(is(__P[0] : CType), __errorMsg); 153 static assert(is(__P[1] == MemberType), __errorMsg); 154 155 auto hdl = __wrap(_controller, HDL); 156 if (hdl !is null) 157 dg(hdlUDA.method, getFullPath(hdlUDA.path), new Chain(hdl)); 158 else 159 logError("Handler '%s' in controller '%s' not register", 160 MemberName, CType.stringof); 161 } 162 } 163 164 165 private string getFullPath(string path) 166 { 167 static if (CTL.prefix.length > 0) 168 return joinInetPath(CTL.prefix, path); 169 else 170 return path; 171 } 172 } 173 174 175 /** 176 * Фабрика для контроллера на основе кодогенерации 177 */ 178 class GenericWebControllerFactory(CType) : WebControllerFactory 179 if (is(CType == class)) 180 { 181 static assert(IsGenericController!CType, 182 "Class '" ~ CType.stringof ~ "' is not controller"); 183 184 /** 185 * Создает новый контроллер 186 */ 187 WebController createComponent(DependencyContainer cnt, UniConf conf) @safe 188 { 189 CType controller; 190 auto provider = new ClassProvider!(CType, CType)(cnt); 191 provider.withProvided(true, (val) @trusted { 192 controller = cast(CType)(*(cast(Object*)val)); 193 }); 194 return new GenericWebController!CType(controller); 195 } 196 } 197 198 199 private: 200 201 202 /** 203 * Возвращает список обработчиков контроллера 204 * Params: 205 * C = Проверяемый тип 206 */ 207 template GetWebControllerHandlers(C) 208 { 209 template IsHandler(T, string name) 210 { 211 alias Member = Alias!(__traits(getMember, T, name)); 212 static if (protectionLevel!Member == ProtectionLevel.public_) 213 { 214 static if (isCallable!Member && getUDAs!(Member, Handler).length) 215 enum IsHandler = true; 216 else 217 enum IsHandler = false; 218 } 219 else 220 enum IsHandler = false; 221 } 222 223 alias GetWebControllerHandlers = FilterMembersOf!(C, IsHandler); 224 } 225 226 227 /** 228 * Проверка на тип контроллера 229 */ 230 template IsGenericController(CType) 231 { 232 enum IsGenericController = GetControllerUDAs!CType.length > 0; 233 } 234 235 236 /** 237 * Возвращает аннотации контроллера 238 */ 239 template GetControllerUDAs(CType) 240 { 241 template IsController(alias A) 242 { 243 static if (!isType!A) 244 { 245 enum V = A; // call is function 246 enum IsController = __traits(isSame, 247 TemplateOf!(typeof(V)), ControllerAttribute); 248 } 249 else 250 enum IsController = false; 251 } 252 253 alias GetControllerUDAs = Filter!(IsController, 254 __traits(getAttributes, CType)); 255 } 256