European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core Hosting - HostForLIFE.eu :: Javascript, CSS, HTML in ASP.NET Core

clock April 12, 2019 09:45 by author Scott

This article will teach you how to include and customize the use of static files in ASP .NET Core web applications. It is not a tutorial on front-end web development.

If your ASP .NET Core web app has a front end – whether it’s a collection of MVC Views or a Single-Page Application (SPA) – you will need to include static files in your application. This includes (but is not limited to): JavaScript, CSS, HTML and various image files.

When you create a new web app using one of the built-in templates (MVC or Razor Pages), you should see a “wwwroot” folder in the Solution Explorer. This points to a physical folder in your file system that contains the same files seen from Visual Studio. However, this location can be configured, you can have multiple locations with static files, and you can enable/disable static files in your application if desired. In fact, you have to “opt in” to static files in your middleware pipeline.

Configuring Static Files via Middleware

Let’s start by observing the Startup.cs configuration file. We’ve seen this file several times throughout this blog series. In the Configure() method, you’ll find the familiar method call to enable the use of static files.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   ...
   app.UseStaticFiles();
   ...
   app.UseMvc(...);
}

This call to app.UseStaticFiles() ensures that static files can be served from the designated location, e.g. wwwroot.

It’s useful to note the placement of this line of code. It appears before app.UseMvc(), which is very important. This ensures that static file requests can be processed and sent back to the web browser without having to touch the MVC middleware. This becomes even more important when authentication is used.

In the code below, you can see the familiar call to app.UseStaticFiles() once again. However, there is also a call to app.UseAuthentication(). It’s important for the authentication call to appear after the call to use static files. This ensure that the authentication process isn’t triggered when it isn’t needed.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   ...
   app.UseStaticFiles();
   ...
   app.UseAuthentication();
   ...
   app.UseMvc(...);
}

By using the middleware pipeline in this way, you can “short-circuit” the pipeline when a request has been fulfilled by a specific middleware layer. If a static file has been successfully served using the Static Files middleware, it prevents the next layers of middleware (i.e. authentication, MVC) from processing the request.

Customizing Locations for Static Files

It may be convenient to have the default web templates create a location for your static files and also enable the use of those static files. As you’ve already seen, enabling static files isn’t magic. Removing the call to app.useStaticFiles() will disable static files from being served. In fact, the location for static files isn’t magic either.

public class Program
{
   ...
   public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
      WebHost.CreateDefaultBuilder(args)
         .UseStartup<Startup>();
}

Behind the scenes, this method call sets the “content root” to the current directory, which contains the “wwwroot” folder, your project’s “web root”. These can both be customized.

WebHost.CreateDefaultBuilder(args).UseContentRoot("c:\\<content-root>")

WebHost.CreateDefaultBuilder(args).UseWebRoot("public")

You may also use the call to app.UseStaticFiles() to customize an alternate location to serve static files. This allows you to serve additional static files from a location outside of the designated web root.

...
using Microsoft.Extensions.FileProviders;
using System.IO;
...
public void Configure(IApplicationBuilder app)
{
   ...
   app.UseStaticFiles(new StaticFileOptions
   {
      FileProvider = new PhysicalFileProvider
         Path.Combine(env.ContentRootPath, "AltStaticRoot")),
         RequestPath = "/AltStaticFiles"
   });
}

Wait a minute… why does it look like there are two alternate locations for static files? There is a simple explanation:

  • In the call to Path.Combine(), the “AltStaticRoot” is an actual folder in your current directory. This Path class and its Combine() method are available in the System.IO namespace.
  • The “AltStaticFiles” value for RequestPath is used as a root-level “virtual folder” from which images can be served. The PhysicalFileProvider class is available in the Microsoft.Extensions.FileProviders namespace.

The following markup may be used in a .cshtml file to refer to an image, e.g. MyImage01.png:

<img src="~/AltStaticFiles/MyImages/MyImage01.png" />

The screenshot below shows an example of an image loaded from an alternate location.

The screenshot below shows a web browser displaying such an image.

Preserving CDN Integrity

When you use a CDN (Content Delivery Network) to serve common CSS and JS files, you need to ensure that the integrity of the source code is reliable. You can rest assured that ASP .NET Core has already solved this problem for you in its built-in templates.

<environment include="Development">
 <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</environment>

<environment exclude="Development">
 <link
   rel="stylesheet"
   href=
