Prerequisites 🔨

Tools you have to install before create this example project:

What we have out of the box?

I have created Web API, using default .NET Core template dotnet new webapi -n SampleApi. Then you have to follow steps:

  • In controller WeatherForecastController Get method add throwing exception throw new Exception("Some error appears!"); 🔥
  • Using the Windows command prompt run application. Before dotnet run it is important to set environment variable ASPNETCORE_ENVIRONMENT to Production or Staging value. ⚠️ It is because in Development mode response will include full details about our exception and in production it is not acceptable.
set ASPNETCORE_ENVIRONMENT=Staging
dotnet run --no-launch-profile
Error response of REST API

What we will get in response is an internal server error. But if we think from a consumer perspective and try to deal with that error details we have. The response contains HTTP code 500 (Internal Server Error) and the response body has no content. So that is all we have, just one HTTP code. Do we have enough details in response we got from API? The answer is "No"! 😥

How to include details in error response?

To provide good quality of Web API we have to handle errors and include in response more details about the error. Microsoft already has described some guidelines on how to build a good Web API and how to format non-success response. Guidelines say that we have to return JSON response that includes error code and some self describable error details.

{
  "error": {
    "code": "BadArgument",
    "message": "Previous passwords may not be reused",
    "target": "password",
    "innererror": {
      "code": "PasswordError",
      "innererror": {
        "code": "PasswordDoesNotMeetPolicy",
        "minLength": "6",
        "maxLength": "64",
        "characterTypes": ["lowerCase","upperCase","number","symbol"],
        "minDistinctCharacterTypes": "2",
        "innererror": {
          "code": "PasswordReuseNotAllowed"
        }
      }
    }
  }
}
Sample error response

To provide a JSON response we have to create a class for errors. When we have a class then we can handle all exceptions and return an instance of that error class. I changed the default controller to return BadRequest when an error appears.

[HttpGet]
public IActionResult Get()
{
	try 
	{
		throw new Exception("Some error appears!");            
	}
	catch(Exception ex) 
	{
		var errorResponse = new ErrorResponse 
		{
			Error = new Error 
			{
				Code = "unhandled",
				Message = ex.Message
			}
		};
		return BadRequest(errorResponse);
	}
}
WeatherForecastController return BadRequest on error.

If we do GET request https://localhost:5001/weatherforecast using Postman again, we will get many more details about the error. 🎉

Web API error response.

What I don`t like in this type of solution, that we have to create class manually. This class is coupled to current Web API. 🤔 Sure we can pack that class inside the common library and push to NuGet repository. But would it be great if .NET Core have that error class out of the box and all our Web API will have that without any additional dependencies?

Return Problem Details using .NET Core build-in feature! 🚀

To answer that question "Do .NET Core already solved this task?" I dived into Microsoft.AspNetCore.Mvc source code. Think it is very valuable to read the source code of the library you use.

After some research, I found that ControllerBase class contains public method Problem which produces a ProblemDetails class response. If we read the summary of ProblemDetails class we will see "A machine-readable format for specifying errors in HTTP API responses based on https://tools.ietf.org/html/rfc7807.", that sounds like we found what we needed.

If you visit https://tools.ietf.org/html/rfc7807 you'll see it is a standard of error processing for Web API. It means that Microsoft already implemented that standard for handling and formatting errors in Web API. Let`s try out! 🔨

I have changed WeatherForecastController to return Problem when we got an error.

[HttpGet]
public IActionResult Get()
{
	try 
	{
		throw new Exception("Some error appears!");            
	}
	catch(Exception ex) 
	{
		return Problem(title: ex.Message);
	}
}
WeatherForecastController return Problem on error.

If we do GET request https://localhost:5001/weatherforecast using Postman again, we will error details in a different format. 🤩

HTTP/1.1 500 Internal Server Error
Date: Sat, 29 Aug 2020 14:22:25 GMT
Content-Type: application/problem+json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked

{"type":"https://tools.ietf.org/html/rfc7231#section-6.6.1","title":"Some error appears!","status":500,"traceId":"|d4c87220-406514cefaf95a6e."}
Web API problem response.

What is interesting that Problem method returns different Content-Type as we expect usually application/problem+json. Also, the response contains a body that contains a link to the internal error description.

Microsoft.AspNetCore.Mvc.Core library also contains validation problem implementation ValidationProblemDetails class. This class provides us to return Web API consumer validation error response in an appropriate format. Let`s try out.

I have changed WeatherForecastController to return both types of problems.

[HttpGet]
public IActionResult Get()
{
	try 
	{
		ModelState.AddModelError("parameterName", "Some parameter is not valid!");

		if (!ModelState.IsValid) 
		{ 
			return ValidationProblem(ModelState);
		}

		return Ok();
	}
	catch(Exception ex) 
	{
		return Problem(title: ex.Message);
	}
}
WeatherForecastController return Problem on error and ValidationProblem on invalid request.

If we do GET request https://localhost:5001/weatherforecast using Postman again, we will get validation error details.

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|34f88f8e-41acdb6aeb708c8f.",
    "errors": {
        "parameterName": [
            "Some parameter is not valid!"
        ]
    }
}
Web API validation problem response.

Now our error responses look`s much better. And what is more valuable than we use .NET Core build-in feature. ❤️

How we can customize build-in problem formatting behaviour? 🚧

It is also possible to customize the behavior of problem formatting. Using the extension method ConfigureApiBehaviorOptions we can change problem formatting behaviour. In the following configuration example, I will change the validation problem response type reference link. 😉

public void ConfigureServices(IServiceCollection services)
{
	services
		.AddControllers()
		.ConfigureApiBehaviorOptions(options => 
		{
			options.ClientErrorMapping[400] = new ClientErrorData
			{
				Link = "https://craftbakery.dev/"
			};
		});
}
Startup.cs change API behaviour options.

If we do GET request https://localhost:5001/weatherforecast using Postman again, we will get validation error details with the changed type attribute.

{
    "type": "https://craftbakery.dev/",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|7edb3147-49c4f43826f311ce.",
    "errors": {
        "parameterName": [
            "Some parameter is not valid!"
        ]
    }
}
Web API validation customized problem response.

For further API behavior customization see ApiBehaviorOptions realisation. 🔬

In the following post, we saw that .NET Core already has build-in features on how to deal with unhandled errors and how to return Web API consumer invalid request validation errors. That is what you can share in your organization for building APIs in which error responses are formatted using the unified and same style. You`ll save a lot of time and your API consumers will be happier!

Happy error handling!  🔥

[eof]