Home > Net >  How to better error handling on invalid json, on POST request?
How to better error handling on invalid json, on POST request?

Time:12-15

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" } };
    });
}

  • Related