https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css
   asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
   asp-fallback-test-class="sr-only"
   asp-fallback-test-property="position"
   asp-fallback-test-value="absolute"
   crossorigin="anonymous"
   integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE="/>
</environment>

Right away, you’ll notice that there are two conditional <environment> blocks in the above markup. The first block is used only during development, in which the bootstrap CSS file is obtained from your local copy. When not in development (e.g. staging, production, etc), the bootstrap CSS file is obtained from a CDN, e.g. CloudFlare.

You could use an automated hash-generation tool to generate the SRI (Subresource Integrity) hash values, but you would have to manually copy the value into your code. You can try out the relatively-new LibMan (aka Library Manager) for easily adding and updating your client-side libraries.

LibMan (aka Library Manager)

The easiest way to use LibMan is to use the built-in features available in Visual Studio. Using LibMan using the IDE is as easy as launching it from Solution Explorer. Specify the provider from the library you want, and any specific files you want from that library.

In the popup that appears, select/enter the following:

  • Provider: choose from cdnjs, filesystem, unpkg
  • Library search term, e.g. @aspnet/signalr@1… pick latest stable if desired
  • Files: At a minimum, choose specific files, e.g. signalr.js and/or its minified equivalent

 

For more on LibMan (using VS or CLI), check out the official docs:

Use LibMan with ASP.NET Core in Visual Studio: https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/libman-vs

Use the LibMan command-line interface (CLI): https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/libman-cli

Library Manager: Client-side content manager for web apps: https://devblogs.microsoft.com/aspnet/library-manager-client-side-content-manager-for-web-apps/

In any case, using LibMan will auto-populate a “libman.json” manifest file, which you can also inspect and edit manually.

{
  "version": "1.0",
  "defaultProvider": "unpkg",
  "libraries": [
    {
      "library": "@aspnet/signalr@1.1.0",
      "destination": "wwwroot/lib/signalr/",
      "files": [
        "dist/browser/signalr.js",
        "dist/browser/signalr.min.js"
      ]
    }
  ]
}

What About NPM or WebPack?

If you’ve gotten this far, you may be wondering: “hey, what about NPM or WebPack?”

It’s good to be aware that LibMan is a not a replacement for your existing package management systems. In fact, the Single-Page Application templates in Visual Studio (for Angular and React) currently use npm and WebPack. LibMan simply provides a lightweight mechanism to include client-side libraries from external location.

 



European ASP.NET Core Hosting :: RESTful WebAPI With Onion Architecture

clock April 9, 2019 11:29 by author Peter

Hello friends, here I will show you how to create a WebApi with the following characteristics:

  • ASP.Core 2.1
  • EntityFramework
  • FluentValidation
  • Nlogger
  • Swagger
  • Jwt

Let's start. First create an empty project, then add the following folders:

  • Application
  • Domain
  • Service
  • Infrastructure

Then in the Domain folder, we create a library project Net.Core 2.1 with the name WebApi.Domain add the following dependencies

FluentValidation.AspNetCore

In this project, we add the following folders:

  • Dtos
  • Entities
  • Interfaces

In the Entities folder, we create the BaseEntity class:

    namespace WebApi.Domain.Entities 
    { 
        public abstract class BaseEntity 
        { 
            public virtual int Id { get; set; } 
        } 
    } 

Our project classes will inherit the field Id from this abstract class (if you want you can add other fields like CreatedAt or CreatedBy).

Then we create the Country class with the properties that defines a Country.

    namespace WebApi.Domain.Entities 
    { 
        public class Country : BaseEntity 
        { 
            public string Name { get; set; } 
            public int Population { get; set; } 
            public decimal Area { get; set; } 
            public string ISO3166 { get; set; } 
            public string DrivingSide { get; set; } 
            public string Capital { get; set; } 
     
        } 
    } 

Now to make the exercise more interesting, we are going to assume that we do not want to expose all the Country class. In the Dtos folder, we create the following CountryDensityDTO class.

    using System; 
    using WebApi.Domain.Entities; 
     
    namespace WebApi.Domain.Dtos 
    { 
        public class CountryDensityDTO : BaseEntity 
        { 
            public string Name { get; set; } 
            public string Capital { get; set; } 
            public decimal Area { get; set; } 
            public int Population { get; set; } 
     
     
            public int Populationdensity 
            { 
                get 
                { 
                    return Decimal.ToInt32(Population / Area); 
                } 
            } 
        } 
    }

