I am creating an ASP.Net 5 application with MVC 6, using .Net 4.5.1. I have a POST method that uses a FromBody parameter to get the object automatically.
[HttpPost]
public IActionResult Insert([FromBody]Agent agent)
{
try
{
var id = service.Insert(agent);
return Ok(id);
}
catch (Exception ex)
{
return HttpBadRequest(ex);
}
}
This is just a proof a concept, I won't return only the id on success or the full exception on error.
When a valid JSON is sent everything works fine. However when an invalid JSON is sent, I get an exception during debug:
Exception thrown: 'Newtonsoft.Json.JsonReaderException' in Newtonsoft.Json.dll
Additional information: After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20.
The problem is that after this error, the method is called normally, but with a null parameter, and the exception doesn't propagate.
I could check for null and return a generic message, but that is not as useful as the original message, which has important information, such as the tag name and position of the invalid character.
So I want to capture this exception and return it to the HTTP caller. How do I do that? Something like this:
{"error": "After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20"}
I know I could capture and deserialize the JSON manually inside a try/catch block, but that is not acceptable for me. I want to do it and continue using FromBody, which I find very productive.
The default JsonInputFormatter
will in fact return a null model upon encountering an error - but it will populate ModelState
with all exceptions.
So you have access to all encountered errors by digging into ModelState
:
[HttpPost]
public IActionResult Insert([FromBody]Agent agent)
{
if (!ModelState.IsValid)
{
var errors = ModelState
.SelectMany(x => x.Value.Errors, (y, z) => z.Exception.Message);
return BadRequest(errors);
}
// Model is valid, do stuff.
}
Output of above is an array of all exception messages, e.g.:
[
"After parsing a value an unexpected character was encountered: l. Path 'Name', line 2, position 20",
"Another exception message..."
]
JsonInputFormatter - Source
I found myself with exactly the same problem, but was able to find a different solution. I will share my solution here as an alternative to @ypsilo0n's answer.
Instead of checking in every controller the if (!ModelState.IsValid)
we can have this middleware filter:
public class FooFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var modelState = context.ModelState;
if (modelState != null && modelState.IsValid == false)
{
// this class takes the model state and parses
// it into a dictionary with all the errors
var errorModel = new SerializableError(modelState);
context.Result = new BadRequestObjectResult(errorModel);
}
}
}
Now, the controller never gets called because this middleware runs before and ends the request. (read docs for more information).
When we set a non-null context.Result
it means "end the HTTP request here" (the docs) -- not very user friendly/intuitive if you ask me but OK (would expect a return value instead).
using .net core 1.1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With