ASP.NET Core 2.1

Objective of this blog is to Create a web API with ASP.NET Core.

It is challenging to configure .Net Core WebApi. It need lot of reading and researching on every little thing. I have tried to configure few basic things required:

  • Configuring Entity Framework DbContext – pool connection object
  • Configuring IIS Options
  • Registering services with IOC Container
  • Configure Jwt Bearer Authorisation
  • Configure MVC Options
  • Configure swagger – Open API documentation
  • Configure the HTTP request pipeline

1) Program class provides Main function and it used to build web host. Web host have request and response pipelines. Configuring https with Kestrel serve, it may not required, if Kestrel serve is configured with IIS. IIS is a full-fledged web server.

 public class Program
  {
    public static void Main(string[] args)
    {
      BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseKestrel(options =>
            {
              options.Listen(IPAddress.Loopback, 55221, listenOptions =>
              {
                listenOptions.UseHttps("certificate.pfx", "123-any?");
              });
            })
            .Build();
  }

2) Startup class contains Configure Services at run-time. It includes, dependency injection, adding configuration(use appSettings.json to add settings), adding EF Core DbContextPool, JwtBearer token authentication, Swagger Api documentation.

  public class Startup
  {
    internal const string ValidAudienceAndIssuer = "ValidAudienceAndIssuer";

    // Unique security key that is to be used for signature validation.
    internal const string SecurityKey = "private key";

    private readonly IConfiguration configuration;

    public Startup(IConfiguration configuration)
    {
      this.configuration = configuration;
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddDbContextPool<IDBContext, DBContext>(options => options.UseSqlServer(configuration.GetConnectionString("connectionString")));

      services.Configure<IISOptions>(options =>
      {
        options.AutomaticAuthentication = false;
        options.ForwardClientCertificate = false;
      });

      // Registering services with IOC Container
      services.AddScoped<MembershipProvider>();
      services.AddScoped<ModelValidationAttribute>();
      services.AddScoped<UserCultureInfo>();

      services.AddAuthorization(auth =>
      {
        auth.AddPolicy("Authpolicy", new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‚ÄĆ‚Äč)
            .RequireAuthenticatedUser().Build());
      });

      bool OnAuthenticationFailedCalled = false;
      services.AddAuthentication(options =>
      {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
      }).AddJwtBearer(option =>
      {
        option.TokenValidationParameters = new TokenValidationParameters
        {
          ValidateIssuer = true,
          ValidateAudience = true,
          ValidateIssuerSigningKey = true,
          ValidIssuer = ValidAudienceAndIssuer,
          ValidAudience = ValidAudienceAndIssuer,
          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey)),
          ClockSkew = TimeSpan.Zero,
        };

        option.Events = new JwtBearerEvents()
        {
          OnChallenge = c =>
          {
            if (OnAuthenticationFailedCalled)
            {
              OnAuthenticationFailedCalled = false;
              c.HandleResponse();
              return c.Response.WriteAsync("");
            }

            string err = string.Empty;
            if (!(c.AuthenticateFailure is null))
              err = "Invalid Token";
            else if (c.Error is null)
              err = "Missing Token";

            var payload = new JObject
            {
              ["Error"] = err
            };
            c.Response.StatusCode = 401;
            c.HandleResponse();
            return c.Response.WriteAsync(payload.ToString());
          },

          OnAuthenticationFailed = c =>
          {
            c.NoResult();
            string err = c.Exception.Message;
            if (c.Exception.GetType() == typeof(SecurityTokenValidationException))
              err = "Invalid Token";
            else if (c.Exception.GetType() == typeof(SecurityTokenInvalidIssuerException))
              err = "Invalid Issuer";
            else if (c.Exception.GetType() == typeof(SecurityTokenExpiredException))
              err = "Token Expired";
            else if (c.Exception.GetType() == typeof(SecurityTokenException) ||
                     c.Exception.GetType() == typeof(SecurityTokenValidationException) ||
                     c.Exception.GetType() == typeof(SecurityTokenInvalidSignatureException))
              // End up here when SecurityKey is changed and old token is used.
              err = "Authorization has been denied for this request - Invalid or Expired Token.";

            var payload = new JObject
            {
              ["Error"] = err
            };

            c.Response.StatusCode = 401;
            OnAuthenticationFailedCalled = true;
            return c.Response.WriteAsync(payload.ToString());
          }
        };
      });

      // Application to honor browser accept headers, false by default
      // Added support for XML formatting
      services.AddMvc(options =>
      {
        options.RespectBrowserAcceptHeader = true;
        options.Filters.Add(new RequireHttpsAttribute()); // Applying HTTPS globally, Security best practice
      })
      .AddXmlFormaterExtensions()
      .AddJsonOptions(options =>
      {
        options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;  // Fixes self referencing loops error
        options.SerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat;
        options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
        options.SerializerSettings.DateParseHandling = DateParseHandling.None;
        options.RegisterDateTimeConverter(services);
      });

      // Configuring Swagger
      // https://docs.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-2.1&tabs=visual-studio%2Cvisual-studio-xml
      // https://github.com/domaindrivendev/Swashbuckle.AspNetCore
      services.AddSwaggerGen(c =>
      {
        c.SwaggerDoc("v1", new Info
        {
          Title = "API",
          Version = "v1",
          Description = "This is as test API Documentation"
        });

        // Swagger 2.+ support
        var security = new Dictionary<string, IEnumerable<string>>
                {
                    {"Bearer", new string[] { }},
                    {"Basic", new string[] { }},
                };

        c.AddSecurityDefinition("Bearer", new ApiKeyScheme
        {
          Description = "Please enter token(token can be generated from Auth method) in below text box prefixed with JWT Authorization header using the Bearer scheme. Example: Bearer {token}",
          Name = "Authorization",
          In = "header",
          Type = "apiKey",
        });
        c.AddSecurityRequirement(security);

        c.EnableAnnotations();

        // Set the comments path for the Swagger JSON and UI.
        c.IncludeXmlComments(api.xml);

      });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
      app.UseStaticFiles();

      if (env.IsDevelopment())
      {
        app.UseDeveloperExceptionPage();
        app.UseExceptionMiddleware();
        app.UseSwaggerUI(c =>
        {
          c.SwaggerEndpoint(configuration["Swagger:EndPoint"], "Web API V1");
          c.RoutePrefix = string.Empty;
          c.DocumentTitle = "Web API Documentation";
        });
      }
      else
      {
        app.UseExceptionMiddleware();
        app.UseExceptionHandler();
        app.UseSwaggerUI(c =>
        {
          // To deploy on IIS
          c.SwaggerEndpoint(configuration["Swagger:EndPoint"], "Web API V1");
          c.RoutePrefix = string.Empty;
          c.DocumentTitle = "Web API Documentation";
        });
      }

      app.UseAuthentication();

      app.UseSwagger();

      app.UseMvc();
    }
  }

