【问题标题】:.Net Core HttpClient 在某些 get 调用上返回 NotFound 404
【发布时间】:2022-01-23 15:42:13
【问题描述】:

我正在编写一个 .NET Core API,它通过向外部网站发送 API 调用来收集数据并将其保存到数据库中。我已经编写了三种方法,Get_Access_Token、Get_Access 和 Get_Trips 前两个工作文件,但第三个返回 404 URL not found,有趣的是它适用于 Postman 和 Swagger UI。 这是我的代码

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace KnsPortal.Models.MediRouteCustom
{
    public class MRHttpClient 
    {
        /// <summary>
        /// Get and Set the Access Token - Bearer Token
        /// </summary>
        private static AccessToken? _accessToken;
        /// <summary>
        /// Get and Set the list of Access Keys and Funding Sources
        /// </summary>
        private static List<Access.Access>? _accessKey;
        /// <summary>
        /// Get the MediRoute Username from config
        /// </summary>
        private readonly string _clientId;
        /// <summary>
        /// Get MediRoute Password from config
        /// </summary>
        private readonly string _clientSecret;
        /// <summary>
        /// Get the GrantType from config, eg. 'Password'
        /// </summary>
        private readonly string _grantType;
        /// <summary>
        /// Get the API Host URL of MediRoute eg. https://external.medirouteapi.com
        /// </summary>
        private readonly string _apiHost;
        /// <summary>
        /// Set the number of days you want to collect the trips data, Setting 0 will collect the data of current day
        /// </summary>
        private readonly int _nextDaysSpan;
        /// <summary>
        /// Get the instance of HTTPClient
        /// </summary>
        private readonly HttpClient _httpClient;
        /// <summary>
        /// Either to included Unscheduled trips or not? 
        /// </summary>
        private readonly bool _includeUnScheduledTrips;
        /// <summary>
        /// Either to include UnPerformed Trips or not?
        /// </summary>
        private readonly bool _includeUnperformedTrips;
        /// <summary>
        /// Either to include Breaks or not?
        /// </summary>
        private readonly bool _includeBreaks;

        IConfigurationRoot configuration = new ConfigurationBuilder()
             .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
             .AddJsonFile("appsettings.json")
             .Build();
        //readonly HttpClient httpClient = new HttpClient();
        static MRHttpClient()
        {
            _accessToken = null!;
        }
        [JsonConstructor]
        public MRHttpClient() { }
        [JsonConstructor]
        public MRHttpClient( HttpClient _client)
        {   
            // Initializing the values on first call
            _httpClient = _client ?? throw new ArgumentNullException(nameof(_client));
            var config = configuration ?? throw new ArgumentNullException(nameof(configuration));
            _clientId = config.GetValue<string>("MediRoute:UserName");
            _clientSecret = config.GetValue<string>("MediRoute:Password");
            _grantType = config.GetValue<string>("MediRoute:GrantType");
            _apiHost = config.GetValue<string>("MediRoute:ApiHost");
            _nextDaysSpan = config.GetValue<int>("MediRoute:NextDaySpan");
            _includeUnScheduledTrips = config.GetValue<bool>("MediRoute:IncludeUnScheduledTrips");
            _includeUnperformedTrips = config.GetValue<bool>("MediRoute:IncludeUnPerformedTrips");
            _includeBreaks = config.GetValue<bool>("MediRoute:IncludeBreaks");
        }

        public async Task<List<MediRoute>> GetTrips(List<Access.Access> _acs, AccessToken _tkn)
        {
            var _totalTrips = new List<MediRoute>();
            if (_acs.Count > 0)
            {
                for (int i = 0; i < _acs.Count; i++)
                {
                    for (int j = 0; j < _nextDaysSpan + 1; j++)
                    {
                        DateTime _date = DateTime.Now.AddDays(j);
                        // set body parameters required by api 
                        var keyValues = new List<KeyValuePair<string, string>>
                        {
                            new KeyValuePair<string, string>("api_key", _acs[i].APIKey),
                            new KeyValuePair<string, string>("includeUnScheduled", _includeUnScheduledTrips.ToString()),
                            new KeyValuePair<string, string>("includeUnperformed", _includeUnperformedTrips.ToString()),
                            new KeyValuePair<string, string>("includeBreaks", _includeBreaks.ToString()),
                            new KeyValuePair<string, string>("date", _date.ToString("yyyy-MM-dd"))
                        };
                        // initialize a request message
                        //API Host: https://external.mediroutesapi.com
                        var request = new HttpRequestMessage
                        {
                            Method = HttpMethod.Get,
                            RequestUri = new Uri(_apiHost + "/api/v1/rides/getRidesByDate"),
                            Headers =
                            {
                                { HttpRequestHeader.Accept.ToString(), "application/json" },
                                { HttpRequestHeader.Authorization.ToString(), "Bearer "+_tkn.access_token }
                            },
                            Content = new FormUrlEncodedContent(keyValues)
                        };
                        // sent the api call
                        using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
                        // collect response
                        var responseContent = await response.Content.ReadAsStringAsync();
                        //Deserialize Object from a json format to Object
                        List<MediRoute> _newTrips = JsonConvert.DeserializeObject<List<MediRoute>>(responseContent);
                        if (_newTrips.Count > 0)
                        {
                            _totalTrips.AddRange(_newTrips);
                        }
                        // wait for 2 seconds to send next api call
                        Thread.Sleep(2000);
                    }
                    // wait for 2 seconds to send api calls for another trip provider
                    Thread.Sleep(2000);
                }
                return _totalTrips;
            }
            else return _totalTrips;
        }
    }
}

