5

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 Answers 2

6

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);
        }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

That's been driving me bonkers for the last hour, great answer, it's saved my sanity!
So if I have the Accept header application/xml, and have set UseXmlSerializer should this work with Request.CreateResponse or will it always try and use DataContractSerializer? It isn't working for me either way.
@GlacialSpoon the XmlSerializer also does not serialize Dictionaries, so you need to use a type which is Serialize-able.
@nemesv Thanks. Turns out that I had forgotten about XmlSerializer's need for parameter-less constructors on the model.
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.