using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using ServiceStack.Common; using ServiceStack.Common.Utils; using ServiceStack.Common.Web; using ServiceStack.ServiceHost; using ServiceStack.Text; namespace ServiceStack.ServiceInterface { public static class ServiceRoutesExtensions { /// /// Scans the supplied Assemblies to infer REST paths and HTTP verbs. /// ///The instance. /// /// The assemblies with REST services. /// /// The same instance; /// never . public static IServiceRoutes AddFromAssembly(this IServiceRoutes routes, params Assembly[] assembliesWithServices) { foreach (Assembly assembly in assembliesWithServices) { IEnumerable services = from t in assembly.GetExportedTypes() where !t.IsAbstract && t.IsSubclassOfRawGeneric(typeof(ServiceBase<>)) select t; foreach (Type service in services) { Type baseType = service.BaseType; //go up the hierarchy to the first generic base type while (!baseType.IsGenericType) { baseType = baseType.BaseType; } Type requestType = baseType.GetGenericArguments()[0]; string allowedVerbs = null; //null == All Routes if (service.IsSubclassOfRawGeneric(typeof(RestServiceBase<>))) { //find overriden REST methods var allowedMethods = new List(); if (service.GetMethod("OnGet").DeclaringType == service) { allowedMethods.Add(HttpMethods.Get); } if (service.GetMethod("OnPost").DeclaringType == service) { allowedMethods.Add(HttpMethods.Post); } if (service.GetMethod("OnPut").DeclaringType == service) { allowedMethods.Add(HttpMethods.Put); } if (service.GetMethod("OnDelete").DeclaringType == service) { allowedMethods.Add(HttpMethods.Delete); } if (service.GetMethod("OnPatch").DeclaringType == service) { allowedMethods.Add(HttpMethods.Patch); } if (allowedMethods.Count == 0) continue; allowedVerbs = string.Join(" ", allowedMethods.ToArray()); } routes.Add(requestType, requestType.Name, allowedVerbs, null); var hasIdField = requestType.GetProperty(IdUtils.IdField) != null; if (hasIdField) { var routePath = requestType.Name + "/{" + IdUtils.IdField + "}"; routes.Add(requestType, routePath, allowedVerbs, null); } } } return routes; } public static IServiceRoutes Add(this IServiceRoutes routes, string restPath, ApplyTo verbs) { return routes.Add(restPath, verbs.ToVerbsString()); } public static IServiceRoutes Add(this IServiceRoutes routes, Type requestType, string restPath, ApplyTo verbs) { return routes.Add(requestType, restPath, verbs.ToVerbsString(), null); } private static string ToVerbsString(this ApplyTo verbs) { var allowedMethods = new List(); if (verbs.Has(ApplyTo.Get)) allowedMethods.Add(HttpMethods.Get); if (verbs.Has(ApplyTo.Post)) allowedMethods.Add(HttpMethods.Post); if (verbs.Has(ApplyTo.Put)) allowedMethods.Add(HttpMethods.Put); if (verbs.Has(ApplyTo.Delete)) allowedMethods.Add(HttpMethods.Delete); if (verbs.Has(ApplyTo.Patch)) allowedMethods.Add(HttpMethods.Patch); if (verbs.Has(ApplyTo.Options)) allowedMethods.Add(HttpMethods.Options); if (verbs.Has(ApplyTo.Head)) allowedMethods.Add(HttpMethods.Head); return string.Join(" ", allowedMethods.ToArray()); } public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) { while (toCheck != typeof(object)) { Type cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; if (generic == cur) { return true; } toCheck = toCheck.BaseType; } return false; } private static string FormatRoute(string restPath, params Expression>[] propertyExpressions) { var properties = propertyExpressions.Select(x => string.Format("{{{0}}}", PropertyName(x))).ToArray(); return string.Format(restPath, properties); } private static string PropertyName(LambdaExpression lambdaExpression) { return (lambdaExpression.Body is UnaryExpression ? (MemberExpression)((UnaryExpression)lambdaExpression.Body).Operand : (MemberExpression)lambdaExpression.Body).Member.Name; } public static void Add(this IServiceRoutes serviceRoutes, string restPath, ApplyTo verbs, params Expression>[] propertyExpressions) { serviceRoutes.Add(FormatRoute(restPath, propertyExpressions), verbs); } } }