Skip to content

Commit

Permalink
Also add support for registering global handlers after AppHost is ini…
Browse files Browse the repository at this point in the history
…tialized
  • Loading branch information
mythz committed Nov 11, 2021
1 parent 2527d2f commit ab97d77
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 35 deletions.
101 changes: 66 additions & 35 deletions src/ServiceStack/AppHostBase.NetCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using ServiceStack.Host;
using ServiceStack.Host.NetCore;
using ServiceStack.Host.Handlers;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
Expand All @@ -37,10 +36,10 @@ namespace ServiceStack
public abstract class AppHostBase : ServiceStackHost, IAppHostNetCore, IConfigureServices, IRequireConfiguration
{
protected AppHostBase(string serviceName, params Assembly[] assembliesWithServices)
: base(serviceName, assembliesWithServices)
: base(serviceName, assembliesWithServices)
{
Platforms.PlatformNetCore.HostInstance = this;

//IIS Mapping / sometimes UPPER CASE https://serverfault.com/q/292335
var iisPathBase = Environment.GetEnvironmentVariable("ASPNETCORE_APPL_PATH");
if (!string.IsNullOrEmpty(iisPathBase) && !iisPathBase.Any(char.IsLower))
Expand All @@ -49,6 +48,7 @@ protected AppHostBase(string serviceName, params Assembly[] assembliesWithServic
}

private string pathBase;

public override string PathBase
{
get => pathBase ?? Config?.HandlerFactoryPath;
Expand All @@ -58,7 +58,7 @@ public override string PathBase
{
if (value[0] != '/')
throw new Exception("PathBase must start with '/'");

pathBase = value.TrimEnd('/');
}
else
Expand All @@ -73,7 +73,7 @@ public override string PathBase
public IApplicationBuilder App => app;
public IServiceProvider ApplicationServices => app?.ApplicationServices;

public Func<NetCoreRequest,Task> BeforeNextMiddleware { get; set; }
public Func<NetCoreRequest, Task> BeforeNextMiddleware { get; set; }

public virtual void Bind(IApplicationBuilder app)
{
Expand Down Expand Up @@ -130,14 +130,15 @@ public override string GetWebRootPath()
}

private IWebHostEnvironment env;
public IWebHostEnvironment HostingEnvironment => env ??= app?.ApplicationServices.GetService<IWebHostEnvironment>();

public IWebHostEnvironment HostingEnvironment =>
env ??= app?.ApplicationServices.GetService<IWebHostEnvironment>();

public override void OnConfigLoad()
{
base.OnConfigLoad();
if (app != null)
{

Config.DebugMode = HostingEnvironment.IsDevelopment();
Config.HandlerFactoryPath = PathBase?.TrimStart('/');

Expand All @@ -149,6 +150,7 @@ public override void OnConfigLoad()
//Set VirtualFiles to point to ContentRootPath (Project Folder)
VirtualFiles = new FileSystemVirtualFiles(HostingEnvironment.ContentRootPath);
}

RegisterLicenseFromAppSettings(AppSettings);
InjectRequestContext = app?.ApplicationServices.GetService<IHttpContextAccessor>() != null;
}
Expand All @@ -163,9 +165,9 @@ public static void RegisterLicenseFromAppSettings(IAppSettings appSettings)
LicenseUtils.RegisterLicense(licenceKeyText);
}
}

public Func<HttpContext, Task<bool>> NetCoreHandler { get; set; }

public bool InjectRequestContext { get; set; }

public virtual async Task ProcessRequest(HttpContext context, Func<Task> next)
Expand All @@ -176,7 +178,7 @@ public virtual async Task ProcessRequest(HttpContext context, Func<Task> next)
if (handled)
return;
}

