如何在 C# 中使用 HttpClient 读取 Web api 响应

Posted

技术标签:

【中文标题】如何在 C# 中使用 HttpClient 读取 Web api 响应【英文标题】:How to read webapi responses with HttpClient in C# 【发布时间】:2016-08-26 09:01:57 【问题描述】:

我对 webapi 还很陌生,并且开发了一个小型 webapi,它有一些操作并返回我的自定义类 Response

Response

public class Response

    bool IsSuccess=false;
    string Message;
    object ResponseData;

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

我的带有操作的 webapi

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

现在我卡住的地方是我不知道如何读取从 webapi 操作返回的响应数据并从我的响应类中提取 json。得到 json 后我怎么能deserialize 那个 json 到客户类。

这是我调用 webapi 函数的方式:

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

请帮我整理一下我的代码。

    如何获取webapi在客户端返回的响应类

    如何从响应类中提取 json

    如何将json反序列化为客户端的客户类

谢谢

编辑:我使用了这段代码,但仍然出现错误。

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

错误是:

Newtonsoft.Json.dll 中出现“Newtonsoft.Json.JsonSerializationException”类型的异常,但未在用户代码中处理

附加信息:无法将当前 JSON 对象(例如 "name":"value")反序列化为类型“WebAPIClient.Response[]”,因为该类型需要 JSON 数组(例如 [1,2,3])正确反序列化。

为什么响应会导致这个错误?

【问题讨论】:

请参阅dotnetperls.com/httpclient 以获取阅读内容的示例。序列化和反序列化可以在这里找到newtonsoft.com/json/help/html/serializingjson.htm 当我以这种方式阅读 var customerJsonString = await response.Content.ReadAsStringAsync(); 然后将这个符号 存储在 customerJsonString 变量中。我在哪里犯错了? 当您打开浏览器并导航到:localhost:8010/api/customer/GetAll 时会发生什么 - 会返回结果吗? (即在没有您的客户端代码的情况下测试 api)。 我不太清楚你为什么要创建 Response 类。从您的 API 中,您应该能够从存储库中返回对象(或者在本例中为对象列表)。在客户端,您只需通过response.Content.ReadAsStringAsync(); 获取响应的字符串(即 JSON),然后您可以使用 JsonConvert 之类的东西将该 Json 字符串反序列化为 C# 对象。您需要在客户端中使用相同的类才能反序列化 - 可能创建了一个共享库或其他东西,以便您可以在两个项目中导入它(只是一个建议) 【参考方案1】:

在客户端,包括阅读内容:

    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
    

更改您的 WebApi 以不使用您的 Response 类,而是使用 IEnumerableCustomer。使用 HttpResponseMessage 响应类。

您的 WebAPI 应该只需要:

[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;

根据 cmets 中的讨论添加了通用响应类的代码,尽管我仍然建议您不要这样做并避免调用您的类 Response。你应该返回HTTP status codes 而不是你自己的。 200 OK、401 Unauthorised 等。还有this post 关于如何返回 HTTP 状态代码。

    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;
        
    

【讨论】:

正要这样做,但这节省了我写相同的答案:) 要添加到您的答案中,您还可以使用 Json.NET (Newtonsoft.Json) 将您的 Json 字符串反序列化为 C# 类客户端 - 例如var deserialized = JsonConvert.DeserializeObject&lt;IEnumerable&lt;Customer&gt;&gt;(customerJsonString); 如果我返回包含很多东西的响应类而不是 IEnumerable 那么会有什么不好呢? 我真的会避免调用您的类 Response 并返回 HTTP 状态代码而不是您自己的。 200 OK、401 Unauthorised 等(请参阅en.wikipedia.org/wiki/List_of_HTTP_status_codes)。另见***.com/questions/10655350/…。我在您的 Response 类中看到的错误是 ResponseData 是一个对象,但应该是一个 IEnumerable,或者更高级一点,它应该是一个 IEnumerable(通用)。 @MonojitSarkar - 为什么您究竟想要返回自己的响应类?正如我已经在关于 OP 的 cmets 中所说的那样,Murray 说得很对 - 没有意义;您只是在完全不必要地抽象您要返回的内容。发送/使用 API 数据时的最佳实践是尽可能从您的存储库(数据库等)返回对象/对象列表,并通过 HTTP 状态代码处理未经授权/无效的请求 - 这是统一的,这就是它们的用途! .您无需重新发明***! 对于 WebApi,您应该做的是返回 400 Bad Request。请关注这篇文章:asp.net/web-api/overview/error-handling/exception-handling【参考方案2】:

或者您可以在同一通话中转换

  TResponse responseobject = response.Content.ReadAsAsync<TResponse>().Result;
            responseJson += "hostResponse: " + JsonParser.ConvertToJson(responseobject);
            //_logger.Debug($"responseJson : responseJson", correlationId);

【讨论】:

以上是关于如何在 C# 中使用 HttpClient 读取 Web api 响应的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 中使用 HttpClient 存储 cookie?

如何在 C# 中使用 HttpClient 发送文件和表单数据

如何使用 HttpClient 在 ASP.Net C# 中禁用分块传输编码

c#:如何使用 httpclient 发布异步请求并获取流?

如何在 C# HttpClient 中循环调用分页 URL 以从 JSON 结果中下载所有页面

C# HttpClient:如何在 POST 请求中发送查询字符串