Let's say I have this action on my PotatoController
working as an api in my .NET Core 3.1 web-app:
[HttpPost]
public async Task<IActionResult> CreatePotatoAsync([FromBody] Potato potato)
{
// Code to create potato
}
Now let's say that the user when trying to create a new potato, sends and invalid json like so:
{
"potatoName": "testPotato",
"potatoAge": 7,
}
What would be the best way for my api to return not an ugly 500 error from System.Text.Json
library, but instead a nice custom 400 created by me?
CodePudding user response:
When I create a .Net 3.1 test API project and use the code that you posted, the response I get is a 400 BadRequest response with this body:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|57182e0e-445086f96bddb4e8.",
"errors": {
"$.potatoAge": [
"The JSON object contains a trailing comma at the end which is not supported in this mode. Change the reader options. Path: $.potatoAge | LineNumber: 3 | BytePositionInLine: 0."
]
}
}
When I take off the [ApiController]
attribute I get a null Potato object. I cannot replicate your behaviour of getting a 500 from System.Text.Json
So it seems it does what you're looking for out of the box. Are you using a custom JSON serializer or other model binding options? Could you post more of your code?
CodePudding user response:
I prefer returning a 200 and handling the error at the application level
If I do not need to return any value
{
"status": "ok"
}
If I need to return a value
{
"status": "ok",
"result": [
{
"Name": "Fabio"
},
{
"Name": "Laura"
}
]
}
And in case an error occurs
{
"status": "ko",
"exceptionMessage": "Something went wrong!"
}
Consider including the following methods in the base class of your controllers
/// <summary>
/// Processes the request using a method that does not return any value
/// </summary>
/// <param name="action">The method to be used to process the request</param>
protected JsonResult ProcessRequest(Action action)
{
return ProcessRequest(() =>
{
action();
return null;
});
}
/// <summary>
/// Processes the request using a method that returns a value
/// </summary>
/// <param name="func">The method to be used to process the request</param>
protected JsonResult ProcessRequest(Func<object> func)
{
JsonResult jsonResult = new JsonResult();
try
{
object result = func();
if (result != null)
{
jsonResult.Data = new { status = "ok", result = result, };
}
else
{
jsonResult.Data = new { status = "ok", };
}
}
catch (Exception e)
{
string message = e.Message;
jsonResult.Data = new { status = "ko", exceptionMessage = message, };
}
return jsonResult;
}
Then your action methods would look like the following
public ActionResult DoThat()
{
return ProcessRequest(() =>
{
// Do something
});
}
public ActionResult ReturnPersonList()
{
return ProcessRequest(() =>
{
return new List<Person> { new Person { Name = "Fabio" }, new Person { Name = "Laura" } };
});
}