Permalink
Browse files

implemented first spike of new draft proposal

  • Loading branch information...
1 parent 5dc7add commit c9f8d02f91ac863212f76a55593c487f17cb3d62 @mythz mythz committed Sep 20, 2012
Showing with 1,307 additions and 916 deletions.
  1. +23 −1 src/ServiceStack.Interfaces/ServiceHost/IService.cs
  2. +9 −7 src/ServiceStack.Interfaces/ServiceInterface.ServiceModel/ErrorResponse.cs
  3. +1 −1 src/ServiceStack.ServiceInterface/AsyncServiceBase.cs
  4. +105 −0 src/ServiceStack.ServiceInterface/ErrorHandler.cs
  5. +125 −0 src/ServiceStack.ServiceInterface/Service.cs
  6. +5 −85 src/ServiceStack.ServiceInterface/ServiceBase.cs
  7. +7 −72 src/ServiceStack.ServiceInterface/ServiceModel/ResponseStatusTranslator.cs
  8. +3 −1 src/ServiceStack.ServiceInterface/ServiceStack.ServiceInterface.csproj
  9. +1 −107 src/ServiceStack.ServiceInterface/ServiceUtils.cs
  10. +6 −1 src/ServiceStack.ServiceInterface/Testing/BasicAppHost.cs
  11. +6 −1 src/ServiceStack.ServiceInterface/Testing/TestAppHost.cs
  12. +240 −0 src/ServiceStack/ServiceHost/DtoUtils.cs
  13. +1 −3 src/ServiceStack/ServiceHost/HttpRequestExtensions.cs
  14. +164 −92 src/ServiceStack/ServiceHost/ServiceController.cs
  15. +190 −107 src/ServiceStack/ServiceHost/ServiceExec.cs
  16. +2 −2 src/ServiceStack/ServiceHost/ServiceManager.cs
  17. +181 −0 src/ServiceStack/ServiceHost/ServiceRunner.cs
  18. +3 −0 src/ServiceStack/ServiceStack.csproj
  19. +15 −0 src/ServiceStack/WebHost.Endpoints/ActionContext.cs
  20. +7 −1 src/ServiceStack/WebHost.Endpoints/AppHostBase.cs
  21. +5 −0 src/ServiceStack/WebHost.Endpoints/IAppHost.cs
  22. +6 −1 src/ServiceStack/WebHost.Endpoints/Support/HttpListenerBase.cs
  23. +192 −431 tests/RazorRockstars.Console.Files/ReqStarsService.cs
  24. +2 −0 tests/RazorRockstars.Console.Files/ReqstarsController.cs
  25. +5 −0 tests/ServiceStack.ServiceHost.Tests/Formats/ViewTests.cs
  26. +1 −1 tests/ServiceStack.ServiceHost.Tests/PerfTests.cs
  27. +2 −2 tests/ServiceStack.ServiceHost.Tests/UseCase/CustomerUseCase.cs