JwtBearerEvents:

OnChallenge :- This event raised by the framework when any resource is requested. In this event you can validate user by accessing HttpContext.Current.User as ClaimsPrincipal

OnAuthenticationFailed :- Use this event to add failure message.

WPF – .Net Core 3 – EF Core

Microsoft included WPF support in first public release of .NET Core 3. This blog shows steps to create WPF application in .Net Core. This includes adding dependency injection and EF Core using AddDbContextPool. AddDbContextPool is quite useful when you want to run multiple instances of application in a windows server RDS connections, it re-uses DbContext connection. Real advantage of it is in dot net core web api (check blog here to configure AddDbContextPool with dot net core web api)

Create a new project:

Open command prompt and type below command:

dotnet new wpf -o NewWpfApp1

Dependency Injection: ServiceCollection class provides plumbing to configure dependency injection.

    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private IServiceProvider serviceProvider = null;

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            IServiceCollection serverCollection = new ServiceCollection();
            ConfigureServices(serverCollection);

            if (serviceProvider == null)
                throw new NullReferenceException("serviceProvider is null");

            MainWindow window = new MainWindow();
            window.DataContext = serviceProvider.GetService<MainViewModel>();
            window.ShowDialog();
        }

        private void ConfigureServices(IServiceCollection serviceCollection)
        {
            serviceCollection.AddDbContextPool<TestDataContext>(options => options.UseSqlServer(@"Server=USER-PC\SQLEXPRESS;Database=testdb;Trusted_Connection=True;"));
            serviceCollection.AddSingleton<ITitle, ApplicationTitle>();
            serviceCollection.AddTransient<MainViewModel>();
            serviceProvider = serviceCollection.BuildServiceProvider();
        }
    }

Entity Framework Core

DataContext:

public class TestDataContext : DbContext
    {
        public TestDataContext(DbContextOptions options)
            : base(options)
        { }

        public DbSet<testModel> data { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<testModel>()
                .ToTable("testtable");

            modelBuilder.Entity<testModel>()
                    .Property(b => b.Id)
                    .HasColumnName("id");
        }
    }

Good Example:

https://github.com/sukhmindersandhu/wpf-dotnet-3-ef

WPF – Command

The command pattern is a common software design pattern that allows you to easily encapsulate the logic.

The ICommand interface requires CanExecute and Execute methods, as well as a CanExecuteChanged event handler that signals when the can execute status has changed:

public interface ICommand
{
   bool CanExecute(object parameter);
   event EventHandler CanExecuteChanged;
   void Execute(object parameter)
}

Implementation of ICommand Interface:

using System;
using System.Windows.Input;

namespace CommandAndMessenger.Common
{
    internal class CustomCommand : ICommand
    {
        private Action<object> execute;
        private Predicate<object> can;

        public CustomCommand(Action<object> execute, Predicate<object> can)
        {
            this.execute = execute;
            this.can = can ?? ((x) => true);
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }

            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }

        public bool CanExecute(object parameter)
        {
            return can(parameter);
        }

        public void Execute(object parameter)
        {
            execute(parameter);
        }
    }
}

Example:

using CommandAndMessenger.Common;
using CommandAndMessenger.Model;
using System.Windows.Input;

namespace CommandAndMessenger.ViewModel
{
    internal class Main: SimpleItem
    {
        public Main():
            base("My Sample Application")
        {
            OpenFileCommand = new CustomCommand((x) => FileOpen(x), (x)=> true);
            CloseApplicationCommand = new CustomCommand((x) => CloseApplication(x), (x) => true);
        }

        public ICommand OpenFileCommand { get; set; }

        public ICommand CloseApplicationCommand { get; set; }

        private void FileOpen(object para)
        {
           // Code
        }

        private void CloseApplication(object para)
        {
           // Code            
        }
    }
}

XAML:

<button.contextmenu>
  <contextmenu>
     <menuitem header="Open File" command="{Binding OpenFileCommand}">
     <menuitem header="Close Application" command="{Binding CloseApplicationCommand}">
  </menuitem></menuitem></contextmenu>
</button.contextmenu>