19

I have developed a small webapi which has a few actions and returns my custom class called Response.

The Response class

public class Response
{
    bool IsSuccess=false;
    string Message;
    object ResponseData;

    public Response(bool status, string message, object data)
    {
        IsSuccess = status;
        Message = message;
        ResponseData = data;
    }
}

My webapi with actions

[RoutePrefix("api/customer")]
public class CustomerController : ApiController
{
    static readonly ICustomerRepository repository = new CustomerRepository();

    [HttpGet, Route("GetAll")]
    public Response GetAllCustomers()
    {
        return new Response(true, "SUCCESS", repository.GetAll());
    }

    [HttpGet, Route("GetByID/{customerID}")]
    public Response GetCustomer(string customerID)
    {
        Customer customer = repository.Get(customerID);
        if (customer == null)
        {
            throw new HttpResponseException(HttpStatusCode.NotFound);
        }
        return new Response(true, "SUCCESS", customer);
        //return Request.CreateResponse(HttpStatusCode.OK, response);
    }

    [HttpGet, Route("GetByCountryName/{country}")]
    public IEnumerable<Customer> GetCustomersByCountry(string country)
    {
        return repository.GetAll().Where(
            c => string.Equals(c.Country, country, StringComparison.OrdinalIgnoreCase));
    }
}

Now where I am stuck is that I do not know how to read the response data returned from the webapi actions and extract json from my response class. After getting json how could I deserialize that json to the customer class.

This is the way I am calling my webapi function:

private void btnLoad_Click(object sender, EventArgs e)
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:8010/");
    // Add an Accept header for JSON format.  
    //client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    // List all Names.  
    HttpResponseMessage response = client.GetAsync("api/customer/GetAll").Result;  // Blocking call!  
    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine("Request Message Information:- \n\n" + response.RequestMessage + "\n");
        Console.WriteLine("Response Message Header \n\n" + response.Content.Headers + "\n");
    }
    else
    {
        Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
    }
    Console.ReadLine();   
}
Questions
  1. How to get the response class the webapi returns at the client side

  2. How could I extract json from the response class

  3. How to deserialize the json to the customer class at client side


I use this code but still getting an error.

    var baseAddress = "http://localhost:8010/api/customer/GetAll";
    using (var client = new HttpClient())
    {
        using (var response =  client.GetAsync(baseAddress).Result)
        {
            if (response.IsSuccessStatusCode)
            {
                var customerJsonString = await response.Content.ReadAsStringAsync();
                var cust = JsonConvert.DeserializeObject<Response>(customerJsonString);
            }
            else
            {
                Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
            }
        }
    }

The error is:

An exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll but was not handled in user code

Additional information: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'WebAPIClient.Response[]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.

Why is the response causing this error?

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Monojit Sarkar
  • 2,353
  • 8
  • 43
  • 94
  • See http://www.dotnetperls.com/httpclient for a sample to read the content. Serialising and deserialising can be found here http://www.newtonsoft.com/json/help/html/serializingjson.htm – Murray Foxcroft Aug 26 '16 at 09:05
  • when i am reading like this way `var customerJsonString = await response.Content.ReadAsStringAsync();` then just getting this symbol `{}` stored in customerJsonString variable. where i am making the mistake? – Monojit Sarkar Aug 26 '16 at 09:18
  • 1
    What happens when you open your browser and navigate to: http://localhost:8010/api/customer/GetAll - does that return results? (i.e. testing the api without your client code). – Murray Foxcroft Aug 26 '16 at 09:20
  • 1
    I'm not quite sure why you've created a `Response` class. From your API you should just be able to return the object (or in this case the list of objects) from your repository. At the client, you then just need to get the string of the response (being JSON) via `response.Content.ReadAsStringAsync();` and then you can use something like JsonConvert to deserialize that Json string to a C# object. You would need the same class in your client in order to deserialize back - maybe created a shared library or something so you can import this in both projects (only a suggestion) – Geoff James Aug 26 '16 at 09:26

2 Answers2

37

On the client, include a read of the content:

    HttpResponseMessage response = client.GetAsync("api/customer/GetAll").Result;  // Blocking call!  
    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine("Request Message Information:- \n\n" + response.RequestMessage + "\n");
        Console.WriteLine("Response Message Header \n\n" + response.Content.Headers + "\n");
        // Get the response
        var customerJsonString = await response.Content.ReadAsStringAsync();
        Console.WriteLine("Your response data is: " + customerJsonString);

        // Deserialise the data (include the Newtonsoft JSON Nuget package if you don't already have it)
        var deserialized = JsonConvert.DeserializeObject<IEnumerable<Customer>>(custome‌​rJsonString);
        // Do something with it
    }

