C# HttpClient PostAsync 通过 SSL 返回 403 禁止访问

3
自从我们切换到带SSL的服务器后,当我进行POST请求时,我总是收到403禁止错误。但是如果我使用WebClient,它就可以正常工作。无论如何,我仍然希望能够使用HttpClient使其正常工作,因为我不得不改变很多代码,并且还有一个调用使用MultipartFormDataContent发布文件,我不能使用WebClient做到这一点。在WebClient中,我可以选择发布数据或上传文件,但我需要在一个调用中同时发布数据和文件。
以下是HttpClient和WebClient调用的示例:
HttpClient(403禁止错误)
 private async void Login_Click(object sender, RoutedEventArgs e)
    {
        Apic = CryptoEngine.DecryptAPI(Apic);
        if (API())
        {
            using (var content = new MultipartFormDataContent())
            {
                if (EmailText.Text.Contains("@") && EmailText.Text.Contains("."))
                {
                    content.Add(new StringContent(EmailText.Text), "\"email\"");
                }
                else
                {
                    content.Add(new StringContent(EmailText.Text), "\"username\"");
                }

                content.Add(new StringContent(PasswordText.Password), "\"password\"");
                using (var httpClient = new HttpClient())
                {
                    httpClient.DefaultRequestHeaders.Clear();
                    httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json");
                    httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36");
                    httpClient.DefaultRequestHeaders.Add("appName", "CloudMetaPrinter");
                    httpClient.DefaultRequestHeaders.Add("latitude", Apic.Latitude);
                    httpClient.DefaultRequestHeaders.Add("longitude", Apic.Longitude);
                    httpClient.DefaultRequestHeaders.Add("hostname", Apic.IP);
                    httpClient.DefaultRequestHeaders.Add("hostType", "PC");
                    var httpResponse = httpClient.PostAsync(url + "/" + api + "/auth/login", content);
                    responseContent = await httpResponse.Result.Content.ReadAsStringAsync();
                    Result result = JsonConvert.DeserializeObject<Result>(responseContent);

WebClient(正常工作)


private void Login_Click(object sender, RoutedEventArgs e)
    {
        Apic = CryptoEngine.DecryptAPI(Apic);
        if (API())
        {
            string data;
            if (EmailText.Text.Contains("@"))
            {
                data = JsonConvert.SerializeObject(new
                {
                    email = EmailText.Text,
                    password = PasswordText.Password
                });
            }
            else
            {
                data = JsonConvert.SerializeObject(new
                {
                    username = EmailText.Text,
                    password = PasswordText.Password
                });
            }
            using (var httpClient = new WebClient { UseDefaultCredentials = true })
            {
                httpClient.Headers.Add("appName", "CloudMetaPrinter");
                httpClient.Headers.Add("latitude", Apic.Latitude);
                httpClient.Headers.Add("longitude", Apic.Longitude);
                httpClient.Headers.Add("hostname", Apic.IP);
                httpClient.Headers.Add("hostType", "PC");
                httpClient.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
                var httpResponse = httpClient.UploadData(url + "/" + api + "/auth/login", "POST", Encoding.UTF8.GetBytes(data));
                var str = System.Text.Encoding.Default.GetString(httpResponse);
                Result result = JsonConvert.DeserializeObject<Result>(str);

我试图在标题中进行更改,使用 UseDefaultCredentials = true 等等,但是没有任何作用。
编辑:我正在使用 .net core 3.0 编辑2:使用 HttpClient 的 GetAsync 正常工作,问题只出现在 PostAsync 中。
好的,所以我尝试与 Fiddler 进行比较,并更改一些内容,使 httpclient 调用更像 webclient,但仍然得到了 403 错误。 左侧是 WebClient,右侧是 HttpClient 比较图片

1
无论代码如何,您都可以轻松使用Fiddler来查看向服务器发送POST请求时这两种方法之间的差异,以帮助您解决未来可能遇到的问题。 - TheGeneral
在每种情况下,您使用的凭据不同。使用WebClient时,您使用的是Windows身份验证。而使用HttpClient,则没有任何身份验证。您甚至没有传递基本身份验证标头。请更改HttpClient代码以同时使用Windows身份验证。 - Panagiotis Kanavos
@mortb 不,那个答案最终使用了WebClient,我需要使用HttpClient。 - Phantom
SSL没有效果。你在两个片段中的代码完全不同。你没有使用任何凭据与HttpClient,实际上是进行匿名调用。其余的调用也完全不同。使用WebClient,你正在使用JSON字符串进行POST(这就是UploadData所做的)。而使用HttpClient,你正在发布一个表单。 - Panagiotis Kanavos
@TheGeneral,我用Fiddler进行了测试,并进行了一些更改,使其尽可能相似,但仍然存在一些差异,请您检查我在最后编辑中放置的图像并告诉我我缺少了什么?我已经尝试搜索,但没有找到任何信息。 - Phantom
显示剩余2条评论
3个回答

10

[解决方案] 添加以下标头部分:

Headers.Add("User-Agent: Other");


1
出乎意料的是,这对我起作用了。看起来一些服务器会拒绝没有定义用户代理的调用。HttpClient 不提供默认值。request.Headers.Add("User-Agent", "Other"); - Chris Jensen
1
@ChrisJensen 是的。该服务器的网络端申请了一个作为用户代理所需的安全角色。这就是为什么会收到“禁止访问”错误的原因。 - Thilina Chamika

2

确保没有代理阻止了调用。要么不使用代理,要么确保已正确配置地址和端口。使用mortb示例中的处理程序:

using (var handler=new HttpHandler())
{
     handler.UseProxy=false;
     var client=new HttpClient(handler);
     var result=await client.PostAsync(url);
     //… retrieve the content
}

2

您需要将一个HttpClienthandler对象传递到HttpClient的构造函数中。 UseDefaultCredentials属性已经移动到HttpClienthandler

示例:

   using(var handler = new HttpClientHandler())
   {
      handler.UseDefaultCredentials = true;

      using(var client = new HttpClient(handler))
      {

         var response = await client.GetAsync("http://www.contoso.com/");
         response.EnsureSuccessStatusCode();

         var responseBody = await response.Content.ReadAsStringAsync();
   }

请注意,频繁地Dispose() HttpClient可能效率不高,最好重复使用它。 https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler?view=netcore-3.1

我已经尝试过了,但我仍然得到403禁止访问的错误,至少在POST请求中是这样,在GET请求中我没有设置任何东西,它可以正常运行。 - Phantom
很难比较你问题中的这两个示例。在你的 HttpClient 示例中,你发布了 MultipartFormDataContent,在 Web 客户端示例中则是 JSON 字符串,所以它们是在做不同的事情。你尝试过使用 HttpClient 发布 JSON 吗? - mortb
我知道这很不同,但如果我在没有SSL服务器机器人的情况下使用调用,则两种方式都可以正常工作,只有在SSL的情况下才会出现问题,请查看我在Fiddler中制作的比较图像,我进行了一些更改,以使机器人调用尽可能相似,但我仍然有一些最小的差异,我不知道如何改变它。 - Phantom

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