//Keep in sync with Kestrel/AppSelfHostBase.cs
var operationName = context.Request.GetOperationName() ?? "Home"; //already decoded
var pathInfo = context.Request.Path.HasValue
Expand All @@ -189,7 +191,8 @@ public virtual async Task ProcessRequest(HttpContext context, Func<Task> next)
//IIS Reports "ASPNETCORE_APPL_PATH" in UPPER CASE
var includedInPathInfo = pathInfo.IndexOf(mode, StringComparison.OrdinalIgnoreCase) == 1;
var includedInPathBase = context.Request.PathBase.HasValue &&
context.Request.PathBase.Value.IndexOf(mode, StringComparison.OrdinalIgnoreCase) == 1;
context.Request.PathBase.Value.IndexOf(mode,
StringComparison.OrdinalIgnoreCase) == 1;
if (!includedInPathInfo && !includedInPathBase)
{
await next().ConfigAwait();
Expand All @@ -206,11 +209,11 @@ public virtual async Task ProcessRequest(HttpContext context, Func<Task> next)
IResponse httpRes;
IHttpHandler handler;

try
try
{
httpReq = new NetCoreRequest(context, operationName, RequestAttributes.None, pathInfo);
httpReq = new NetCoreRequest(context, operationName, RequestAttributes.None, pathInfo);
httpReq.RequestAttributes = httpReq.GetAttributes() | RequestAttributes.Http;

httpRes = httpReq.Response;
handler = HttpHandlerFactory.GetHandler(httpReq);

Expand All @@ -225,7 +228,7 @@ public virtual async Task ProcessRequest(HttpContext context, Func<Task> next)
await holdNext().ConfigAwait();
};
}
}
}
catch (Exception ex) //Request Initialization error
{
var logFactory = context.Features.Get<ILoggerFactory>();
Expand Down Expand Up @@ -309,13 +312,14 @@ public static IRequest GetOrCreateRequest(HttpContext httpContext)
if (httpContext != null)
{
if (httpContext.Items.TryGetValue(Keywords.IRequest, out var oRequest))
return (IRequest) oRequest;
return (IRequest)oRequest;

var req = httpContext.ToRequest();
httpContext.Items[Keywords.IRequest] = req;

return req;
}

return null;
}

Expand All @@ -329,7 +333,7 @@ protected override void Dispose(bool disposing)
/// Requires using ModularStartup
/// </summary>
/// <param name="services"></param>
public virtual void Configure(IServiceCollection services) {}
public virtual void Configure(IServiceCollection services) { }

