Common features in ASP.NET Core 2.1 WebApi: Documenting

 

Introduction

Provide a list of the services that are offered, for each one, how to call them and the structure of the response that is returned is also an essential feature for a Web API.

In this article, we will talk about Swagger.

Swagger tools takes the hard work out of generating and maintaining your API docs, ensuring your documentation stays up-to-date as your API evolves.

The Swagger framework for ASP.NET Core is an open source implementation based on the OpenAPI specification born in 2010.

Installation

Download this package:

PM> Install-Package Swashbuckle.AspNetCore -Version 4.0.1

Configuring Startup.cs 

It’s pretty simple to configure and activate Swagger.

First let’s add Swagger in ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
   services.AddSwaggerGen(c =>
   {
      c.SwaggerDoc("v1", new Info { Title = "My API + profiler integrated on top left page", Version = "v1" });
      c.AddSecurityDefinition("Bearer", new ApiKeyScheme
      {
         Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
         Name = "Authorization",
         In = "header",
         Type = "apiKey"
      });
      c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
      {
         { "Bearer", new string[] { } }
      });
   });
}

Adding the document needs only one line:

c.SwaggerDoc(“v1”, new Info { Title = “My API + profiler integrated on top left page”, Version = “v1” });

It defines only the version, and the title of the documentation.

The rest below is there only for enable Authorization with a bearer token. In this serie of article I’m using bearer token, so you can replicate everything I do in this serie if you follow the serie since the first article.

Now let’s activate the middleware that serves Swagger and the middleware that serves the Swagger UI:

// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();

// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
app.UseSwaggerUI(c =>
{
   c.RoutePrefix = "api-doc";
   c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});

I added here  a RoutePrefix named “api-doc” in order to build a friendly Url when you want to display the Swagger UI.

Demo

Now if we run the WebApi Swagger UI page, it should look like this:

We have the title as we typed it ine the Startup configurations, our routes as documentation and the Authorize button.

Let’s try now to test a route with the Authorization, because our routes are protected with a Bearer Token.

Set up the token value

Open the popup after clicking on the Authorize button and set up the value like this: bearer “Your token”:

Then press Authorize button again in the popup:

Call the route

Like the picture below, the call should succeed:

Conclusion

You saw how to add easily documentation in your WebApi.

That’s a nice feature to have when you create a WebAPI.

I hope you will now have the reflex to add it to your WebApis 😉

Common features in ASP.NET Core 2.1 WebApi: Testing

 

Introduction

Often neglected as well, unit tests make it possible to verify that an implementation remains in conformity with the expected operating rule, preventing regressions in case of evolution of the program.

The integration tests, they are used to verify what the unit tests can not do: test the operation of each layer of software between them.

In this article we will use these tools:

  • FluentAssertion
    • Fluent Assertions is a set of .NET extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style test. This enables, simple intuitive syntax
  • XUnit
    • xUnit.net is a free, open source, community-focused unit testing tool for the .NET Framework. Written by the original inventor of NUnit v2, xUnit.net is the latest technology for unit testing C#, F#, VB.NET and other .NET languages. xUnit.net works with ReSharper, CodeRush, TestDriven.NET and Xamarin. It is part of the .NET Foundation, and operates under their code of conduct. It is licensed under Apache 2 (an OSI approved license).
  • NSubstitute
    • NSubstitute is a friendly substitute for .NET mocking frameworks
  • TestServer
    • ASP.NET Core includes a test host that can be added to integration test projects and used to host ASP.NET Core applications, serving test requests without the need for a real web host.

Installation

Download these packages:

PM> Install-Package FluentAssertions -Version 5.5.3
PM> Install-Package xunit -Version 2.4.1
PM> Install-Package xunit.runner.console -Version 2.4.1
PM> Install-Package NSubstitute -Version 3.1.0
PM> Install-Package Microsoft.AspNetCore.TestHost -Version 2.1.1

Creating unit tests

I have already written some articles on this subject. They show how to make unit tests with XUnit, FluentAssertion and NSubstitute.

