katie77
katie77

Reputation: 1811

Using ModelState to return errors from WebAPI 2

I am trying to use ModelState object to send Errors to the Client; I am using asp.net Web API for the Service.

On the Web Service side, I am doing this.

    public HttpResponseMessage VerifyData(Cobject data)
    {
        string[] errors;
        if (!VerifyAllRequiredData(data, out errors))
        {
            foreach(string error in errors)
                ModelState.AddModelError("", error);
            return Request.CreateErrorResponse(HttpStatusCode.ExpectationFailed, ModelState);
        }

        return Request.CreateResponse(HttpStatusCode.OK, data);
    }

I am creating a .NET Client library for the service so we can use it for exsiting windows applications.

on the client side:

    public bool VerifyData(Cobject data)
    {
        try
        {  
            HttpClient c = new HttpClient();
            c.BaseAddress = BaseAddress;
            c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(JsonHeader));
            c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(HTMLHeader));
            c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(TextHeader));
            var asyncResponse = this.PostAsJsonAsync(url, data);
            asyncResponse.Wait();
            asyncResponse.Result.EnsureSuccessStatusCode();
            return true;
        }
        catch (HttpRequestException hre)
        {
            Console.WriteLine(hre.Message);
            return false;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            return false;
        }
    }

The problem here is I don't see any messages that were added to the ModelState on the respose. I see the status(ExpectionFailed) but no messages. How do I retrive those messages on the client?

Upvotes: 8

Views: 5920

Answers (3)

freedeveloper
freedeveloper

Reputation: 4102

Actually you can resolve the problem using "CreateErrorResponse". This method has a overload that give you the opportunity to send back the ModelState

 [HttpGet]
        public HttpResponseMessage Get([FromUri]InputFilterModel filter)
        {
            try
            {
                if (filter == null || ModelState.IsValid == false)
                {
                   return (filter == null)? Request.CreateErrorResponse(HttpStatusCode.BadRequest,"Input parameter can not be null")
                                          : Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
            }

The model state return a Json with each field in the model with the validation problem. You can see it doing the query directly in the brower or with fiddler.

Upvotes: 0

Alisson Reinaldo Silva
Alisson Reinaldo Silva

Reputation: 10705

I created a strong typed object, and used like below:

public class BadRequestResponse
{

    public string Message { get; set; }
    public Dictionary<string, string[]> ModelState { get; set; }
}

and used it to deserialize JSON like this example:

    public async Task DoStuff()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = "http://localhost/";
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            RegisterUserRequest content = new RegisterUserRequest { ConfirmPassword = "foo", Password = "foo", Email = "foo@bar" };

            HttpResponseMessage response = await client.PostAsJsonAsync("api/Account/Register", content).ConfigureAwait(false);
            if (response.StatusCode == System.Net.HttpStatusCode.BadRequest)
            {
                BadRequestResponse message = await response.Content.ReadAsAsync<BadRequestResponse>().ConfigureAwait(false);
                // do something with "message.ModelState.Values"
            }
        }
    }

You might check for HttpStatusCode.ExpectationFailed instead of HttpStatusCode.BadRequest.

I also installed WebApi Client package (since this ReadAsAsync overload is an extension from this library):

Install-Package Microsoft.AspNet.WebApi.Client

Upvotes: 10

JLuis Estrada
JLuis Estrada

Reputation: 978

I use a "similar" code to make posts and gets to my web api. Let me copy some code so you can get the information from the ModelState

var responseTask = await client.SendAsync(request);
var result = responseTask.ContinueWith(async r =>
               {
                   var response = r.Result;

                   var value = await response.Content.ReadAsStringAsync();
                   if (!response.IsSuccessStatusCode)
                   {
                       var obj = new { message = "", ModelState = new Dictionary<string,string[]>() };
                       var x = JsonConvert.DeserializeAnonymousType(value, obj);
                       throw new AggregateException(x.ModelState.Select(kvp => new Exception(string.Format("{0}: {1}", kvp.Key, string.Join(". ", kvp.Value)))));
                   }

               }).Result;

I hope this do the trick for you :)

Upvotes: 12

Related Questions