/// <summary> /// Builds object mapper from OWIN environment for given .NET type. /// </summary> /// <param name="type">The type of object to map.</param> public static Func <IOwinContext, object> Build(Type type) { if (type == null) { throw new ArgumentNullException("type"); } var ctors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); var ctor = ctors.FirstOrDefault(c => c.GetParameters().Any(p => p.HasAttribute <BindingAttribute>())); if (ctor != null) { var args = ParameterMapper.Build(ctor); // TODO build expression tree to create instance return(ctx => ctor.Invoke(args(ctx))); } var props = type .GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) .Where(p => p.CanWrite && p.GetIndexParameters().Length == 0 && p.HasAttribute <BindingAttribute>()) .ToArray(); var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) .Where(f => !f.IsLiteral && !f.IsInitOnly && f.HasAttribute <BindingAttribute>()) .ToArray(); var setters = props .Select(p => Setter(p)) .Concat(fields.Select(f => Setter(f))) .ToArray(); return(ctx => { var instance = Activator.CreateInstance(type); foreach (var setter in setters) { setter(ctx, instance); } return instance; }); }
/// <summary> /// Registers methods annotated with <see cref="RouteAttribute"/> into routing pipeline. /// </summary> /// <typeparam name="T">Type to reflect.</typeparam> /// <param name="mapRouteBuilder">The fluent API to register http method handlers.</param> /// <param name="getInstance">Function to get instance of T.</param> /// <returns></returns> public static MapRouteBuilder UseApi <T>(this MapRouteBuilder mapRouteBuilder, Func <IOwinContext, T> getInstance) { if (getInstance == null) { throw new ArgumentNullException("getInstance"); } var type = typeof(T); var prefixAttr = type.GetAttribute <RoutePrefixAttribute>(); var prefix = prefixAttr != null ? prefixAttr.Prefix : string.Empty; var serializerSettings = type.GetProperties(BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public) .Where(p => p.HasAttribute <ResponseSerializerSettingsAttribute>() && p.PropertyType == typeof(JsonSerializerSettings)) .Select(p => p.GetValue(null) as JsonSerializerSettings) .FirstOrDefault(); var errorHandler = type.GetMethods(BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.Public) .Where(m => m.HasAttribute <ErrorHandlerAttribute>()) .Select(m => DynamicMethods.CompileMethod(type, m)) .FirstOrDefault(); const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; var actions = (from m in type.GetMethods(bindingFlags) let route = m.GetAttribute <RouteAttribute>() where route != null select new { Method = m, Route = route }).ToList(); actions.ForEach(a => { var invoke = DynamicMethods.CompileMethod(type, a.Method); var mapper = ParameterMapper.Build(a.Method); var returnType = a.Method.ReturnType; var verb = GetHttpMethod(a.Method); var pattern = AddPrefix(prefix, a.Route.Template); mapRouteBuilder.Register(pattern, verb, async ctx => { Exception error; var args = MapParameters(ctx, mapper, out error); if (error != null) { if (null != errorHandler) { var errorResponse = errorHandler(null, new object[] { ctx, error }); if (null != errorResponse) { await ctx.WriteJson(errorResponse); } } else { ctx.Response.StatusCode = (int)HttpStatusCode.BadRequest; await ctx.WriteJson(new { error = error.Message }); } return; } var instance = a.Method.IsStatic ? (object)null : getInstance(ctx); var result = invoke(instance, args); var task = result as Task; if (null != task) { result = await HandleAsyncResult(task, returnType); } if (result != null) { await ctx.WriteJson(result, serializerSettings); } }); }); return(mapRouteBuilder); }