A way to configure an asp.net core site

sltech

Stein Lundbeck

Posted on May 21, 2024

A way to configure an asp.net core site

I found myself coding a lot of the same code when creating and configuring ASP.NET Core sites, so I made this system to gather all the config code in one class and simplify adding and removing features.

I create one class per feature and all classes derive from AppFeatureBase.cs. All classes must set a value indicating what feature they represent.

Sorry, but I can't get to mark the code, so it's a little hard to read.

In Program.cs I add features by creating new features classes.

AppFeatureBase.cs:
`public interface IAppFeatureBase
{
Features Type { get; }
}

public abstract class AppFeatureBase : IAppFeatureBase
{
private readonly Features _type;

protected AppFeatureBase(Features type)
{
    _type = type;
}

public Features Type => _type;
Enter fullscreen mode Exit fullscreen mode

}`

Some types implementing IAppFeatureBase:
public interface IDefaultRoute
{
string Route { get; }
}

public sealed class DefaultRoute : AppFeatureBase, IAppFeatureBase, IDefaultRoute
{
private readonly string _route;

public DefaultRoute(string controller, string action) : base(Features.DefaultRoute)
{
    _route = CoreStatics.GetBaseRoute(controller, action);
}

public string Route => _route;
Enter fullscreen mode Exit fullscreen mode

}

public interface ISSL
{
int Port { get; }
}

