diff --git a/src/ServiceStack.Interfaces/ServiceHost/IServiceController.cs b/src/ServiceStack.Interfaces/ServiceHost/IServiceController.cs index f5c189cfcc7..0a1dcba239e 100644 --- a/src/ServiceStack.Interfaces/ServiceHost/IServiceController.cs +++ b/src/ServiceStack.Interfaces/ServiceHost/IServiceController.cs @@ -1,45 +1,50 @@ -using System; -using System.Collections.Generic; - -namespace ServiceStack.ServiceHost -{ - /// - /// Responsible for executing the operation within the specified context. - /// - /// The operation types. - public interface IServiceController - { - /// - /// Returns a list of operation types available in this service - /// - /// The operation types. - IList OperationTypes { get; } - - /// - /// Returns a list of ALL operation types available in this service - /// - /// The operation types. - IList AllOperationTypes { get; } - - /// - /// Returns the first matching RestPath - /// - /// - /// - /// - IRestPath GetRestPathForRequest(string httpMethod, string pathInfo); - - /// - /// Allow the registration of custom routes - /// - IServiceRoutes Routes { get; } - - /// - /// Executes the DTO request under the supplied requestContext. - /// - /// - /// - /// - object Execute(object request, IRequestContext requestContext); - } +using System; +using System.Collections.Generic; + +namespace ServiceStack.ServiceHost +{ + /// + /// Responsible for executing the operation within the specified context. + /// + /// The operation types. + public interface IServiceController + { + /// + /// Returns a list of operation types available in this service + /// + /// The operation types. + IList OperationTypes { get; } + + /// + /// Returns a list of ALL operation types available in this service + /// + /// The operation types. + IList AllOperationTypes { get; } + + /// + /// Returns the first matching RestPath + /// + /// + /// + /// + IRestPath GetRestPathForRequest(string httpMethod, string pathInfo); + + /// + /// Allow the registration of custom routes + /// + IServiceRoutes Routes { get; } + + /// + /// Executes the DTO request with no requestContext. + /// + object Execute(object dto); + + /// + /// Executes the DTO request under the supplied requestContext. + /// + /// + /// + /// + object Execute(object request, IRequestContext requestContext); + } } \ No newline at end of file diff --git a/src/ServiceStack/ServiceHost/NServiceExec.cs b/src/ServiceStack/ServiceHost/NServiceExec.cs index a5194b8f41d..13e3fd486ab 100644 --- a/src/ServiceStack/ServiceHost/NServiceExec.cs +++ b/src/ServiceStack/ServiceHost/NServiceExec.cs @@ -1,165 +1,167 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; -using ServiceStack.ServiceClient.Web; -using ServiceStack.Text; -using ServiceStack.WebHost.Endpoints; - -namespace ServiceStack.ServiceHost -{ - public interface INServiceExec - { - object Execute(IRequestContext requestContext, object instance, object request); - } - - public class NServiceRequestExec : INServiceExec - { - static NServiceRequestExec() - { - NServiceExec.CreateServiceRunnersFor(); - } - - public object Execute(IRequestContext requestContext, object instance, object request) - { - return NServiceExec.Execute(requestContext, instance, request, - typeof(TRequest).Name); - } - } - - public class NServiceExec - { - private static Dictionary> actionMap - = new Dictionary>(); - - private static Dictionary execMap - = new Dictionary(); - - static NServiceExec() - { - var mis = typeof(TService).GetMethods(BindingFlags.Public | BindingFlags.Instance); - foreach (var methodInfo in mis) - { - var mi = methodInfo; - if (mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void)) continue; - var args = mi.GetParameters(); - if (args.Length != 1) continue; - var actionName = mi.Name.ToUpper(); - if (!HttpMethod.AllVerbs.Contains(actionName) && actionName != ActionContext.AnyAction) - continue; - - var requestType = args[0].ParameterType; - var actionCtx = new ActionContext { - Id = ActionContext.Key(actionName, requestType.Name), - RequestType = requestType, - }; - - try - { - actionCtx.ServiceAction = CreateExecFn(requestType, mi); - } - catch - { - //Potential problems with MONO, using reflection for fallback - actionCtx.ServiceAction = (service, request) => - mi.Invoke(service, new[] { request }); - } - - var reqFilters = new List(); - var resFilters = new List(); - - foreach (var attr in mi.GetCustomAttributes(false)) - { - var hasReqFilter = attr as IHasRequestFilter; - var hasResFilter = attr as IHasResponseFilter; - - if (hasReqFilter != null) - reqFilters.Add(hasReqFilter); - - if (hasResFilter != null) - resFilters.Add(hasResFilter); - } - - if (reqFilters.Count > 0) - actionCtx.RequestFilters = reqFilters.ToArray(); - - if (resFilters.Count > 0) - actionCtx.ResponseFilters = resFilters.ToArray(); - - if (!actionMap.ContainsKey(requestType)) - actionMap[requestType] = new List(); - - actionMap[requestType].Add(actionCtx); - } - } - - public static ActionInvokerFn CreateExecFn(Type requestType, MethodInfo mi) - { - var serviceType = typeof(TService); - - var serviceParam = Expression.Parameter(typeof(object), "serviceObj"); - var serviceStrong = Expression.Convert(serviceParam, serviceType); - - var requestDtoParam = Expression.Parameter(typeof(object), "requestDto"); - var requestDtoStrong = Expression.Convert(requestDtoParam, requestType); - - Expression callExecute = Expression.Call( - serviceStrong, mi, requestDtoStrong); - - if (mi.ReturnType != typeof(void)) - { - var executeFunc = Expression.Lambda - (callExecute, serviceParam, requestDtoParam).Compile(); - - return executeFunc; - } - else - { - var executeFunc = Expression.Lambda - (callExecute, serviceParam, requestDtoParam).Compile(); - - return (service, request) => { - executeFunc(service, request); - return null; - }; - } - } - - public static List GetActionsFor() - { - List requestActions; - return actionMap.TryGetValue(typeof(TRequest), out requestActions) - ? requestActions - : new List(); - } - - public static void CreateServiceRunnersFor() - { - foreach (var actionCtx in GetActionsFor()) - { - if (execMap.ContainsKey(actionCtx.Id)) continue; - - var serviceRunner = EndpointHost.CreateServiceRunner(actionCtx); - execMap[actionCtx.Id] = serviceRunner.Process; - } - } - - public static object Execute(IRequestContext requestContext, - object instance, object request, string requestName) - { - var actionName = requestContext.Get().HttpMethod; - - InstanceExecFn action; - if (execMap.TryGetValue(ActionContext.Key(actionName, requestName), out action) - || execMap.TryGetValue(ActionContext.AnyKey(requestName), out action)) - { - return action(requestContext, instance, request); - } - - var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLower(); - throw new NotImplementedException( - "Could not find method named {1}({0}) or Any({0}) on Service {2}" - .Fmt(request.GetType().Name, expectedMethodName, typeof(TService).Name)); - } - } +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using ServiceStack.ServiceClient.Web; +using ServiceStack.Text; +using ServiceStack.WebHost.Endpoints; + +namespace ServiceStack.ServiceHost +{ + public interface INServiceExec + { + object Execute(IRequestContext requestContext, object instance, object request); + } + + public class NServiceRequestExec : INServiceExec + { + static NServiceRequestExec() + { + NServiceExec.CreateServiceRunnersFor(); + } + + public object Execute(IRequestContext requestContext, object instance, object request) + { + return NServiceExec.Execute(requestContext, instance, request, + typeof(TRequest).Name); + } + } + + public class NServiceExec + { + private static Dictionary> actionMap + = new Dictionary>(); + + private static Dictionary execMap + = new Dictionary(); + + static NServiceExec() + { + var mis = typeof(TService).GetMethods(BindingFlags.Public | BindingFlags.Instance); + foreach (var methodInfo in mis) + { + var mi = methodInfo; + if (mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void)) continue; + var args = mi.GetParameters(); + if (args.Length != 1) continue; + var actionName = mi.Name.ToUpper(); + if (!HttpMethod.AllVerbs.Contains(actionName) && actionName != ActionContext.AnyAction) + continue; + + var requestType = args[0].ParameterType; + var actionCtx = new ActionContext { + Id = ActionContext.Key(actionName, requestType.Name), + RequestType = requestType, + }; + + try + { + actionCtx.ServiceAction = CreateExecFn(requestType, mi); + } + catch + { + //Potential problems with MONO, using reflection for fallback + actionCtx.ServiceAction = (service, request) => + mi.Invoke(service, new[] { request }); + } + + var reqFilters = new List(); + var resFilters = new List(); + + foreach (var attr in mi.GetCustomAttributes(false)) + { + var hasReqFilter = attr as IHasRequestFilter; + var hasResFilter = attr as IHasResponseFilter; + + if (hasReqFilter != null) + reqFilters.Add(hasReqFilter); + + if (hasResFilter != null) + resFilters.Add(hasResFilter); + } + + if (reqFilters.Count > 0) + actionCtx.RequestFilters = reqFilters.ToArray(); + + if (resFilters.Count > 0) + actionCtx.ResponseFilters = resFilters.ToArray(); + + if (!actionMap.ContainsKey(requestType)) + actionMap[requestType] = new List(); + + actionMap[requestType].Add(actionCtx); + } + } + + public static ActionInvokerFn CreateExecFn(Type requestType, MethodInfo mi) + { + var serviceType = typeof(TService); + + var serviceParam = Expression.Parameter(typeof(object), "serviceObj"); + var serviceStrong = Expression.Convert(serviceParam, serviceType); + + var requestDtoParam = Expression.Parameter(typeof(object), "requestDto"); + var requestDtoStrong = Expression.Convert(requestDtoParam, requestType); + + Expression callExecute = Expression.Call( + serviceStrong, mi, requestDtoStrong); + + if (mi.ReturnType != typeof(void)) + { + var executeFunc = Expression.Lambda + (callExecute, serviceParam, requestDtoParam).Compile(); + + return executeFunc; + } + else + { + var executeFunc = Expression.Lambda + (callExecute, serviceParam, requestDtoParam).Compile(); + + return (service, request) => { + executeFunc(service, request); + return null; + }; + } + } + + public static List GetActionsFor() + { + List requestActions; + return actionMap.TryGetValue(typeof(TRequest), out requestActions) + ? requestActions + : new List(); + } + + public static void CreateServiceRunnersFor() + { + foreach (var actionCtx in GetActionsFor()) + { + if (execMap.ContainsKey(actionCtx.Id)) continue; + + var serviceRunner = EndpointHost.CreateServiceRunner(actionCtx); + execMap[actionCtx.Id] = serviceRunner.Process; + } + } + + public static object Execute(IRequestContext requestContext, + object instance, object request, string requestName) + { + var actionName = requestContext != null + ? requestContext.Get().HttpMethod + : HttpMethod.Post; //MQ Services + + InstanceExecFn action; + if (execMap.TryGetValue(ActionContext.Key(actionName, requestName), out action) + || execMap.TryGetValue(ActionContext.AnyKey(requestName), out action)) + { + return action(requestContext, instance, request); + } + + var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLower(); + throw new NotImplementedException( + "Could not find method named {1}({0}) or Any({0}) on Service {2}" + .Fmt(request.GetType().Name, expectedMethodName, typeof(TService).Name)); + } + } } \ No newline at end of file diff --git a/src/ServiceStack/ServiceHost/ServiceRunner.cs b/src/ServiceStack/ServiceHost/ServiceRunner.cs index 641a83d1a55..2da1b7a321f 100644 --- a/src/ServiceStack/ServiceHost/ServiceRunner.cs +++ b/src/ServiceStack/ServiceHost/ServiceRunner.cs @@ -1,188 +1,188 @@ -using System; -using System.Diagnostics; -using ServiceStack.Common; -using ServiceStack.Common.Web; -using ServiceStack.Logging; -using ServiceStack.Messaging; -using ServiceStack.ServiceClient.Web; -using ServiceStack.Text; -using ServiceStack.WebHost.Endpoints; - -namespace ServiceStack.ServiceHost -{ - public class ServiceRunner : IServiceRunner - { - protected static readonly ILog Log = LogManager.GetLogger(typeof(ServiceRunner<>)); - - protected readonly IAppHost AppHost; - protected readonly ActionInvokerFn ServiceAction; - protected readonly IHasRequestFilter[] RequestFilters; - protected readonly IHasResponseFilter[] ResponseFilters; - - public ServiceRunner() { } - - public ServiceRunner(IAppHost appHost, ActionContext actionContext) - { - this.AppHost = appHost; - this.ServiceAction = actionContext.ServiceAction; - this.RequestFilters = actionContext.RequestFilters; - this.ResponseFilters = actionContext.ResponseFilters; - } - - public IAppHost GetAppHost() - { - return AppHost ?? EndpointHost.AppHost; - } - - public T TryResolve() - { - return this.GetAppHost() == null - ? default(T) - : this.GetAppHost().TryResolve(); - } - - public T ResolveService(IRequestContext requestContext) - { - var service = this.GetAppHost().TryResolve(); - var requiresContext = service as IRequiresRequestContext; - if (requiresContext != null) - { - requiresContext.RequestContext = requestContext; - } - return service; - } - - public virtual void BeforeEachRequest(IRequestContext requestContext, TRequest request) - { - OnBeforeExecute(requestContext, request); - - var requestLogger = TryResolve(); - if (requestLogger != null) - { - requestContext.SetItem("_requestDurationStopwatch", Stopwatch.StartNew()); - } - } - - public virtual object AfterEachRequest(IRequestContext requestContext, TRequest request, object response) - { - var requestLogger = TryResolve(); - if (requestLogger != null) - { - try - { - var stopWatch = (Stopwatch)requestContext.GetItem("_requestDurationStopwatch"); - requestLogger.Log(requestContext, request, response, stopWatch.Elapsed); - } - catch (Exception ex) - { - Log.Error("Error while logging request: " + request.Dump(), ex); - } - } - - //only call OnAfterExecute if no exception occured - return response.IsErrorResponse() ? response : OnAfterExecute(requestContext, response); - } - - public virtual void OnBeforeExecute(IRequestContext requestContext, TRequest request) { } - - public virtual object OnAfterExecute(IRequestContext requestContext, object response) - { - return response; - } - - public virtual object Execute(IRequestContext requestContext, object instance, TRequest request) - { - try - { - BeforeEachRequest(requestContext, request); - - var httpReq = requestContext.Get(); - var httpRes = requestContext.Get(); - - if (RequestFilters != null) - { - foreach (var requestFilter in RequestFilters) - { - requestFilter.RequestFilter(httpReq, httpRes, request); - if (httpRes.IsClosed) return null; - } - } - - var response = AfterEachRequest(requestContext, request, ServiceAction(instance, request)); - - if (ResponseFilters != null) - { - foreach (var responseFilter in ResponseFilters) - { - responseFilter.ResponseFilter(httpReq, httpRes, response); - if (httpRes.IsClosed) return null; - } - } - - return response; - } - catch (Exception ex) - { - var result = HandleException(requestContext, request, ex); - - if (result == null) throw; - - return result; - } - } - - public virtual object Execute(IRequestContext requestContext, object instance, IMessage request) - { - return Execute(requestContext, instance, request.GetBody()); - } - - public virtual object HandleException(IRequestContext requestContext, TRequest request, Exception ex) - { - var useAppHost = GetAppHost(); - - //TODO workout validation errors - var errorResponse = useAppHost != null && useAppHost.ServiceExceptionHandler != null - ? useAppHost.ServiceExceptionHandler(request, ex) - : DtoUtils.HandleException(useAppHost, request, ex); - - AfterEachRequest(requestContext, request, errorResponse ?? ex); - - return errorResponse; - } - - public object ExecuteOneWay(IRequestContext requestContext, object instance, TRequest request) - { - var msgFactory = TryResolve(); - if (msgFactory == null) - { - return Execute(requestContext, instance, request); - } - - //Capture and persist this async request on this Services 'In Queue' - //for execution after this request has been completed - using (var producer = msgFactory.CreateMessageProducer()) - { - producer.Publish(request); - } - - return WebRequestUtils.GetErrorResponseDtoType(request).CreateInstance(); - } - - //signature matches ServiceExecFn - public object Process(IRequestContext requestContext, object instance, object request) - { - return Execute(requestContext, instance, (TRequest)request); - } - - public object ProcessOneWay(IRequestContext requestContext, object instance, object request) - { - return ExecuteOneWay(requestContext, instance, (TRequest)request); - } - - public object Process(IRequestContext requestContext, object instance, IMessage message) - { - return Execute(requestContext, instance, (IMessage)message); - } - } - +using System; +using System.Diagnostics; +using ServiceStack.Common; +using ServiceStack.Common.Web; +using ServiceStack.Logging; +using ServiceStack.Messaging; +using ServiceStack.ServiceClient.Web; +using ServiceStack.Text; +using ServiceStack.WebHost.Endpoints; + +namespace ServiceStack.ServiceHost +{ + public class ServiceRunner : IServiceRunner + { + protected static readonly ILog Log = LogManager.GetLogger(typeof(ServiceRunner<>)); + + protected readonly IAppHost AppHost; + protected readonly ActionInvokerFn ServiceAction; + protected readonly IHasRequestFilter[] RequestFilters; + protected readonly IHasResponseFilter[] ResponseFilters; + + public ServiceRunner() { } + + public ServiceRunner(IAppHost appHost, ActionContext actionContext) + { + this.AppHost = appHost; + this.ServiceAction = actionContext.ServiceAction; + this.RequestFilters = actionContext.RequestFilters; + this.ResponseFilters = actionContext.ResponseFilters; + } + + public IAppHost GetAppHost() + { + return AppHost ?? EndpointHost.AppHost; + } + + public T TryResolve() + { + return this.GetAppHost() == null + ? default(T) + : this.GetAppHost().TryResolve(); + } + + public T ResolveService(IRequestContext requestContext) + { + var service = this.GetAppHost().TryResolve(); + var requiresContext = service as IRequiresRequestContext; + if (requiresContext != null) + { + requiresContext.RequestContext = requestContext; + } + return service; + } + + public virtual void BeforeEachRequest(IRequestContext requestContext, TRequest request) + { + OnBeforeExecute(requestContext, request); + + var requestLogger = TryResolve(); + if (requestLogger != null) + { + requestContext.SetItem("_requestDurationStopwatch", Stopwatch.StartNew()); + } + } + + public virtual object AfterEachRequest(IRequestContext requestContext, TRequest request, object response) + { + var requestLogger = TryResolve(); + if (requestLogger != null) + { + try + { + var stopWatch = (Stopwatch)requestContext.GetItem("_requestDurationStopwatch"); + requestLogger.Log(requestContext, request, response, stopWatch.Elapsed); + } + catch (Exception ex) + { + Log.Error("Error while logging request: " + request.Dump(), ex); + } + } + + //only call OnAfterExecute if no exception occured + return response.IsErrorResponse() ? response : OnAfterExecute(requestContext, response); + } + + public virtual void OnBeforeExecute(IRequestContext requestContext, TRequest request) { } + + public virtual object OnAfterExecute(IRequestContext requestContext, object response) + { + return response; + } + + public virtual object Execute(IRequestContext requestContext, object instance, TRequest request) + { + try + { + BeforeEachRequest(requestContext, request); + + var httpReq = requestContext != null ? requestContext.Get() : null; + var httpRes = requestContext != null ? requestContext.Get() : null; + + if (RequestFilters != null) + { + foreach (var requestFilter in RequestFilters) + { + requestFilter.RequestFilter(httpReq, httpRes, request); + if (httpRes != null && httpRes.IsClosed) return null; + } + } + + var response = AfterEachRequest(requestContext, request, ServiceAction(instance, request)); + + if (ResponseFilters != null) + { + foreach (var responseFilter in ResponseFilters) + { + responseFilter.ResponseFilter(httpReq, httpRes, response); + if (httpRes != null && httpRes.IsClosed) return null; + } + } + + return response; + } + catch (Exception ex) + { + var result = HandleException(requestContext, request, ex); + + if (result == null) throw; + + return result; + } + } + + public virtual object Execute(IRequestContext requestContext, object instance, IMessage request) + { + return Execute(requestContext, instance, request.GetBody()); + } + + public virtual object HandleException(IRequestContext requestContext, TRequest request, Exception ex) + { + var useAppHost = GetAppHost(); + + //TODO workout validation errors + var errorResponse = useAppHost != null && useAppHost.ServiceExceptionHandler != null + ? useAppHost.ServiceExceptionHandler(request, ex) + : DtoUtils.HandleException(useAppHost, request, ex); + + AfterEachRequest(requestContext, request, errorResponse ?? ex); + + return errorResponse; + } + + public object ExecuteOneWay(IRequestContext requestContext, object instance, TRequest request) + { + var msgFactory = TryResolve(); + if (msgFactory == null) + { + return Execute(requestContext, instance, request); + } + + //Capture and persist this async request on this Services 'In Queue' + //for execution after this request has been completed + using (var producer = msgFactory.CreateMessageProducer()) + { + producer.Publish(request); + } + + return WebRequestUtils.GetErrorResponseDtoType(request).CreateInstance(); + } + + //signature matches ServiceExecFn + public object Process(IRequestContext requestContext, object instance, object request) + { + return Execute(requestContext, instance, (TRequest)request); + } + + public object ProcessOneWay(IRequestContext requestContext, object instance, object request) + { + return ExecuteOneWay(requestContext, instance, (TRequest)request); + } + + public object Process(IRequestContext requestContext, object instance, IMessage message) + { + return Execute(requestContext, instance, (IMessage)message); + } + } + } \ No newline at end of file