This class exposes Name, Capital Area, Population and a calculated field Populationdensity.
Now we will continue with the Infrastructure layer and then we will finish the missing parts.We go to the Infrastructure folder and create a library Net.Core 2.1. We name it WebApi.Infrastructure.Data.

We add the following Packages:

  • Microsoft.EntityFrameworkCore.SqlServer 2.1.4
  • Microsoft.EntityFrameworkCore.Tools 2.1.4
  • Microsoft.Extensions.Identity.Stores 2.1.1
  • Microsoft.VisualStudio.Web.CodeGeneration.Design 2.1.5
  • Add Project reference WebApi.Domain 

We create the following folders:

  • Context
  • EntityDbMapping
  • Repository

In the Context Folder, we add the SqlServerContext class. We refer to our Country entity with DbSet to work with the database. As we work with CodeFirst approach, we will create a mapping for our entity Country in the database.
"modelBuilder.Entity<Country>(new CountryMap().Configure);"

Optionally, in this part we can also add seed data when creating a table.

using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 
using Microsoft.EntityFrameworkCore; 
using WebApi.Domain.Entities; 
using WebApi.Infrastructure.Data.EntityDbMapping; 

namespace WebApi.Infrastructure.Data.Context 

public class SqlServerContext :   IdentityDbContext<ApplicationUser> 

    public DbSet<Country> Country { get; set; } 

    public SqlServerContext(DbContextOptions<SqlServerContext> options) : base(options) 
    { 
      
    }     
    protected override void OnModelCreating(ModelBuilder modelBuilder) 
    { 
        base.OnModelCreating(modelBuilder); 
        modelBuilder.Entity<Country>(new CountryMap().Configure); 
        // ModelBuilderExtensions.Seed(modelBuilder); 

    } 

//Data for first time on table 
public static class ModelBuilderExtensions 

    public static void Seed(this ModelBuilder modelBuilder) 
    { 
        modelBuilder.Entity<Country>().HasData( 
            new Country 
            { 
                Id = 1, 
                Name = "Venezuela", 
                Population = 300000000, 
                Area = 230103 
              
            }, 
            new Country 
            { 
                Id = 2, 
                Name = "Peru", 
                Population = 260000000, 
                Area =33249               
            } 
        ); 
    } 


In the folder, EntityDbMapping, we create the CountryMap class. In this class, we define the physical representation of the properties of the Country class as fields in the table of the database.

    using Microsoft.EntityFrameworkCore;   
    using Microsoft.EntityFrameworkCore.Metadata.Builders;   
    using WebApi.Domain.Entities;   
       
    namespace WebApi.Infrastructure.Data.EntityDbMapping   
    {   
        public class CountryMap : IEntityTypeConfiguration<Country>   
        {   
            public void Configure(EntityTypeBuilder<Country> builder)   
            {   
                builder.ToTable("Country");   
       
                builder.HasKey(c => c.Id);   
       
                builder.Property(c => c.Name)   
                    .IsRequired()   
                    .HasColumnName("Name")   
                    .HasColumnType("varchar(150)");   
       
                builder.Property(c => c.Population)   
                    .IsRequired()   
                    .HasColumnType("int")   
                    .HasColumnName("Population");   
       
                builder.Property(c => c.Area)   
                    .IsRequired()   
                    .HasColumnType("decimal(14,2)")   
                    .HasColumnName("Area");   
       
                builder.Property(c => c.ISO3166)   
                .IsRequired()   
                .HasColumnType("varchar(3)")   
                .HasColumnName("ISO3166");   
       
                builder.Property(c => c.DrivingSide)   
                .IsRequired()   
                .HasColumnType("varchar(50)")   
                .HasColumnName("DrivingSide");   
       
                builder.Property(c => c.Capital)   
                .IsRequired()   
                .HasColumnType("varchar(50)")   
                .HasColumnName("Capital");   
            }   
       
        }   
    }   


This is it for now.
In the next chapter, we will implement validations with FluentValidation. We will also configure Mapper to use it with our DTOs and will implement Identity using Jwt.



About HostForLIFE.eu

HostForLIFE.eu is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2016 Hosting, ASP.NET Core 2.2.1 Hosting, ASP.NET MVC 6 Hosting and SQL 2017 Hosting.


Tag cloud

Sign in