Web.API 2 centralised model state validation

Model state validation is easy with DataAnnotations. However, you will find repeating the next line of code very soon:


if(!ModelState.IsValid) return BadRequest(..);

Luckily, there is a pretty simple solution – to use ActionFilter:

All you have to do is to add the ValidateModel attribute to your controller methods that require model state validation.

Advertisements

Enterprise and small mistakes made in ASP.NET Web.API

Short Intro

“The only real mistake is the one from which we learn nothing.” – Henry Ford

It is important to share our experience to help others avoid stepping into the same bucket.

It was an enterprise distributed project with a small group of developers. Each developer had a different part of the solution to develop and at some point, all of us concentrated on the backend.

The Story

So here is a list of mistakes we made:
  • Security:
    • In some (bad designed) case authentication tokens were sent in a query string as a part of the URL. Please don’t try it at home, it’s like sharing your username and password with the world. Anyone who will get the URL will be able to access the protected resources.
  • Caching:
    • In memory caching for distributed solution – that might work if you manage to sync the in-memory caches between different nodes, but that was not our case. If you would like to get a bit more information about different caching options please check here and here.
    • Complicated, bad organised and annoying caching system – in most cases we had a lot of dependent cache but in order to clean the dependent cache, we had to write a lot of boilerplate code. However, upon Insert, we could add CacheDependency to simplify the solution for example.
  • Database:
    • We used EntityFramework to work without MSSQL database – but most of our developers were not familiar with “Performance Considerations for EF 4, 5, and 6“. This led us to quite slow queries without cached query plans and etc.
    • We ended up with a lot of copy-paste queries – a violation of DRY principle.
    • We had to write a lot of boilerplate code on each request – DTO -> SQL Query -> Entity -> DTO. One of the possible solutions here would be to use Automapper.
  • API:
    • A mix of RESTful and RPC.
    • Almost RESTful – where POSTs used to return data (query).
  • Conflict of interests:
    • Our backend was written in C# and front-end in javascript – we ended up with lowercase property names in our C# DTOs. Well, it’s due to different naming conventions but why not use the next solution and let all of the devs be happy?
  • General:
    • SOLID principles were totally ignored.
    • 0 automated tests.
    • DRY principles violated.

Conclusion

At some point, as you can imagine we had to refactor a lot of code, the problem, as usual, was that we had to do it after release because we didn’t have any automated tests it was quite risky and hard to achieve. The good news is that we managed to improve the code quality, maintainability and scalability by making baby steps re-factoring and the most important thing is that we managed to deliver a working solution on time. This story ends up well but if we had a good code base from the begging we would be happier and stress less.
Happy coding!
P.S.: Don’t judge your colleagues, share your knowledge, thoughts and experience to improve the world.

Adding swagger to ASP.NET Web.Api

Sounds easy right? But it turn to be very confusing. When I first tried to integrate Swashbuckle I met few problems:
  1. Auto generated documentation totally ignored existing and working API endpoints (methods in ApiControllers). So it was totally empty.
  2. After solving the first issue I realised that “/Token” endpoint is still not listed.
  3. Had to find a way to pass a bearer token with each request swagger generated for me.
So let’s start solving those problems one by one.
First we have to install Swashbuckle nuget package: Install-Package Swashbuckle
That should generate SwaggerConfig.cs under App_Start folder:

swashbuckle swagger
To solve problem #1 we need to use the right HttpConfiguration:
  1. Remove [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), “Register”)] from SwaggerConfig.cs. We will register it manually. 
  2. Change the signature of Register method to public static void Register(HttpConfiguration httpConfig). This will allow us to pass the right HttpConfiguration.
  3. Change GlobalConfiguration.Configuration to httpConfig.EnableSwagger.  This will allow us to use the right HttpConfiguration upon the registration.
swashbuckle swagger

Now we have to manually register our swagger, for that we just have to add one additional line to Startup.cs;

At this point all our API endpoints should be visible if you navigate to http://yourapi:port/swagger
To solve problem number #2 we have to manually define documentation for our /Token endpoint by creating a AuthTokenDocumentFilter.cs (source):
public class AuthTokenDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
swaggerDoc.paths.Add("/token", new PathItem
{
post = new Operation
{
tags = new List { "Auth" },
consumes = new List
{
"application/x-www-form-urlencoded"
},
parameters = new List {
new Parameter
{
type = "string",
name = "grant_type",
required = true,
@in = "formData",
@default = "password"
},
new Parameter
{
type = "string",
name = "username",
required = false,
@in = "formData"
},
new Parameter
{
type = "string",
name = "password",
required = false,
@in = "formData"
}
}
}
});
}
The next step will be to add AuthTokenDocumentFilter to our swagger configuration:

At this point “Auth” endpoint will become visible at http://yourapi:port/swagger

To solve problem number #3 we have to make few small changes in SwaggerConfig.cs.
Add the next line inside EnableSwagger section:

c.ApiKey("Token")
.Description("Bearer token")
.Name("Authorization")
.In("header");

Inside EnableSwaggerUi section add the next line:

c.EnableApiKeySupport("Authorization", "header");

Now in order to get a bearer token you can use swagger and if you want to use the retrieved token in all calls simply add it near the “Explore” button:

Good luck!

P.S.: You can get the file from GitHub Gist