HttpClient GetAsync 不按预期工作

5

在使用Postman测试我的Web API时,API可以正常执行!

但是当在客户端应用程序中使用HttpClient运行代码时,代码执行没有错误,但服务器上没有预期的结果。这是什么原因呢?

来自我的客户端应用程序:

private string GetResponseFromURI(Uri u)
{
    var response = "";
    HttpResponseMessage result;
    using (var client = new HttpClient())
    {
        Task task = Task.Run(async () =>
        {
            result = await client.GetAsync(u);
            if (result.IsSuccessStatusCode)
            {
                response = await result.Content.ReadAsStringAsync();
            }
        });
        task.Wait();
    }
    return response;
}

这是API控制器:

[Route("api/[controller]")]
public class CartsController : Controller
{
    private readonly ICartRepository _cartRepo;

    public CartsController(ICartRepository cartRepo)
    {
        _cartRepo = cartRepo;
    }

    [HttpGet]
    public string GetTodays()
    {
        return _cartRepo.GetTodaysCarts();
    }

    [HttpGet]
    [Route("Add")]
    public string GetIncrement()
    {
        var cart = new CountedCarts();
        _cartRepo.Add(cart);

        return _cartRepo.GetTodaysCarts();
    }

    [HttpGet]
    [Route("Remove")]
    public string GetDecrement()
    {
        _cartRepo.RemoveLast();
        return _cartRepo.GetTodaysCarts();
    }


}

请注意,这些API调用在Postman中调用时可以按预期工作。

2
警告,HttpClient 应该作为单个实例重复使用。将其包装在 using 语句中或为每个调用创建一个新的实例可能会产生不良影响。 - maccettura
1
使用 Fiddler 比较两个请求。可能缺少头部信息。 - burnsi
感谢@maccettura的提醒,我将httpclient移至静态字段,结果相同。 - CAD.Dev
抱歉,我并不是想暗示那会解决你的问题。我只是想指出 HttpClient 是为数不多的几个实例之一,在每次使用后都不需要立即处理。 - maccettura
1
我只是想指出,在我的情况下,处理HTTPClient没有产生任何“不利”的影响,这并不是问题的原因。 - CAD.Dev
你如何访问这些动作方法 - GetTodays、GetIncrement 和 GetDecrement?能给我展示一下对应的URL吗? - Win
5个回答

21

不应该在client.GetAsync中使用await,因为它由.Net平台管理,你只能同时发送一个请求。

只需像这样使用:

var response = client.GetAsync("URL").Result;  // Blocking call!

            if (response.IsSuccessStatusCode)
            {
                // Parse the response body. Blocking!
                var dataObjects = response.Content.ReadAsAsync<object>().Result;

            }
            else
            {
                var result = $"{(int)response.StatusCode} ({response.ReasonPhrase})";
               // logger.WriteEntry(result, EventLogEntryType.Error, 40);
            }

1
我不明白为什么,但它按照你说的方式运行,否则我的调试器会停在等待行上,再也无法继续执行!我想我需要重新学习一下 await 和 result 的工作原理!谢谢你,你救了我的一天。 - A Khudairy
这个救了我的一天。在 await client.GetAsync(..) 之后,我的调试器跳过了方法的其余部分。 - Nir Lanka

8

您正在使用“fire-and-forget”方法。在您的情况下,您需要等待结果。

例如,

static async Task<string> GetResponseFromURI(Uri u)
{
    var response = "";
    using (var client = new HttpClient())
    {
        HttpResponseMessage result = await client.GetAsync(u);
        if (result.IsSuccessStatusCode)
        {
            response = await result.Content.ReadAsStringAsync();
        }
    }
    return response;
}

static void Main(string[] args)
{
    var t = Task.Run(() => GetResponseFromURI(new Uri("http://www.google.com")));
    t.Wait();

    Console.WriteLine(t.Result);
    Console.ReadLine();
}

我已经实现了这个功能,但行为没有改变。我开始觉得我应该使用put而不是get。在api中的路径是一个获取响应,它正在递增一个计数器...它只在第一次调用时起作用。 - CAD.Dev
在我们继续之前,你能检查一下 http://www.google.com 的链接吗? - Win
它确实可以使用我的Uri正常工作。当调用Uri时,计数器会增加并返回结果。我还有一个类似的api调用来减少计数器,但它只是不能递增。 - CAD.Dev
CartsController 违反了基本的 REST 原则 - HttpGet 不应该改变服务器上的任何内容 - 幂等和安全。 - Win

1

用于获取页面数据的简单示例。

public string GetPage(string url)
{
    HttpResponseMessage response = client.GetAsync(url).Result;

    if (response.IsSuccessStatusCode)
    {
        string page = response.Content.ReadAsStringAsync().Result;
        return "Successfully load page";
    }
    else
    {
        return "Invalid Page url requested";
    }
}

0
由于您能够使用Postman命中API端点,因此您应该能够在Postman中获取HttpClient的工作代码。只需点击右侧的“”即可。它提供了许多语言的代码,包括C#的HttpClient。

0

当使用httpclient时,我遇到了缓存控制的问题。

HttpBaseProtocalFilter^ filter = ref new HttpBaseProtocolFilter();
filter->CacheControl->ReadBehavior = Windows::Web::Http::Filters::HttpCacheReadBehavior::MostRecent;
HttpClient^ httpClient = ref new HttpClient(filter);

我不太确定预期结果是什么,也不知道你得到了什么结果,所以现在这只是一个猜测游戏。

当我使用HttpClient进行POST操作时,我发现手动添加标头比使用默认标头更有效。

auto httpClient = ref new HttpClient();
Windows::Web::Http::Headers::HttpMediaTypeHeaderValue^ type = ref new Windows::Web::http::Headers::HttpMediaTypeHeaderValue("application/json");
content->Headers->ContentType = type;

如果我不做我发现的这两件事情,对我来说,有一半的时间我的网络请求要么根本没有被发送,要么头部信息都乱了,而另一半时间它完美地工作。

我刚刚读到一个评论,你说它只会触发一次,这让我想到了缓存控制。我认为发生的事情是某些东西(Windows?)看到了两个完全相同的请求被发送,为了加快速度,它只是假设相同的答案,并从未真正发送第二个请求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接