<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stein Lundbeck</title>
    <description>The latest articles on DEV Community by Stein Lundbeck (@sltech).</description>
    <link>https://dev.to/sltech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1508275%2F4287a388-b5d0-43b2-a96e-bc72ee5813a5.jpg</url>
      <title>DEV Community: Stein Lundbeck</title>
      <link>https://dev.to/sltech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sltech"/>
    <language>en</language>
    <item>
      <title>A way to configure an asp.net core site</title>
      <dc:creator>Stein Lundbeck</dc:creator>
      <pubDate>Tue, 21 May 2024 03:26:43 +0000</pubDate>
      <link>https://dev.to/sltech/a-way-to-configure-an-aspnet-core-site-45ol</link>
      <guid>https://dev.to/sltech/a-way-to-configure-an-aspnet-core-site-45ol</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I create one class per feature and all classes derive from AppFeatureBase.cs. All classes must set a value indicating what feature they represent.&lt;/p&gt;

&lt;p&gt;Sorry, but I can't get to mark the code, so it's a little hard to read.&lt;/p&gt;

&lt;p&gt;In Program.cs I add features by creating new features classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AppFeatureBase.cs&lt;/strong&gt;:&lt;br&gt;
`public interface IAppFeatureBase&lt;br&gt;
{&lt;br&gt;
    Features Type { get; }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;public abstract class AppFeatureBase : IAppFeatureBase&lt;br&gt;
{&lt;br&gt;
    private readonly Features _type;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected AppFeatureBase(Features type)
{
    _type = type;
}

public Features Type =&amp;gt; _type;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;Some types implementing &lt;em&gt;IAppFeatureBase&lt;/em&gt;:&lt;br&gt;
public interface IDefaultRoute&lt;br&gt;
{&lt;br&gt;
    string Route { get; }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;public sealed class DefaultRoute : AppFeatureBase, IAppFeatureBase, IDefaultRoute&lt;br&gt;
{&lt;br&gt;
    private readonly string _route;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public DefaultRoute(string controller, string action) : base(Features.DefaultRoute)
{
    _route = CoreStatics.GetBaseRoute(controller, action);
}

public string Route =&amp;gt; _route;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
&lt;code&gt;&lt;br&gt;
&lt;/code&gt;public interface ISSL&lt;br&gt;
{&lt;br&gt;
    int Port { get; }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;public sealed class SSL : AppFeatureBase, IAppFeatureBase, ISSL&lt;br&gt;
{&lt;br&gt;
    private readonly int _port = 443;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public SSL() : base(Features.SSL)
{
}

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

public int Port =&amp;gt; _port;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AssetsConfig.cs&lt;/strong&gt;:&lt;br&gt;
`public static class AssetsConfig&lt;br&gt;
{&lt;br&gt;
    public static void AddAssets(WebApplicationBuilder builder, params IAppFeatureBase[] features) =&amp;gt; AddAssets(builder, null, features);&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static void AddAssets(WebApplicationBuilder builder, Action&amp;lt;MvcOptions&amp;gt;? 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&amp;lt;IConfiguration&amp;gt;(builder.Configuration)
            .AddSingleton(cnf);
    }
    else
    {
        throw new ConfigSectionNotFoundException();
    }

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

    if (features.Exists(Features.Secure))
    {
        _ = builder.Services
            .AddTransient&amp;lt;ISecureAccessor, SecureAccessor&amp;gt;();
    }

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

    if (features.Exists(Features.Logging))
    {
        _ = builder.Services
            .AddTransient&amp;lt;IAssetsLogging, AssetsLogging&amp;gt;();
    }

    if (features.Exists(Features.SSL))
    {
        SSL ssl = (SSL)features.Single(feat =&amp;gt; feat.Type == Features.SSL);

        _ = builder.Services
           .AddHttpsRedirection(conf =&amp;gt;
           {
               conf.HttpsPort = ssl.Port;
           });
    }

    if (features.Exists(Features.Service))
    {
        foreach (Service feat in features.Where(feat =&amp;gt; feat.Type == Features.Service).Cast&amp;lt;Service&amp;gt;())
        {
            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 =&amp;gt; ft.Type == Features.Content))
        {
            switch (cnt.Feature)
            {
                case ContentFeature.Local:
                    _ = builder.Services
                        .AddTransient&amp;lt;ILocalizationAccessor, LocalizationAccessor&amp;gt;();
                    break;

                case ContentFeature.Value:
                    _ = builder.Services
                        .AddTransient&amp;lt;IContentAccessor, ContentAccessor&amp;gt;();
                    break;

                case ContentFeature.All:
                    _ = builder.Services
                        .AddTransient&amp;lt;ILocalizationAccessor, LocalizationAccessor&amp;gt;()
                        .AddTransient&amp;lt;IContentAccessor, ContentAccessor&amp;gt;();
                    break;
            }
        }
    }

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

    if (features.Exists(Features.Minify))
    {
        Minify mn = (Minify)features.Single(ft =&amp;gt; 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 =&amp;gt;
                {
                    opt.AllowCompressionInDevelopmentEnvironment = true;
                    opt.AllowMinificationInDevelopmentEnvironment = true;
                })
                .AddHtmlMinification(opt =&amp;gt;
                {
                    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 =&amp;gt;
        {
            opt.ClientId = config.ClientId;
            opt.ClientSecret = config.ClientSecret;
        });
    }

    WebApplication app = builder.Build();

    if (features.Exists(Features.Debug))
    {
        Debug db = (Debug)features.Single(feat =&amp;gt; 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 =&amp;gt; ft.Type == Features.GDPR);

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

    if (features.Exists(Features.Minify))
    {
        Minify mn = (Minify)features.Single(ft =&amp;gt; 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&amp;lt;SetCulture&amp;gt;();
    }

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

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

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

            _ = endpoints.MapBlazorHub();
        });
    }
    else
    {
        _ = app.UseEndpoints(opt =&amp;gt; opt.MapDefaultControllerRoute());
    }

    app.Run();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And this is how it looks in Program.cs&lt;/strong&gt;:&lt;br&gt;
`WebApplicationBuilder bld = WebApplication.CreateBuilder(args);&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Hope you like it :)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>aspnetcore</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