public IConfiguration Configuration { get; set; }
}
Expand All @@ -347,28 +351,48 @@ public interface IAppHostNetCore : IAppHost, IRequireConfiguration
public static class NetCoreAppHostExtensions
{
/// <summary>
/// Register static callbacks fired just after AppHost.Configure()
/// Register static callbacks fired just after AppHost.Configure()
/// <param name="beforeConfigure">Register static callbacks fired just before AppHost.Configure()</param>
/// <param name="afterConfigure">Register static callbacks fired just after AppHost.Configure()</param>
/// <param name="afterPluginsLoaded">Register static callbacks fired just after plugins are loaded</param>
/// <param name="afterAppHostInit">Register static callbacks fired after the AppHost is initialized</param>
/// </summary>
public static IWebHostBuilder ConfigureAppHost(this IWebHostBuilder builder,
Action<ServiceStackHost> configure=null, Action<ServiceStackHost> afterConfigure=null,
Action<ServiceStackHost> afterPluginsLoaded=null)
public static IWebHostBuilder ConfigureAppHost(this IWebHostBuilder builder,
Action<ServiceStackHost> beforeConfigure = null,
Action<ServiceStackHost> afterConfigure = null,
Action<ServiceStackHost> afterPluginsLoaded = null,
Action<ServiceStackHost> afterAppHostInit = null)
{
if (configure != null)
ServiceStackHost.GlobalBeforeConfigure.Add(configure);
if (beforeConfigure != null)
ServiceStackHost.GlobalBeforeConfigure.Add(beforeConfigure);
if (afterConfigure != null)
ServiceStackHost.GlobalAfterConfigure.Add(afterConfigure);
if (afterPluginsLoaded != null)
ServiceStackHost.GlobalAfterPluginsLoaded.Add(afterPluginsLoaded);
if (afterAppHostInit != null)
ServiceStackHost.GlobalAfterAppHostInit.Add(afterAppHostInit);
return builder;
}

public static IConfiguration GetConfiguration(this IAppHost appHost) => ((IAppHostNetCore)appHost).Configuration;

public static IConfiguration GetConfiguration(this IAppHost appHost) =>
((IAppHostNetCore)appHost).Configuration;

public static IApplicationBuilder GetApp(this IAppHost appHost) => ((IAppHostNetCore)appHost).App;
public static IServiceProvider GetApplicationServices(this IAppHost appHost) => ((IAppHostNetCore)appHost).App.ApplicationServices;
public static IWebHostEnvironment GetHostingEnvironment(this IAppHost appHost) => ((IAppHostNetCore)appHost).HostingEnvironment;
public static bool IsDevelopmentEnvironment(this IAppHost appHost) => appHost.GetHostingEnvironment().EnvironmentName == "Development";
public static bool IsStagingEnvironment(this IAppHost appHost) => appHost.GetHostingEnvironment().EnvironmentName == "Staging";
public static bool IsProductionEnvironment(this IAppHost appHost) => appHost.GetHostingEnvironment().EnvironmentName == "Production";

public static IServiceProvider GetApplicationServices(this IAppHost appHost) =>
((IAppHostNetCore)appHost).App.ApplicationServices;

public static IWebHostEnvironment GetHostingEnvironment(this IAppHost appHost) =>
((IAppHostNetCore)appHost).HostingEnvironment;

public static bool IsDevelopmentEnvironment(this IAppHost appHost) =>
appHost.GetHostingEnvironment().EnvironmentName == "Development";

public static bool IsStagingEnvironment(this IAppHost appHost) =>
appHost.GetHostingEnvironment().EnvironmentName == "Staging";

public static bool IsProductionEnvironment(this IAppHost appHost) =>
appHost.GetHostingEnvironment().EnvironmentName == "Production";

public static IApplicationBuilder UseServiceStack(this IApplicationBuilder app, AppHostBase appHost)
{
Expand Down Expand Up @@ -398,16 +422,23 @@ public static IHttpRequest ToRequest(this HttpContext httpContext, string operat
public static T TryResolve<T>(this IServiceProvider provider) => provider.GetService<T>();
public static T Resolve<T>(this IServiceProvider provider) => provider.GetRequiredService<T>();

public static IHttpRequest ToRequest(this HttpRequest request, string operationName = null) => request.HttpContext.ToRequest();
public static IHttpRequest ToRequest(this HttpRequest request, string operationName = null) =>
request.HttpContext.ToRequest();

public static T TryResolveScoped<T>(this IRequest req) => (T)((IServiceProvider)req).GetService(typeof(T));
public static object TryResolveScoped(this IRequest req, Type type) => ((IServiceProvider)req).GetService(type);
public static T ResolveScoped<T>(this IRequest req) => (T)((IServiceProvider) req).GetRequiredService(typeof(T));
public static object ResolveScoped(this IRequest req, Type type) => ((IServiceProvider)req).GetRequiredService(type);
public static T ResolveScoped<T>(this IRequest req) => (T)((IServiceProvider)req).GetRequiredService(typeof(T));

public static object ResolveScoped(this IRequest req, Type type) =>
((IServiceProvider)req).GetRequiredService(type);

public static IServiceScope CreateScope(this IRequest req) => ((IServiceProvider)req).CreateScope();
public static IEnumerable<object> GetServices(this IRequest req, Type type) => ((IServiceProvider)req).GetServices(type);

public static IEnumerable<object> GetServices(this IRequest req, Type type) =>
((IServiceProvider)req).GetServices(type);

public static IEnumerable<T> GetServices<T>(this IRequest req) => ((IServiceProvider)req).GetServices<T>();
}
}

#endif
#endif
6 changes: 6 additions & 0 deletions src/ServiceStack/ServiceStackHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ void RunConfigure(object instance)

Plugins.ForEach(RunAfterInitAppHost);
configInstances.ForEach(RunAfterInitAppHost);
GlobalAfterAppHostInit.Each(RunManagedAction);

ReadyAt = DateTime.UtcNow;

Expand Down Expand Up @@ -650,6 +651,11 @@ public virtual ServiceStackHost Start(string urlBase)
/// </summary>
public List<Action<IAppHost>> AfterInitCallbacks { get; set; }

/// <summary>
/// Register static callbacks fired after the AppHost is initialized
/// </summary>
public static List<Action<ServiceStackHost>> GlobalAfterAppHostInit { get; } = new();

/// <summary>
/// Register callbacks that's fired when AppHost is disposed
/// </summary>
Expand Down

0 comments on commit ab97d77

Please sign in to comment.