Nermin Kaharovic
May 10 · 5 min read

This is a two-part guide for designing flexible and cross-platform API using ASP.NET Core. You can read Part 1 here.


In this part, we will explain how to configure API middleware and other services. Following this, we will explain how to test everything using Swagger.

Middleware configuration

ASP.NET Core applications start inside of Program.cs and after initial configuration is completed, Startup.cs is initiated. We use ConfigureServices method inside of Startup class to define services that our application will use. All third party libraries configuration, as well as other service initialization, will be placed here.

public void ConfigureServices(IServiceCollection services)
{
// MVC + fluent validation initialization
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddFluentValidation();
// Dependency injection - fluent validation
services.AddSingleton<IValidator<UserRegisterDto>,
AddUserDtoValidator>();
// Override Modelstate behavior
// Aggregate valdiation errors
services.Configure<ApiBehaviorOptions>(options =>{
options.InvalidModelStateResponseFactory = (context) =>
{
var errors = context.ModelState.Values.SelectMany(x =>
x.Errors.Select(p => p.ErrorMessage)).ToList();

var result = new
{
Code = "400", // BadRequest error code
Message = "Validation errors",
Errors = errors
};
return new BadRequestObjectResult(result);
};});
// SQL provider configuration
services.AddDbContext<DataContext>(x => x.UseSqlite
(Configuration.GetConnectionString("DefaultConnection")));

// Dependency injection - repositories
services.AddScoped<IAuthentificationRepository,
AuthRepository>();
services.AddScoped<IBookRepository, BooksRepository>();// JWT token authentification set up
services.AddAuthentication
(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var token = Encoding.ASCII.GetBytes(
Configuration.GetSection("AppSettings:Token").Value);
options.TokenValidationParameters = new
TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(token),
ValidateIssuer = false,
ValidateAudience = false
};
});
// AutoMapper initialization
services.AddAutoMapper();}
// Register the Swagger generator, defining 1 or more Swagger
documents
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new Info
{
Title = "WebApp.API",
Version = "v1"
});
c.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
In = "header",
Description = "Please enter JWT with Bearer into field",
Name = "Authorization",
Type = "apiKey"
});

c.AddSecurityRequirement(new Dictionary<string,
IEnumerable<string>>
{
{ "Bearer", Enumerable.Empty<string>() }
});
});

services.AddCors();
}

Next, modify Configure method. This is the place where we typically use IApplicationBuilder to set up our middleware.

public void Configure(IApplicationBuilder app, 
IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// Global exception handler
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode =
(int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
˛ var contextFeature =
context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
// Log error
// Write response
await context.Response
.WriteAsync(contextFeature.Error.Message);
}});
});
app.UseHttps();}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseSwagger();

// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseMvc();
}

We configured authentication and Swagger. In addition to that, we created a global exception handler to make API actions more readable and the error handling process more maintainable.

Test API

Once we have created API and set up all the necessary tools, the only thing left is to test it.

Registration

Let’s create a POST request in order to set up a new User:

Picture 1 - Registration request

After the request has been submitted, we’ll get 201 code and the user should be inserted into the database.

Picture 2 - Registration response

Authentication

In order to authenticate user, we need to send a valid username/password combination:

Picture 3 - Login request

If the user is successfully authenticated, the response will contain JWT token:

Picture 4 - Login response

Retrieve data

Now when we try to access Books controller that requires an authorization, we’ll receive 401 Unauthorized error code:

Picture 5 - Response - Unauthorized request

In order to pass authorization, we need to include Bearer token in our request. In order to do this with Swagger, we’ll need some additional configuration in the Swagger generator:

// Register the Swagger generator, defining 1 or more Swagger documentsservices.AddSwaggerGen(c =>
{
...
c.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
In = "header",
Description = "Please enter JWT with Bearer into field",
Name = "Authorization",
Type = "apiKey"
});

c.AddSecurityRequirement(new Dictionary<string,
IEnumerable<string>>
{
{ "Bearer", Enumerable.Empty<string>() }
});
});

We should see an Authorize button in the upper right corner where we can enter authorization data:

Picture 6 - API methods

Enter token that was previously retrieved with Login action:

Picture 7 - Authorization token

Now, when we submit the request, we should get a response with valid data. This means the user was successfully authorized.

Picture 8 - Successful response

Conclusion

We hope you’ll find this two-part guide for designing a flexible and cross-platform API useful. In the end, I’d like to point out that this is not a bullet-proof solution, and it might not cover all your needs for “Production ready API”, but it will definitely give you an idea how to start with building a flexible, extensible and maintainable API using ASP.NET Core.

This will give you a headstart and you’ll get a basic overview of:

  • Implementing authentification,
  • Creating model validation,
  • Repositories,
  • Generic error handler
  • Data mappings between ORM entities and DTO’s,
  • Developing and testing with Swagger,
  • Implementing asynchronous data transfer.

That’s it for now! Feel free to share any thoughts or questions you may have.

Maestral Solutions

An application development company that creates solutions you can bet on.

Nermin Kaharovic

Written by

Maestral Solutions

An application development company that creates solutions you can bet on.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade