ASP.NET 扩展支持

本文介绍将如何在Nebula中实现与ASP.NET相关的一些扩展场景:

  • 添加 MVC Filter
  • 调整 MVC UrlRouting
  • 引入新组件
  • 添加 Middleware/RequestDelegate



添加 MVC Filter

你可以开发一个符合 ASP.NET 要求的 MVC Filter,例如:

public class MyActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // 过程省略
    }

然后,在NebulaBehavior的继承类中注册它,例如:

public class MyNebulaBehavior : NebulaBehavior
{
    public override void SetMvcOptions(MvcOptions opt)
    {
        opt.Filters.Add(typeof(MyActionFilter));
    }

最后,在 AppStartOption 中引 MyNebulaBehavior,例如:

public class Program
{
    public static void Main(string[] args)
    {
        AppStartOption startOption = new AppStartOption {
            NebulaBehavior = new MyNebulaBehavior()
        };
        
        AppStartup.RunAsWebsite("XDemo.WebSiteApp", args, startOption);
    }        
}

注意:继承NebulaBehavior的子类必须使用上面的方式来使用它,本文后面将不再重复指出。



调整 MVC UrlRouting

以下代码是 NebulaBehavior 中的 UrlRouting 默认实现:

public virtual void ConfigureUrlRouting(WebApplication app)
{
    app.UseRouting();

    app.MapControllers();

    if( AppStartup.StartOption.EnableDefaultHomePage ) {
        app.MapGet("/", ShowHomePage);
    }
}

说明:

  • 建议不要覆盖重写这个方法,可以在调用 base 方法的 before, after 中添加路由规则
  • 或者采用 RequestDelegate 的方式来拦截请求实现路由规则



引入新组件

首先要清楚,在 ASP.NET Core 的设计中,一个组件的引入被分为2个阶段:

  • 在Ioc容器中注入服务(组件),对应 Startup.ConfigureServices 方法
  • 配置ASP.NET管道(可选步骤),对应 Startup.Configure 方法

在Nebula框架中,不再需要每个项目再维护Startup类型,
只需要开发一个新类型从BaseAppStarter继承,然后选择要实现的方法即可。

BaseAppStarter 提供以下方法用于定制ASPNETCORE的启动过程:

  • PreNebulaInit:默认行为:什么也不做(等着你来填空)
  • PostNebulaInit:默认行为:什么也不做(等着你来填空)
  • PreConfigureServices:默认行为:什么也不做(等着你来填空)
  • PostConfigureServices:默认行为:什么也不做(等着你来填空)
  • PreConfigureWeb:默认行为:什么也不做(等着你来填空)
  • PostConfigureWeb:默认行为:什么也不做(等着你来填空)
  • PreBuildWebHost:默认行为:什么也不做(等着你来填空)
  • PostBuildWebHost:默认行为:什么也不做(等着你来填空)
  • PreApplicationInit:默认行为:什么也不做(等着你来填空)
  • PostApplicationInit:默认行为:什么也不做(等着你来填空)

这里以引入 SignalR 为例演示整个开发过程,完整示例如下:

public class ImportSignalrStarter : BaseAppStarter
{
    public override void ConfigureServices(IServiceCollection services)
    {
        string conn = ConfigClient.Instance.GetSetting("Redis.Connection");

        services.AddSignalR(option => {
            option.EnableDetailedErrors = true;
            option.MaximumReceiveMessageSize = 1024 * 1024;
            option.HandshakeTimeout = TimeSpan.FromSeconds(60);
            option.StreamBufferCapacity = 5000;
        }).AddNewtonsoftJsonProtocol()
            .AddStackExchangeRedis(conn);
    }

    public override void PostConfigureWeb(WebApplication app)
    {
        app.UseEndpoints(endpoints => {
            endpoints.MapHub<NotifyHub>("/v20/api/ws/notify", option => {
                option.Transports = HttpTransportType.WebSockets;
            });
        });
    }
}

说明:

  • 一个项目中,可以有多个 BaseAppStarter 的继承类
  • BaseAppStarter 的继承类必须是 public 的可见性,框架会自动识别并调用。
  • BaseAppStarter 的继承类可以出现在类库中,用来构造完整的业务模块(支持所谓的模块化开发)



添加 Middleware/RequestDelegate

开发 MyRequestDelegate,示例代码如下:

public class MyRequestDelegate
{
    private readonly Microsoft.AspNetCore.Http.RequestDelegate _next;

    private long _requestCount = 0;

    public MyRequestDelegate(Microsoft.AspNetCore.Http.RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(Microsoft.AspNetCore.Http.HttpContext httpContext)
    {
        long count = Interlocked.Increment(ref _requestCount);

        httpContext.Response.Headers.Add("x-ReuestCount", count.ToString());

        await _next(httpContext);
    }
}

开发一个新类型,从 BaseAppStarter 继承,

public class XxxAppStarter : BaseAppStarter
{
    public override void PostConfigureWeb(WebApplication app)
    {
        app.UseMiddleware<MyRequestDelegate>();
    }
}

这样就可以了,框架会自动识别!