第一次 foreach 循环后 hTTPResponse.Content.ReadAsStringAsync().Result 上的空消息

Posted

技术标签:

【中文标题】第一次 foreach 循环后 hTTPResponse.Content.ReadAsStringAsync().Result 上的空消息【英文标题】:Null message on hTTPResponse.Content.ReadAsStringAsync().Result after 1st foreach loop 【发布时间】:2019-07-17 22:58:30 【问题描述】:

在 foreach 循环中调用 HttpClient getAsync 时出现空结果消息的问题。

我需要使用对象列表进行迭代,以便通过 HttpClient 调用 API。在第一个循环之后,HttpResponseMessage 的 result() 出现空消息。

我试过 CacheControl.NoCache = true。它不工作。

public async Task<List<Common.ProgressResponse>> RunAsync()

    List<Response> ListOfResponses = new List<Responses>();

    try
    
        _client.BaseAddress = new Uri([URL]);
        _client.DefaultRequestHeaders.Accept.Clear();
        _client.DefaultRequestHeaders.Clear();
        _client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue()  NoCache = true ;
        _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        _client.DefaultRequestHeaders.Add([KEY], [VALUE]);

        ListOfResponses = await SomeFunction();

    
    catch (Exception ex)
    
        //Exceptions here...
    
    return ListOfResponses;



private async Task<List<ListOfResponses>> SomeFunction()

    List<Response> responses = new List<Response>();
    string aPISuffix = string.Format("0/1", [APISUFFIX1], [APISUFFIX2]);

    foreach (Object obj in ListOfObject)
    
        _client.DefaultRequestHeaders.Add("Param1", obj.Param1);
        if (!string.IsNullOrEmpty(obj.Param2))
            _client.DefaultRequestHeaders.Add("Param2", obj.Param2);

        Response response = new Response();


/*This is where the issue is .begin.*/
        HttpResponseMessage hTTPResponse = await _client.GetAsync(aPISuffix).ConfigureAwait(false);
        string result = hTTPResponse.Content.ReadAsStringAsync().Result;
/*This is where the issue is .end.*/


        if (hTTPResponse.IsSuccessStatusCode)
        
            response = [Code here...]
            //Codes here...
        

        responses.Add(response);
    

    return responses;

On: '字符串结果 = hTTPResponse.Content.ReadAsStringAsync().Result;' 我希望结果消息在循环通过 ListOfObjects 时具有值。

【问题讨论】:

你为什么到处都用await,却突然在那一行上用.Result 您应该在阅读内容之前检查 httpResponse 状态码。你可能会看到一些惊喜。我看不到 httpclient 是如何启动的,但我会小心重置默认标头,而是尝试在每个请求的基础上指定它们,以避免多个请求相互冲突跨度> 因为调用函数都是异步的... @ESG httpClient 是类中的静态全局变量。该类使用 using() 语句进行实例化。 @praetorean 也要小心静态 httpclients。如果您将此类注册为单例,那么您的 DNS 条目将变得陈旧。 【参考方案1】:

更新:每条评论

首先,我通常会尽量避免将 .Result 用于任务。我也不认为添加缓存标头是问题,因为您正在进行服务器到服务器的调用。

我经常做的一件事是在尝试阅读之前评估响应。我也更喜欢使用 HttpRequestMessage 以便我可以管理它的处置。

这是一个演示如何调用的简单示例:

using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace Testing

    class Program
    
        static void Main(string[] args)
        
            TestAsync().Wait();
        

        static async Task TestAsync()
        
            var urls = new string[]
            
                "https://***.com/questions/57084989/null-message-on-httpresponse-content-readasstringasync-result-after-1st-foreac",
                "https://***.com/users/2025711/rogala"
            ;

            using (var httpClient = new HttpClient())
            
                foreach (var url in urls)
                
                    using (var request = new HttpRequestMessage(HttpMethod.Get, url))
                    
                        Console.WriteLine($"Request: url".PadLeft(5,'*').PadRight(5, '*'));
                        var response = await httpClient.SendAsync(request)
                            .ConfigureAwait(false);

                        if (response.IsSuccessStatusCode)
                        
                            var body = await response.Content.ReadAsStringAsync()
                            .ConfigureAwait(false);
                            Console.WriteLine($"body.LengthEnvironment.NewLine");
                        
                        else
                        
                            Console.WriteLine($"*Bad request: response.StatusCode");
                        
                        
                    
                
            

            Console.ReadKey();
        
    