Change your WebApi not to use your Response class but rather an IEnumerable of Customer. Use the HttpResponseMessage response class.

Your WebAPI should only require:

[HttpGet, Route("GetAll")]
public IEnumerable<Customer> GetAllCustomers()
{
    var allCustomers = repository.GetAll();
    // Set a breakpoint on the line below to confirm
    // you are getting data back from your repository.
    return allCustomers;
}

Added code for a generic response class based on the discussion in the comments although I still recommend you don't do this and avoid calling your class Response. You should rather return HTTP status codes instead of your own. A 200 Ok, a 401 Unauthorised, etc. Also this post on how to return HTTP status codes.

    public class Response<T>
    {
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
        public IEnumerable<T> ResponseData { get; set; }

        public Response(bool status, string message, IEnumerable<T> data)
        {
            IsSuccess = status;
            Message = message;
            ResponseData = data;
        }
    }
Community
  • 1
  • 1
Murray Foxcroft
  • 12,785
  • 7
  • 58
  • 86
  • 1
    Was just about to, but this saves me writing the same answer :) To add to your answer, you could also use Json.NET (Newtonsoft.Json) to deserialize your Json string to C# classes at the client side - e.g. `var deserialized = JsonConvert.DeserializeObject>(customerJsonString);` – Geoff James Aug 26 '16 at 09:33
  • if i return response class which wrapped many things in it instead of IEnumerable then what will be bad so ? – Monojit Sarkar Aug 26 '16 at 09:48
  • if i return response class that that will not be serialize in json format ? – Monojit Sarkar Aug 26 '16 at 09:48
  • see my updated code where i highlight my error. i am trying to return response class but not working. i believe there must some way to return response class to client. what is wrong in my code with response class ? – Monojit Sarkar Aug 26 '16 at 09:55
  • 1
    I would really avoid calling your class Response and return HTTP status codes instead of your own. A 200 Ok, a 401 Unauthorised, etc (see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes). See also http://stackoverflow.com/questions/10655350/returning-http-status-code-from-web-api-controller. The error I can see in your Response class is that the ResponseData is an object, but should be an IEnumerable, alternatively a bit more advanced, it should be an IEnumerable (generic). – Murray Foxcroft Aug 26 '16 at 10:02
  • how could i use IEnumerable because i may return one customer object or may be many. can u plzz post the modified response code and show me how to return ? please help i am stuck here – Monojit Sarkar Aug 26 '16 at 10:07
  • Updated answer to include a how - to. I still don't recommend it though. – Murray Foxcroft Aug 26 '16 at 10:18
  • 1
    @MonojitSarkar - *Why* exactly do you want to have your own response class returned? As I've already stated in comments on OP, and Murray has quite rightly said - there is no point; you're just abstracting what you're returning completely unnecessarily. The best practice when sending/consuming API data is to return the object/list of object from your repository (database etc.) where possible and handle unauthorised/invalid requests through HTTP status codes - which are uniform and that's what they're for!. You don't need to reinvent the wheel! – Geoff James Aug 26 '16 at 10:22
  • how could i send error message or exception to client by `HttpResponseMessage` class ? – Monojit Sarkar Aug 26 '16 at 10:25
  • @MurrayFoxcroft show me how my action should look like if use your generic Response class ? please one action with generic Response class. thanks – Monojit Sarkar Aug 26 '16 at 10:28
  • @MurrayFoxcroft how could i return error message to client using `HttpResponseMessage` ? suppose i am sending data from client to web api and web api is validation and if data is not right say suppose customer name is empty or null or email is wrong then we have to return error message to client..........tell me how could i do that? – Monojit Sarkar Aug 26 '16 at 11:09
  • 1
    For WebApi, what you should be doing is returning a 400 Bad Request. Please follow this article: http://www.asp.net/web-api/overview/error-handling/exception-handling – Murray Foxcroft Aug 26 '16 at 11:24
6

OR you can convert on same call

  TResponse responseobject = response.Content.ReadAsAsync<TResponse>().Result;
            responseJson += "hostResponse: " + JsonParser.ConvertToJson(responseobject);
            //_logger.Debug($"responseJson : {responseJson}", correlationId);
Taran
  • 2,895
  • 25
  • 22