I have an MVC 4 WebAPI application. What i want to do is filter out any ModelState errors that happen because of malformed data sent during the put/post.
I have an ActionFilterAttribute that checks if the ModelState is valid. I want to send the state's ErrorMessages back to the user. This part is working fine.
/// <summary>
/// This filter will validate the models that are used in the webapi
/// </summary>
public class MyValidationFilter :System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
//ErrorResponse is just a simple data structure used to hold response
ErrorResponse errorResponse = new ErrorResponse();
//loop through each key(field) and see if it has any errors
foreach (var key in actionContext.ModelState.Keys)
{
var state = actionContext.ModelState[key];
if (state.Errors.Any())
{
string validationMessage = state.Errors.First().ErrorMessage;
errorResponse.ErrorMessages.Add(new ErrorMessage(validationMessage));
}
}
//this is a custom exception class that i have that sends the response to the user.
throw new WebAPIException(HttpStatusCode.BadRequest, errorResponse );
}
}
}
Normal validation (Required, StringLength, Regex) all work fine, because I can control those messages.
[Required(ErrorMessage = "ID is required")]
public string ID { get; set; }
However, I can't control the message if someone passes incorrectly formatted XML or JSON data. If that happens, then I might get
Unterminated string. Expected delimiter: \". Path '', line 1, position 9.
or
Invalid character after parsing property name. Expected ':' but got: }. Path '', line 1, position 9.
or these ones which shows my namespace.
Error converting value \"Medium\" to type 'MyNameSpace.Tenant.WebAPIs.Library.IndividualRegistrationInfo'. Path '', line 1, position 8.
Error in line 1 position 7. Expecting element 'IndividualRegistrationInfo' from namespace 'http://schemas.datacontract.org/2004/07/MyNameSpace.Tenant.WebAPIs.Library.IndividualRegistrationInfo'.. Encountered 'Element' with name 'asdf', namespace ''
I want to somehow send back a generic "Invalid data" message when this happens. Is there another filter that I could use, or some other place that could catch and override these messages?
UPDATE
Here is what I ended up doing based on Chris's advice:
I created 2 new Formatters for JSON and XML.
public class JsonFormatter : JsonMediaTypeFormatter
{
public override System.Threading.Tasks.Task<object> ReadFromStreamAsync(Type type, System.IO.Stream stream, System.Net.Http.Headers.HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
System.Threading.Tasks.Task<object> task = base.ReadFromStreamAsync(type, stream, contentHeaders, formatterLogger);
//parse error if null
if (task.Result == null)
{
//handle error here.
}
return task;
}
}
public class XMLFormatter : XmlMediaTypeFormatter
{
public override System.Threading.Tasks.Task<object> ReadFromStreamAsync(Type type, System.IO.Stream stream, System.Net.Http.Headers.HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
{
System.Threading.Tasks.Task<object> task = base.ReadFromStreamAsync(type, stream, contentHeaders, formatterLogger);
//parse error if null
if (task.Result == null)
{
//handle error here
}
return task;
}
}
and in the Application_Start method in global.asax
GlobalConfiguration.Configuration.Formatters.Insert(0, new JsonFormatter());
GlobalConfiguration.Configuration.Formatters.Insert(1, new XMLFormatter());
I am not sure if there is a better way, but this seems to work.