谁能指出我做错了什么?我也附上了参数快照。

任何帮助将不胜感激。

【问题讨论】:

  • server 返回 404。客户端无法更改服务器返回的内容。您必须检查服务器做了什么以及为什么它返回 404 - 也许它不喜欢这些参数?也许缺少一些标题?
  • 但同样适用于 Postman 和 Swagger。我刚刚复制粘贴了参数。
  • 然而服务器不同意。这意味着请求是不同的。人们无法猜测在每种情况下都进行了哪些调用,或者为什么服务器返回 404 而不是例如 401。也许服务器返回 404 是因为令牌不存在。使用 Fiddler 或其他调试代理查看实际的请求和响应是什么,以及它们在每种情况下有何不同
  • @PanagiotisKanavos 提出了一些重要的观点。我还想指出,如果您给我们一个 MCRE (stackoverflow.com/help/minimal-reproducible-example),您会发现人们将能够提供更多帮助 - 您基本上已经复制了大部分应用程序 - 这对我们或您的。尝试将问题隔离到一个易于重现的小代码块 - 这样做的机会是,您自己会注意到问题。如果你不这样做,它会更容易找到帮助
  • @PanagiotisKanavos 感谢您指出正确的方向。

标签: c# api .net-core dotnet-httpclient


【解决方案1】:

让我发布我自己的问题的答案,我是如何弄清楚的。查询字符串和发布数据是两组不同的参数。服务器期待查询字符串,我通过 FormUrlEncodedContent 发送。如果服务器明确要求查询字符串格式的数据,则不能使用替代。

我不得不更换

var keyValues = new List<KeyValuePair<string, string>>
                        {
                            new KeyValuePair<string, string>("api_key", _acs[i].APIKey),
                            new KeyValuePair<string, string>("includeUnScheduled", _includeUnScheduledTrips.ToString()),
                            new KeyValuePair<string, string>("includeUnperformed", _includeUnperformedTrips.ToString()),
                            new KeyValuePair<string, string>("includeBreaks", _includeBreaks.ToString()),
                            new KeyValuePair<string, string>("date", _date.ToString("yyyy-MM-dd"))
                        };
                        // initialize a request message
                        //API Host: https://external.mediroutesapi.com
                        var request = new HttpRequestMessage
                        {
                            Method = HttpMethod.Get,
                            RequestUri = new Uri(_apiHost + "/api/v1/rides/getRidesByDate"),
                            Headers =
                            {
                                { HttpRequestHeader.Accept.ToString(), "application/json" },
                                { HttpRequestHeader.Authorization.ToString(), "Bearer "+_tkn.access_token }
                            },
                            Content = new FormUrlEncodedContent(keyValues)
                        };

var uriBuilder = new UriBuilder(_apiHost+ "/api/v1/rides/getRidesByDate");
                        var paramValues = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
                        paramValues.Add("api_key", _acs[i].APIKey);
                        paramValues.Add("includeUnScheduled", _includeUnScheduledTrips.ToString());
                        paramValues.Add("includeUnperformed", _includeUnperformedTrips.ToString());
                        paramValues.Add("includeBreaks", _includeBreaks.ToString());
                        paramValues.Add("date", _date.ToString("yyyy-MM-dd"));
                        uriBuilder.Query = paramValues.ToString();
                        var url = uriBuilder.Uri.ToString();
                        //
                        // initialize a request message
                        var request = new HttpRequestMessage
                        {
                            Method = HttpMethod.Get,
                            RequestUri = new Uri(uriBuilder.Uri.ToString()),
                            Headers =
                            {
                                { HttpRequestHeader.Accept.ToString(), "application/json" },
                                { HttpRequestHeader.Authorization.ToString(), "Bearer "+_tkn.access_token }
                            }
                        };

它成功了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-26
    相关资源
    最近更新 更多