They are fully applicable on ASP.NET Core 2.1 WebAPI:

I did not use NSubstitute in these two previous articles then I reused the middleware built in this article (ASP.NET Core 2.1 middlewares part 2: Unit test a custom middleware) to show you how NSubstitute works:

Middleware to unit test:

public class CustomExceptionMiddleware
{
   private readonly RequestDelegate _next;
   private readonly ILogger<CustomExceptionMiddleware> _logger;

   public CustomExceptionMiddleware(RequestDelegate next, ILogger<CustomExceptionMiddleware> logger)
   {
      _next = next;
      _logger = logger;
   }

   public async Task Invoke(HttpContext context)
   {
      try
      {
         await _next.Invoke(context);
      }
      catch (Exception ex)
      {
         await HandleExceptionAsync(context, ex);
      }
   }

   private async Task HandleExceptionAsync(HttpContext context, Exception exception)
   {
      var response = context.Response;
      var message = "Unhandled error";
      var code = "00009";
      var errors = new List<string>();

      response.ContentType = "application/json";
      response.StatusCode = (int)HttpStatusCode.InternalServerError;

      // log generic exception message (original error)
      _logger.LogError(exception, exception.Message);
 
      // Response
      await response.WriteAsync(JsonConvert.SerializeObject(new Error
      {
         Code = code,
         Message = message,
         Errors = errors
      }));
   }
}

Middleware unit tested:

[Fact]
public async Task WhenAGenericExceptionIsRaised_CustomExceptionMiddlewareShouldHandleItToDefaultErrorResponseAndLoggerCalled()
{
   // Arrange
   var loggerMock = Substitute.For<ILogger<CustomExceptionMiddleware>>();
   var middleware = new CustomExceptionMiddleware((innerHttpContext) =>
   {
      throw new Exception("Oooops error!");
   }, loggerMock);

   // Use DefaultHttpContext insteadof mocking HttpContext
   var context = new DefaultHttpContext();

   // Initialize response body
   context.Response.Body = new MemoryStream();

   //Act
   await middleware.Invoke(context);

   // set the position to beginning before reading
   context.Response.Body.Seek(0, SeekOrigin.Begin);

   // Read the response
   var reader = new StreamReader(context.Response.Body);
   var streamText = reader.ReadToEnd();
   var objResponse = JsonConvert.DeserializeObject<Error>(streamText);

   //Assert
   objResponse
   .Should()
   .BeEquivalentTo(new { Message = "Unhandled error", Errors = new List<string>() , Code = "00009" });

   context.Response.StatusCode
   .Should()
   .Be((int)HttpStatusCode.InternalServerError);

   loggerMock.Received(1);
}

Simple right 😉 ?

Creating integration tests with TestServer

Emulate a server

The first step before testing WebAPI is to build an emulated server by using the class TestServer provided by the assembly: Microsoft.AspNetCore.TestHost

Example:

public class TestServerFixture : IDisposable
{
   private readonly TestServer _testServer;
   public HttpClient Client { get; }

   public TestServerFixture()
   {
      var builder = new WebHostBuilder()
      .UseEnvironment("Development")
      .UseStartup<Startup>();

      _testServer = new TestServer(builder);
      Client = _testServer.CreateClient();
   }

   public void Dispose()
   {
      Client.Dispose();
      _testServer.Dispose();
   }
}

WebHostBuilder is easy to setup, you just need to setup the environment you want to use for your tests, and delcare the Startup class you want to use (Startup of your WebAPI obviously).

Then you just have to assign to a HttpClient variable an instance of a TestServer class that take in parameters the WebHostBuilder previously defined.

That’s it!

Now let’s see how to test an URI protected by an Autorization by a bearer token for which no token is sent, we expect an Http 401 UnAuthorized:

[Fact]
public async Task WhenGetMethodIsInvokedWithoutAValidToken_GetShouldAnswerUnAuthorized()
{
   using (TestServerFixture fixture = new TestServerFixture())
   {
      // Act
      var response = await fixture.Client.GetAsync("/api/DemoAuthorization/5");

     // Assert
     response
     .StatusCode
     .Should()
     .Be(HttpStatusCode.Unauthorized);
   }
}