public sealed class SSL : AppFeatureBase, IAppFeatureBase, ISSL
{
private readonly int _port = 443;

public SSL() : base(Features.SSL)
{
}

public SSL(int port) : base(Features.SSL)
{
    _port = port;
}

public int Port => _port;
Enter fullscreen mode Exit fullscreen mode

}`

AssetsConfig.cs:
`public static class AssetsConfig
{
public static void AddAssets(WebApplicationBuilder builder, params IAppFeatureBase[] features) => AddAssets(builder, null, features);

public static void AddAssets(WebApplicationBuilder builder, Action<MvcOptions>? mvcOptions, params IAppFeatureBase[] features)
{
    if (builder.Configuration.GetSection(AssetsStatics.ConfigSectionName).Exists())
    {
        IAssetsConfig cnf = new Assets.AssetsConfig(builder.Configuration);
        builder.Configuration.Bind(AssetsStatics.ConfigSectionName, cnf);

        _ = builder.Services
            .AddOptions()
            .Configure<IConfiguration>(builder.Configuration)
            .AddSingleton(cnf);
    }
    else
    {
        throw new ConfigSectionNotFoundException();
    }

    _ = builder.Services
        .AddHttpContextAccessor()
        .AddDbContext<AssetsDbContext>(opt => opt.UseSqlServer(AssetsStatics.ConnectionString))
        .AddDbContext<AssetsClientDbContext>(opt => opt.UseSqlServer(AssetsStatics.ClientConnectionString))
        .AddTransient<ISiteHelper, SiteHelper>()
        .AddTransient<IAssetsDbContext, AssetsDbContext>()
        .AddTransient<IAssetsClientDbContext, AssetsClientDbContext>()
        .AddTransient<IDbContextAccessor, DbContextAccessor>()
        .AddTransient<IActionContextAccessor, ActionContextAccessor>()
        .AddTransient<IAssetsConfigAccessor, AssetsConfigAccessor>()
        .AddTransient<IUtilityHelper, UtilityHelper>()
        .AddTransient<IUrlHelperFactory, UrlHelperFactory>()
        .AddTransient<IRepoFactory, RepoFactory>()
        .AddTransient<IExpressionFactory, ExpressionFactory>()
        .AddTransient<ITagHelperRepo, TagHelperRepo>()
        .AddTransient<ISiteHelper, SiteHelper>()
        .Configure<CookiePolicyOptions>(opt =>
        {
            opt.MinimumSameSitePolicy = SameSiteMode.Lax;
        })
        .AddSession(opt =>
        {
            opt.IdleTimeout = TimeSpan.FromMinutes(CoreStatics.SessionTimeout);
        });

    if (features.Exists(Features.Secure))
    {
        _ = builder.Services
            .AddTransient<ISecureAccessor, SecureAccessor>();
    }

    if (features.Exists(Features.FileUpload))
    {
        IFileUpload fu = (FileUpload)features.Single(feat => feat.Type == Features.FileUpload);
        _ = builder.Services
            .AddSingleton(new PhysicalFileProvider(fu.FolderPath));
    }

    if (features.Exists(Features.Logging))
    {
        _ = builder.Services
            .AddTransient<IAssetsLogging, AssetsLogging>();
    }

    if (features.Exists(Features.SSL))
    {
        SSL ssl = (SSL)features.Single(feat => feat.Type == Features.SSL);

        _ = builder.Services
           .AddHttpsRedirection(conf =>
           {
               conf.HttpsPort = ssl.Port;
           });
    }

    if (features.Exists(Features.Service))
    {
        foreach (Service feat in features.Where(feat => feat.Type == Features.Service).Cast<Service>())
        {
            switch (feat.Scope)
            {
                case ServiceScopes.Singleton:
                    builder.Services
                        .AddSingleton(feat.Interface, feat.Implementation);
                    break;

                case ServiceScopes.Scoped:
                    builder.Services
                        .AddScoped(feat.Interface, feat.Implementation);
                    break;

                case ServiceScopes.Transient:
                    builder.Services
                        .AddTransient(feat.Interface, feat.Implementation);
                    break;
            }
        }
    }

    if (features.Exists(Features.Blazor))
    {
        _ = builder.Services
            .AddServerSideBlazor();
    }

    if (features.Exists(Features.Content))
    {
        foreach (ApplicationFeatures.Content cnt in features.Where(ft => ft.Type == Features.Content))
        {
            switch (cnt.Feature)
            {
                case ContentFeature.Local:
                    _ = builder.Services
                        .AddTransient<ILocalizationAccessor, LocalizationAccessor>();
                    break;

                case ContentFeature.Value:
                    _ = builder.Services
                        .AddTransient<IContentAccessor, ContentAccessor>();
                    break;

                case ContentFeature.All:
                    _ = builder.Services
                        .AddTransient<ILocalizationAccessor, LocalizationAccessor>()
                        .AddTransient<IContentAccessor, ContentAccessor>();
                    break;
            }
        }
    }

    _ = builder.Services
        .AddDistributedMemoryCache()
        .AddSession(opt =>
        {
            opt.Cookie.Name = CoreStatics.SessionCookieName;
            opt.Cookie.HttpOnly = false;
        })
        .AddControllersWithViews(mvcOptions);

    if (features.Exists(Features.Minify))
    {
        Minify mn = (Minify)features.Single(ft => ft.Type == Features.Minify);

        switch (mn.Target)
        {
            case MinifyTargets.Production:
                if (builder.Environment.IsProduction())
                {
                    AddToServices();
                }
                break;

            case MinifyTargets.Both:
                AddToServices();
                break;
        }

        void AddToServices()
        {
            _ = builder.Services
                .AddWebMarkupMin(opt =>
                {
                    opt.AllowCompressionInDevelopmentEnvironment = true;
                    opt.AllowMinificationInDevelopmentEnvironment = true;
                })
                .AddHtmlMinification(opt =>
                {
                    opt.MinificationSettings.RemoveRedundantAttributes = true;
                    opt.MinificationSettings.RemoveHttpsProtocolFromAttributes = true;
                    opt.MinificationSettings.RemoveHttpsProtocolFromAttributes = true;
                });
        }
    }

    if (features.Exists(Features.GoogleAuth))
    {
        GoogleConfigSection config = new();
        builder.Configuration.GetSection("Google").Bind(config);

        _ = builder.Services.AddAuthentication().AddGoogle(opt =>
        {
            opt.ClientId = config.ClientId;
            opt.ClientSecret = config.ClientSecret;
        });
    }

    WebApplication app = builder.Build();

    if (features.Exists(Features.Debug))
    {
        Debug db = (Debug)features.Single(feat => feat.Type == Features.Debug);
        db.Environment = app.Environment;

        if (db.Environment.IsDevelopment() || db.IgnoreEnvironment)
        {
            _ = app
                .UseDeveloperExceptionPage();
        }
    }

    if (features.Exists(Features.SSL))
    {
        _ = app
            .UseHttpsRedirection();
    }

    if (features.Exists(Features.GDPR))
    {
        GDPR feat = (GDPR)features.Single(ft => ft.Type == Features.GDPR);

        _ = app.Map("/AssetsAPI/SetGDPR", async context =>
        {
            context.Response.Cookies.Append(CoreStatics.GDPRCookieName, CoreStatics.GDPRCookieName);
        });
    }

    if (features.Exists(Features.Minify))
    {
        Minify mn = (Minify)features.Single(ft => ft.Type == Features.Minify);

        switch (mn.Target)
        {
            case MinifyTargets.Production:
                if (builder.Environment.IsProduction())
                {
                    _ = app.UseWebMarkupMin();
                }
                break;

            case MinifyTargets.Both:
                _ = app.UseWebMarkupMin();
                break;
        }
    }

    _ = app
        .UseStaticFiles()
        .UseRouting()
        .UseSession()
        .UseCookiePolicy(new CookiePolicyOptions
        {
            MinimumSameSitePolicy = SameSiteMode.Lax
        });

    if (features.Exists(Features.Content))
    {
        app
            .UseMiddleware<SetCulture>();
    }

    if (features.Exists(Features.DefaultRoute) && !features.Exists(Features.Blazor))
    {
        DefaultRoute route = (DefaultRoute)features.Single(feat => feat.Type == Features.DefaultRoute);

        _ = app.UseEndpoints(end =>
        {
            _ = end.MapControllerRoute(name: "default", pattern: route.Route);
        });
    }
    else if (features.Exists(Features.Blazor))
    {
        IBlazor blzr = (Blazor)features.Single(ft => ft.Type == Features.Blazor);

        _ = app.UseEndpoints(endpoints =>
        {
            _ = endpoints.MapControllerRoute(
                name: "default",
                pattern: blzr.Pattern);

            _ = endpoints.MapBlazorHub();
        });
    }
    else
    {
        _ = app.UseEndpoints(opt => opt.MapDefaultControllerRoute());
    }

    app.Run();
}
Enter fullscreen mode Exit fullscreen mode

}`

And this is how it looks in Program.cs:
`WebApplicationBuilder bld = WebApplication.CreateBuilder(args);

AssetsConfig.AddAssets(bld,
new GDPR("LE", "Index"),
new DefaultRoute("LE", "Index"),
new Service(typeof(IDataRepo), typeof(DataRepo)),
new Service(typeof(IPictureListRepo), typeof(PictureListRepo)),
new Service(typeof(ILESiteHelper), typeof(LESiteHelper)),
new Minify(MinifyTargets.Production));`

I have a framework called Assets which all my sites derives from, so this way I can easily add new features to Assets and add them to sites without having to think about adding all services in correct order.

Hope you like it :)

💖 💪 🙅 🚩
sltech
Stein Lundbeck

Posted on May 21, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related