要记住的另一件事是插槽耗尽。如果此代码在服务器上运行,那么一旦您的服务获得一些负载,您将遇到套接字耗尽。我强烈建议使用HttpClientFactory 来处理 HttpClients。

关于内容字符串为空的原因,可能是因为服务器没有返回可能是服务器问题的内容。服务器可能还限制了您,导致没有内容。我建议查看Polly 来处理某些响应代码。

【讨论】:

什么?这并不能真正回答问题,您只是修复了一些小错误。如果您声称使用 .Result 是问题,请解释原因。 不,我展示的模式确实有效。正如评论中提到的,在检查结果之前,您不应尝试阅读正文。此外,此模式的不同之处在于生成并发送新的 HttpRequestMessage 与调用 get。最后,我在这里可能是错的,但是服务器到服务器的调用不需要缓存。 该评论应该是您答案的第一部分。如果您从用户部分阅读此答案,则不清楚如何或为什么会解决此问题【参考方案2】:

解决了...我在 foreach() 循环中移动了 .DefaultRequestHeaders 值。 我怀疑它们在每次循环迭代时都会堆积起来。我必须在 GetAsync() 之前清除它们并设置它们

此处更新代码:

public async Task<List<Common.ProgressResponse>> RunAsync()

    List<Response> ListOfResponses = new List<Responses>();

    try
    
        _client.BaseAddress = new Uri([URL]);
        ListOfResponses = await SomeFunction();

    
    catch (Exception ex)
    
        //Exceptions here...
    
    return ListOfResponses;



private async Task<List<ListOfResponses>> SomeFunction()

    List<Response> responses = new List<Response>();
    string aPISuffix = string.Format("0/1", [APISUFFIX1], [APISUFFIX2]);

    foreach (Object obj in ListOfObject)
    
        /*Moved Code .begin.*/
        _client.DefaultRequestHeaders.Accept.Clear();
        _client.DefaultRequestHeaders.Clear();
        _client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue()  NoCache = true ;
        _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        _client.DefaultRequestHeaders.Add([KEY], [VALUE]);      

        /*Moved Code .end.*/

        _client.DefaultRequestHeaders.Add("Param1", obj.Param1);
        if (!string.IsNullOrEmpty(obj.Param2))
            _client.DefaultRequestHeaders.Add("Param2", obj.Param2);

        Response response = new Response();
        HttpResponseMessage hTTPResponse = await _client.GetAsync(aPISuffix).ConfigureAwait(false);

        if (hTTPResponse.IsSuccessStatusCode)
        
            string result = hTTPResponse.Content.ReadAsStringAsync().Result;        
            response = [Code here...]
            //Codes here...
        

        responses.Add(response);
    

    return responses;

【讨论】:

这正是我喜欢使用 HttpRequestMessage 的原因。必须清除标题似乎有点奇怪。恕我直言 - 通过将代码添加到消息中,代码更清晰。单元测试也更容易。

以上是关于第一次 foreach 循环后 hTTPResponse.Content.ReadAsStringAsync().Result 上的空消息的主要内容,如果未能解决你的问题,请参考以下文章

Foreach 循环仅针对第一组工作人员返回错误消息

PHP foreach循环里面调用函数,需要等函数返回成功才能进入下一次循环,怎么弄呢?

perl之文本文件的嵌套循环

foreach 循环,判断哪个是循环的最后一次迭代

在刀片 laravel 中多次防止 foreach 循环 html 标记

在 forEach 循环完成后运行回调函数