5
votes

I have a controller and method that adds user to a DB.

I call it from Fiddler with a Request Header as follows -

Content-Type: application/xml

Accept: application/xml

Host: localhost:62236

Content-Length: 39

And a request body of -

<User>
  <Firstname>John</Firstname>
  <Lastname>Doe</Lastname>
</User>

This works as expected, the method is called, the user object processed in the method PostUser.

 public class UserController : ApiController
    {
        public HttpResponseMessage PostUser(User user)
        {
            // Add user to DB
            var response = new HttpResponseMessage(HttpStatusCode.Created);
            var relativePath = "/api/user/" + user.UserID;
            response.Headers.Location = new Uri(Request.RequestUri, relativePath);
            return response;
        }
    }

I am performing my Model Validation in it's own class

public class ModelValidationFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.ModelState.IsValid == false)
        {
            // Return the validation errors in the response body.
            var errors = new Dictionary<string, IEnumerable<string>>();
            foreach (KeyValuePair<string, ModelState> keyValue in actionContext.ModelState)
            {
                errors[keyValue.Key] = keyValue.Value.Errors.Select(e => e.ErrorMessage);
            }

            actionContext.Response =
                actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, errors);
        }
    }
}

BUT if I post the following

<User>
  <Firstname></Firstname> **//MISSING FIRST NAME**
  <Lastname>Doe</Lastname>
</User>

The Model is invalid, and a JSON response is returned even though I stated Accept: application/xml.

If I perform model validation within the UserController, I get a proper XML response, but when I perform it in ModelValidationFilterAttribute I get JSON.

2

2 Answers

6
votes

Your problems with the following code:

var errors = new Dictionary<string, IEnumerable<string>>();
actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, errors);

So you try to create the response from the errors object where its type is Dictionary<string, IEnumerable<string>>();.

Web.API will try to automatically find the right MediaTypeFormatter for your response type. However the default XML serializer (DataContractSerializer) not able to handle the type Dictionary<string, IEnumerable<string>>(); so it will use the JSON serializer for your response.

Actually you should use the CreateErrorResponse and just create the response from the ModelState directly (it will create a HttpError object which is XML seriazable)

public class ModelValidationFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.ModelState.IsValid == false)
        {
            actionContext.Response =
                actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, 
                    actionContext.ModelState);
        }
    }
}
1
votes

I think the Web API will return JSON as its default type for responses outside of controller methods.

Have you tried disabling the JSON formatter, as suggested in this article?

http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization

i.e.

void ConfigureApi(HttpConfiguration config)
{

    // Remove the JSON formatter
    config.Formatters.Remove(config.Formatters.JsonFormatter);
}