Problem Details - The right way to specify errors in Web API responses
The need for Problem Details
Often when you are developing REST APIs, one of the tough conversations to have with the consumers of your API (web client, mobile app, other 3rd parties, etc.) is defining the way your API would specify errors. Now, this is a crucial piece of design as depending on it the client/consumer will design their part of showing errors to the end-users. For example, imagine an API that allows customers to book movie tickets online and this call returns a Bad request (400) response without any other additional info. The user doesn't understand why the Client UI is showing an error and the Client UI doesn't know why the API has failed.
Many people have different designs for specifying the errors in the API response - some just return status code without any response message, while some describe a simple text message in the body and others specify custom JSON schema for specifying the errors.
"Problem Details" is a way to carry machine-readable details of errors in a HTTP response to avoid the need to define new error response formats for HTTP APIs.
A simple example of HTTP response of JSON problem details is :
HTTP/1.1 400 Bad Request
"title": "One or more validation errors occurred.",
"The Id field is required."
"The Fact field is required."
The format of the message is
application/problem+xml media type and the response body must at bare minimum contain the below fields:
type: A URI that defines the problem detail type.
title: A title that summarizes the problem.
status: HTTP status code.
We can extend the problem details with additional properties, for example, the above error message defined "errors" to communicate additional information to the client.
Problem details in ASP.NET Core
In ASP.NET Core 2.1, the team added
ProblemDetails class to support problem details model. And in ASP.NET Core 3.0, the
ControllerBase.Problem method was added to produce a ProblemDetails response from controller action methods.
In the rest of the article, we will look at handling different error types from ASP.NET Core and how to use the inbuilt functionality already provided to specify the errors in ProblemDetails response format.
We will look at specifying error responses for :
- 400 Bad Request
- 404 Not Found
- 500 Internal Server Error
We will primarily be using the
ControllerBase.Problem method to specify errors manually and leaving it to the ASP.NET Core to generate for others.
400 Bad Request
We have 2 scenarios here: Failed model validations using DataAnnotations and Custom bad request
Failed model validations using DataAnnotations: In this case, you don't need to do any additional work. ASP.NET Core will take care of it for you, given that you are using the
[ApiController]attribute to the API controller and
DataAnnotationsfor validating the model.
In the above example, we have a POST API and the input data model is validated using the
Requiredattribute of the
DataAnnotationsclass. When this API is called without passing in the required parameters of the model, a 400-Bad Request in the ProblemDetails format is generated.
Custom bad request: Maybe the business rule failed or some other logic check failed and you need to return 400-Bad Request. In this case, you can use
this.Problem(detail, statusCode: 400)inside the Controller to return the error in problem details format.
In the above example code, we are sending 400 from the controller using the
Problemmethod when the business logic fails. Hitting the API with an invalid request will result in a 400-Bad Request in the ProblemDetails format.
404 Not Found
For the 404 wherein the route didn't match, there isn't much you can do, but for scenarios where you want to return "Not Found" when data for the specified Id is not found, etc.; using the
NotFound() method in Controller will return the 404 in Problem Details format.
In the above example code, we are returning
NotFound() from the controller when the result is not found for the specified Id. Hitting the API with an invalid Id will result in 404-Not Found in the ProblemDetails format.
500 Internal Server Error
In case of 500 Internal server error, you don't want to expose off any exception object details as it will contain crucial code and system-related information; but should still adhere to the Problem Details format by giving a vague "An error occurred while processing your request." error message to the API caller. In this case, you can use
this.Problem() inside the Controller to return the error in problem details format with the default 500 error message and status code. In some very exceptional cases wherein you would like to send custom messages for the exception, use the
this.Problem(customErrorMessage) inside the Controller.
Hitting the above API which might result in some exceptions will return the response in the right format.
Problem Details standard is proposed by IETF and has been around for quite some time, but still, its usage or adherence is very low. Often people reinvent the wheel or follow different approaches while specifying errors in the Web API responses. In this article, we looked at the
ProblemDetails specification and also using the
Problem() method in the ControllerBase of ASP.Net Core application to return errors in this standard format.
Did you find this article valuable?
Support Kumar Ashwin Hubert by becoming a sponsor. Any amount is appreciated!