@@ -1,4 +1,7 @@
-namespace ServiceStack.ServiceHost
+using System;
+using ServiceStack.Messaging;
+
+namespace ServiceStack.ServiceHost
{
/// <summary>
/// Marker interface
@@ -9,6 +12,25 @@ public interface IService { }
public interface IReturn<T> { }
public interface IReturnVoid { }
+ public interface IServiceRunner
+ {
+ object Process(IRequestContext requestContext, object instance, object request);
+ object Process(IRequestContext requestContext, object instance, IMessage message);
+ object ProcessAsync(IRequestContext requestContext, object instance, object request);
+ }
+
+ public interface IServiceRunner<TRequest> : IServiceRunner
+ {
+ void OnBeforeExecute(IRequestContext requestContext, TRequest request);
+ object OnAfterExecute(IRequestContext requestContext, object response);
+ object HandleException(IRequestContext requestContext, TRequest request, Exception ex);
+
+ object Execute(IRequestContext requestContext, object instance, TRequest request);
+ object Execute(IRequestContext requestContext, object instance, IMessage<TRequest> request);
+ object ExecuteAsync(IRequestContext requestContext, object instance, TRequest request);
+ }
+
+
/* Supported signatures:
public interface IAny<T>
@@ -1,10 +1,12 @@
namespace ServiceStack.ServiceInterface.ServiceModel
{
- /// <summary>
- /// Generic ResponseStatus for when Response Type can't be inferred
- /// </summary>
- public class ErrorResponse
- {
- public ResponseStatus ResponseStatus { get; set; }
- }
+ // <summary>
+ // Generic ResponseStatus for when Response Type can't be inferred
+ // </summary>
+ //public class ErrorResponse
+ //{
+ // public ResponseStatus ResponseStatus { get; set; }
+ //}
+
+ //Try returning ResponseStatus directly
}
@@ -37,7 +37,7 @@ public override object ExecuteAsync(TRequest request)
producer.Publish(request);
}
- return ServiceUtils.CreateResponseDto(request);
+ return DtoUtils.CreateResponseDto(request);
}
/// <summary>
@@ -0,0 +1,105 @@
+using System;
+using ServiceStack.Common;
+using ServiceStack.Logging;
+using ServiceStack.Redis;
+using ServiceStack.ServiceHost;
+using ServiceStack.ServiceInterface.ServiceModel;
+using ServiceStack.Text;
+using ServiceStack.WebHost.Endpoints;
+
+namespace ServiceStack.ServiceInterface
+{
+ public interface IErrorHandler
+ {
+ object HandleException<TRequest>(IAppHost appHost, TRequest request, Exception ex);
+ }
+
+ public class ErrorHandler : IErrorHandler
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ErrorHandler));
+
+ public static IErrorHandler Instance = new ErrorHandler();
+
+ /// <summary>
+ /// Service error logs are kept in 'urn:ServiceErrors:{ServiceName}'
+ /// </summary>
+ public const string UrnServiceErrorType = "ServiceErrors";
+
+ /// <summary>
+ /// Combined service error logs are maintained in 'urn:ServiceErrors:All'
+ /// </summary>
+ public const string CombinedServiceLogId = "All";
+
+ public object HandleException<TRequest>(IAppHost appHost, TRequest request, Exception ex)
+ {
+ if (ex.InnerException != null && !(ex is IHttpError))
+ ex = ex.InnerException;
+
+ var responseStatus = ResponseStatusTranslator.Instance.Parse(ex);
+
+ if (EndpointHost.UserConfig.DebugMode)
+ {
+ // View stack trace in tests and on the client
+ responseStatus.StackTrace = GetRequestErrorBody(request) + ex;
+ }
+
+ Log.Error("ServiceBase<TRequest>::Service Exception", ex);
+
+ //If Redis is configured, maintain rolling service error logs in Redis (an in-memory datastore)
+ var redisManager = appHost.TryResolve<IRedisClientsManager>();
+ if (redisManager != null)
+ {
+ try
+ {
+ //Get a thread-safe redis client from the client manager pool
+ using (var client = redisManager.GetClient())
+ {
+ //Get a client with a native interface for storing 'ResponseStatus' objects
+ var redis = client.GetTypedClient<ResponseStatus>();
+
+ //Store the errors in predictable Redis-named lists i.e.
+ //'urn:ServiceErrors:{ServiceName}' and 'urn:ServiceErrors:All'
+ var redisSeriviceErrorList = redis.Lists[UrnId.Create(UrnServiceErrorType, typeof(TRequest).Name)];
+ var redisCombinedErrorList = redis.Lists[UrnId.Create(UrnServiceErrorType, CombinedServiceLogId)];
+
+ //Append the error at the start of the service-specific and combined error logs.
+ redisSeriviceErrorList.Prepend(responseStatus);
+ redisCombinedErrorList.Prepend(responseStatus);
+
+ //Clip old error logs from the managed logs
+ const int rollingErrorCount = 1000;
+ redisSeriviceErrorList.Trim(0, rollingErrorCount);
+ redisCombinedErrorList.Trim(0, rollingErrorCount);
+ }
+ }
+ catch (Exception suppressRedisException)
+ {
+ Log.Error("Could not append exception to redis service error logs", suppressRedisException);
+ }
+ }
+
+ var errorResponse = DtoUtils.CreateErrorResponse(request, ex, responseStatus);
+
+ return errorResponse;
+ }
+
+ /// <summary>
+ /// Override to provide additional/less context about the Service Exception.
+ /// By default the request is serialized and appended to the ResponseStatus StackTrace.
+ /// </summary>
+ public virtual string GetRequestErrorBody(object request)
+ {
+ var requestString = "";
+ try
+ {
+ requestString = TypeSerializer.SerializeToString(request);
+ }
+ catch /*(Exception ignoreSerializationException)*/
+ {
+ //Serializing request successfully is not critical and only provides added error info
+ }
+
+ return string.Format("[{0}: {1}]:\n[REQUEST: {2}]", GetType().Name, DateTime.UtcNow, requestString);
+ }
+ }
+}
@@ -0,0 +1,125 @@
+using System;
+using System.Data;
+using ServiceStack.CacheAccess;
+using ServiceStack.OrmLite;
+using ServiceStack.ServiceHost;
+using ServiceStack.Text;
+using ServiceStack.WebHost.Endpoints;
+
+namespace ServiceStack.ServiceInterface
+{
+ /// <summary>
+ /// Generic + Useful IService base class
+ /// </summary>
+ public class Service : IService, IRequiresRequestContext, IDisposable
+ {
+ public IRequestContext RequestContext { get; set; }
+
+ private IAppHost appHost;
+ public virtual IAppHost GetAppHost()
+ {
+ return appHost ?? EndpointHost.AppHost;
+ }
+
+ public virtual void SetAppHost(IAppHost appHost)
+ {
+ this.appHost = appHost;
+ }
+
+ public virtual T TryResolve<T>()
+ {
+ return this.GetAppHost() == null
+ ? default(T)
+ : this.GetAppHost().TryResolve<T>();
+ }
+
+ public virtual T ResolveService<T>()
+ {
+ var service = TryResolve<T>();
+ var requiresContext = service as IRequiresRequestContext;
+ if (requiresContext != null)
+ {
+ requiresContext.RequestContext = this.RequestContext;
+ }
+ return service;
+ }
+
+ private IHttpRequest request;
+ protected virtual IHttpRequest Request
+ {
+ get { return request ?? (request = RequestContext.Get<IHttpRequest>()); }
+ }
+
+ private IHttpResponse response;
+ protected virtual IHttpResponse Response
+ {
+ get { return response ?? (response = RequestContext.Get<IHttpResponse>()); }
+ }
+
+ private ICacheClient cache;
+ public virtual ICacheClient Cache
+ {
+ get { return cache ?? (cache = TryResolve<ICacheClient>()); }
+ }
+
+ private IDbConnection db;
+ public virtual IDbConnection Db
+ {
+ get { return db ?? (db = TryResolve<IDbConnectionFactory>().Open()); }
+ }
+
+ private ISessionFactory sessionFactory;
+ public virtual ISessionFactory SessionFactory
+ {
+ get { return sessionFactory ?? (sessionFactory = TryResolve<ISessionFactory>()) ?? new SessionFactory(Cache); }
+ }
+
+ /// <summary>
+ /// Dynamic Session Bag
+ /// </summary>
+ private ISession session;
+ public virtual ISession Session
+ {
+ get
+ {
+ return session ?? (session = SessionFactory.GetOrCreateSession(Request, Response));
+ }
+ }
+
+ /// <summary>
+ /// Typed UserSession
+ /// </summary>
+ private object userSession;
+ protected virtual TUserSession SessionAs<TUserSession>()
+ {
+ if (userSession != null) return (TUserSession)userSession;
+ if (SessionKey != null)
+ userSession = Cache.Get<TUserSession>(SessionKey);
+ else
+ SessionFeature.CreateSessionIds();
+ var unAuthorizedSession = typeof(TUserSession).CreateInstance();
+ return (TUserSession)(userSession ?? (userSession = unAuthorizedSession));
+ }
+
+ /// <summary>
+ /// The UserAgent's SessionKey
+ /// </summary>
+ protected virtual string SessionKey
+ {
+ get
+ {
+ var sessionId = SessionFeature.GetSessionId();
+ return sessionId == null ? null : SessionFeature.GetSessionKey(sessionId);
+ }
+ }
+
+ public virtual void Dispose()
+ {
+ if (cache != null)
+ cache.Dispose();
+ if (db != null)
+ db.Dispose();
+ }
+ }
+
+}
Oops, something went wrong.

0 comments on commit c9f8d02

Please sign in to comment.