Test Server is really easy to consume, it’s a disposable resource, using {} is the common way to consume it and dispose it after its use.

Conclusion

You saw how to make easily unit tests and integration tests.

That’s a very big feature that you must implement when you develop a program such as a WebAPI.

Don’t forget this 😉

Common features in ASP.NET Core 2.1 WebApi: Logging

 

Introduction

Often overlooked, logging errors and other information in case of bug in the program is an essential feature in a Web API.

In this article we will see how to implement logging in a rolling file in an ASP.NET Core WebAPI with Serilog.

Serilog, Like many other libraries for .NET, Serilog provides diagnostic logging to files, the console, and elsewhere. It is easy to set up, has a clean API, and is portable between recent .NET platforms.

Unlike other logging libraries, Serilog is built with powerful structured event data in mind.

Installation

Download these three packages:

PM> Install-Package Serilog.AspNetCore -Version 2.1.1
PM> Install-Package Serilog.Settings.Configuration -Version 3.0.1
PM> Install-Package Serilog.Sinks.RollingFile -Version 3.3.0

The Serilog.AspNetCore enables Serilog for ASP.NET Core, you have to add Serilog.Settings.Configuration if you need to enable configuration from appsettings.json.

Then Serilog.Sinks.RollingFile enables you to log into rolling files your logs.

Configuring your appsettings.json and Startup.cs

Step 1: Configuring appsettings.json

We chose rolling files to put our logs.

There are three big parts in the configuration

  1. Logging minimum level: In this example the minimum log level is “Information”
  2. Serilog itself: “WriteTo”, as you can see it’s an array, it means you can setup many Sinks (destination of the log), in our case “RollingFile”
  3. Properties: “Application” allows you to name your log if you want to know what log belongs to what application.

Example:

{
 "Serilog": {
    "MinimumLevel": "Information",
    "WriteTo": [
    {
       "Name": "RollingFile",
       "Args": {
          "pathFormat": "C:\\Temp\\log-{Date}.txt",
          "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"
       }
    }
   ],
   "Properties": {
      "Application": "Common feature in WebApi demo"
   }
 }
}

Once done we have to go to Startup.cs assigning this configuration to Serilog.

NB: Using settings in appsettings.json is not mandatory, you can build your configuraion by code, you can find examples here.

Step 2: Configuring Startup.cs

As we said before we need to read the configuration from appsettings.json in our example:

public Startup(IConfiguration configuration)
{
   // Init Serilog configuration
   Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
   Configuration = configuration;
}

Then we need to add Serilog to the LoggerFactory in the Configure method:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
   // logging
   loggerFactory.AddSerilog();
   app.UseMvc();
 }

In fact Serilog override the default Microsoft Logger provided by the assembly: Microsoft.Extensions.Logging

Logging in action

Let’s create an action that catch an exception and logs it:

[Route("api/[controller]")]
[ApiController]
public class DemoExceptionController : ControllerBase
{
   private readonly ILogger<DemoExceptionController> _logger;

   public DemoExceptionController(ILogger<DemoExceptionController> logger)
   {
      _logger = logger;
   }

   [HttpGet]
   public IEnumerable<string> Get()
   {
      try
      {
         _logger.LogInformation("Could break here :(");
         throw new Exception("bohhhh very bad error");
      }
      catch (Exception e)
      {
         _logger.LogError(e, "It broke :(");
      }
      return new string[] { "value1", "value2" };
   }
}

Let’s see now what happen when this code is executed:

We can see the error log and more (Information) because we set it as minimum level.

Conclusion

This was an example of implementing logging in an ASP.NET Core WebAPI 🙂

Serilog provides many differents ways to log (Sinks), you can find the complete list here.

Serilog is very powerfull, you can also enrich your log easily with Enrichers, a great feature of Serilog!

Awesome right ? 😉