Ready-Made ASP.NET 5 MVC 6 Web API Template – Grab It Now!

3

In my previous post, Complete Guide to Setting Up ASP.NET 5 MVC 6 for Stand Alone and IIS Servers, I introduced the awesome ASP.NET 5 framework. If you are not familiar with all of its benefits, you should read it up.  Now that the environment is all setup, including Visual Studio Community, which will allow me to step through my code, I begun the journey of creating the ASP.NET 5 MVC 6 web app that will meet all my project requirement. The project requirements were:

  • Force HTTPS (HTTP over SSL)
  • Authenticate using username and password
  • Only allow white-listed IP addresses
  • Provide RESTful APIs for add, update, and delete
  • Scale to 400 or more requests or transactions per second
  • Access MS SQL Server Stored Procedures
  • Be able to step through the code and debug
  • Setup error logging

Force HTTPS

To force use of HTTPS, I simply added RequireHttpsAttribute() filter in ConfigureServices method. This forced all API calls to HTTPS except the root.

// forces https when api is accessed
// note that the home or root is still accessible non securely
options.Filters.Add(new RequireHttpsAttribute());

Authentication

There is this fancy overhauled authentication framework called ASP.NET Identity. But since I really didn’t need anything too fancy and because its main job was to provide a basic authentication to make sure the user was allowed the access, I ended up with more simpler solution. This was achieved by creating a custom ActionFilter. Many websites had similar codes, but one that made most sense, that did not involve ASP.NET Identity but still provide enough flexibility, was from this post.

The implementation is pretty straightforward. I adjusted the code to pull an array of user names from config.json, as well as, incorporate by validating client IP address:

public class BasicAuthenticationAttribute : ActionFilterAttribute
    {
        public string BasicRealm { get; set; }

        public BasicAuthenticationAttribute()
        {
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var req = filterContext.HttpContext.Request;
            var auth = req.Headers["Authorization"];
            if (!string.IsNullOrEmpty(auth))
            {
                var cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
                var user = new { Name = cred[0], Pass = cred[1] };

                var userIp = Convert.ToString(filterContext.HttpContext.GetFeature<IHttpConnectionFeature>()?.RemoteIpAddress);

                var configuration = new Configuration().AddJsonFile("config.json");
                var authorizedUserString = configuration.Get("Users");
                List<string> authorizedUsers = authorizedUserString.Split(';').ToList<string>();
                foreach (string curAuthorizedUserString in authorizedUsers)
                {
                    List<string> curAuthorizedUser = curAuthorizedUserString.Split(',').ToList<string>();
                    if (user.Name == curAuthorizedUser[0] && user.Pass == curAuthorizedUser[1] && userIp == curAuthorizedUser[2])
                    {
                        return;
                    }
                }

            }
            var res = filterContext.HttpContext.Response;
            res.StatusCode = 401;
            res.HttpContext.Response.WriteAsync("Unauthorized");

            // need to end the response here since no idea how to figure this out, 
            // I will redirect to root and this will prevent from executing API
            filterContext.Result = new RedirectResult("/");

            // asks for user/pass again - cannot have redirect with this.. 
            //res.Headers.Append("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", BasicRealm ?? "MyWebSite"));
        }
    }

Then you can add the attribute as below to each API call that needs to be authenticated:

        [BasicAuthenticationAttribute]
        [HttpGet("{zipcode}", Name = "GetByZipcodeRoute")]
        public IActionResult GetByZipcode (string zipcode)
        {
            var item = _repository.GetByZipcode(zipcode);
            if (item == null)
            {
                //return HttpNotFound();
                item = new CBUItem { Zipcode = "Not Found" };
            }

            return new ObjectResult(item);
        }

 

Calling MS SQL Stored Procedure from ASP.NET 5 MVC 6

As easy as this sounds, because ASP.NET 5 is still going through development, at beta4 at the time of writing, I went through many hours of troubleshooting due to unstable IDE environment (Visual Studio Community in preview version), beta .NET Execution environment, and some libraries simply did not want to be founded by Intellisense if they were not in the precise order. To enable ADO.NET, I knew I had to load System.Data.SqlClient, and possibly System.Data.Common. But turned out that I had to add this particular library in the order found in project.json to allow System.Data.SqlClient and System.Data to be found:

"Microsoft.Framework.CodeGenerators.Mvc": "1.0.0-beta4", // IMPORTANT: this is a must have for System.Data.SqlClient

It beats me why but with this library loded, I did not have to add any dependencies to project.json’s frameworks section. After this, my SqlHelper.cs class loaded all fine and I was able to make calls to Stored Procedure (refer to Startup.cs).

Logging Framework 

There are many proven .NET logging framework, including NLog, Log4Net, and Serilog. But turned out that, at the time of writing, none of them had the version that worked with .NET Core 5. So, I picked the one that was most easy to implement. I picked Serilog. To setup Serilog, you have to add the dependency in project.json, which is nicely found by Intellisense:

  "dependencies": {
    "Microsoft.AspNet.Server.IIS": "1.0.0-beta4", // running on iis
    "Microsoft.AspNet.Server.WebListener": "1.0.0-beta4", // running on standalone web server
    "Kestrel": "1.0.0-beta4", // running on vnext server
    "Microsoft.AspNet.Diagnostics": "1.0.0-beta4", // to load welcome page
    "Microsoft.ASpNet.Mvc": "6.0.0-beta4", // to load mvc framework
    "Microsoft.Framework.CodeGenerators.Mvc": "1.0.0-beta4", // IMPORTANT: this is a must have for System.Data.SqlClient
    "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4", // to load config.json
    "Serilog": "1.5.5" // to enable logging
  },

Then create the logger from Startup() constructor:

        public readonly ILogger _logger;

        public Startup()
        {
            //Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources, 
            //then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely.
            Configuration = new Configuration()
                        .AddJsonFile("config.json")
                        .AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.

            _logger = new LoggerConfiguration()
                        .WriteTo.RollingFile("c:\\rollingfile.txt")  
                        .WriteTo.File("c:\\test.txt")  
                        .WriteTo.Console()
                        .CreateLogger();
            _logger.Information("Serilog created");
        }

But this will still return not found error for dnxcore50 framework since there is no library ready for .NET Core 5. So, unfortunately, if you want to use Serilog, you would have to comment out dnxcore50 from project.json:

  "frameworks": {
    "dnx451": {
      "dependencies": {
      }
    }
    /*, //currently Serilog does not support dnxcore50 so we need to comment it out, otherwise it won't work.. 
    "dnxcore50": {
      "dependencies": {
      }
    }
      */
  },

There you have it! This is very simple ASP.NET 5 MVC 6 framework for you to get going with all your Web API coding needs! Hope you find it useful! The complete source can be downloaded from GitHub.

Share.

About Author

Avatar photo

An avid technologist, entrepreneur at heart, has the determination to make this world a better place by contributing the most useful articles to ThingsYouMustKnow.com, one article at a time.

3 Comments

  1. Kevin Clements on

    Ok – how the heck did you find the idea of including Microsoft.Framework.CodeGenerators.Mvc to get ADO to resolve properly?

    Been beating my head against that wall all night.

    Thanks!

    • Avatar photo

      Hi Kevin,

      I’m glad this article helped! I was trying a bunch of different examples, including trying out EF7 from this post, and one thing led to another I ended up with Microsoft.Framework.CodeGenerators.Mvc in my project.json!

      Philip