Net-中使用Httpclient

314 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

HttpClient

目前项目需要与百度AI和腾讯AI进行接入,不可避免的需要使用到HttpClient这个工具来发送Http请求,边学边用啦,但是目前还是卡着一个问题,还在寻求解决方案

.Netcore中请求的方式

NETCore提供了三种不同类型用于生产的REST API: HttpWebRequest ;WebClient ;HttpClient ,开源社区创建了另一个名为RestSharp 的库,使用这四种方式都可以进行HTTP请求的发送

HttpWebRequest

这是.NET创建者最初开发用于使用HTTP请求的标准类。使用HttpWebRequest可以让开发者控制请求/响应流程的各个方面,如 timeouts, cookies, headers, protocols。另一个好处是HttpWebRequest类不会阻塞UI线程。例如,当您从响应很慢的API服务器下载大文件时,您的应用程序的UI不会停止响应。

然而,强大的个性化操作带来了极大的复杂性。为了简单起见,GET您需要至少五行代码;

HttpWebRequest http = (HttpWebRequest)WebRequest.Create("http://example.com");
                WebResponse response = http.GetResponse();
                Stream stream = response.GetResponseStream();
                using (var streamtemn = File.Create("路径"))
                {
                    stream.CopyTo(streamtemn);
                }

如果对http协议不是了如指掌,使用HttpWebRequest会增加你的开发成本,除非你需要非常细节的处理和底层的控制,另外HttpWebRequest库已经过时,不适合业务中直接使用,他更适用于框架内部操作。

WebClient

WebClient是一种更高级别的抽象,是HttpWebRequest为了简化最常见任务而创建的,使用过程中你会发现他缺少基本的header,timeoust的设置,不过这些可以通过继承httpwebrequest来实现。使用WebClient可能比HttpWebRequest直接使用更慢(大约几毫秒)。但这种“低效率”带来了巨大的好处:它需要更少的代码和隐藏了细节处理,更容易使用,并且在使用它时你不太可能犯错误。同样的请求示例现在很简单只需要两行而且内部周到的处理完了细节:

using (WebClient webClient = new WebClient())
                {
                    webClient.DownloadFile("http://example.com", "路径");
                }

Httpclient

提供了强大的功能,提供了异步支持,可以配合async await 进行使用 ,要注意,需要配合HttpclientFactory使用,可以避免socket耗尽问题。

HttpclientFactory

在.NETCore平台的2.1新增了HttpClientFactory,虽然HttpClient这个类实现了disposable,但使用它的时候用声明using包装块的方式通常不是最好的选择。处理HttpClient,底层socket套接字不会立即释放。该HttpClient类是专为多个请求重复使用而创建的。需要不同的基地址,不同的HTTP标头和其他对请求个性化操作的场景时,需要手动管理多个HttpClient实例,为了简化HttpClient实例管理,.NET Core 2.1提供了一个新的HTTPClientFactory - 它可以创建,缓存和处理HttpClient实例

我们使用HttpClientFactory创建客户端。在幕后,HttpClientFactory将为我们创建一个新的HttpClient。但是等等,之前说过为每个请求使用新的HttpClient是很糟糕。但此处的创建的httpclient是在他所管理的池子中,并不每个请求都会是新的socket。

HttpClientFactory收集这些HttpClientHandler实例并管理它们的生命周期,以解决之前提到的一些问题。每次我们要求HttpClient时,我们都会得到一个新实例,它可能(或可能不)使用现有的HttpClientHandler。HttpClient本身并没有问题。

一旦创建,由此创建的所有HttpClientHandler将被默认保持约2分钟。这意味着针对同一个CreateClient的任何新请求都可以共享处理程序,因此也可以共享连接。当HttpClient存在时,它的处理程序将保持可用状态,并且它将再次共享连接。

两分钟后,每个HttpClientHandler都标记为已过期。过期状态只是标记它们,以便在创建任何新的HttpClient实例时不再使用它们。但是,它们不会立即销毁,因为其他HttpClient实例可能正在使用它们。HttpClientFactory使用后台服务监视过期的处理程序,一旦它们不再被引用,就可以正确释放它们,也允许它们的连接被关闭。

Restsharp

restsharp是开源社区贡献,具有HttpWebRequest的细节控制和WebClient的使用简单的优点从而让他功能强大的同时又简化了操作

  • HttpWebRequest 已经不推荐直接使用了,这已经作为底层机制,不适合业务代码使用
  • WebClient 不想为http细节处理而头疼的coder而生,由于内部已经处理了通用设置,某些情况可能导致性能不是很理想
  • RestSharp 兼具强大功能和友好api很适合业务中使用
  • HttpClient 更加适用于异步编程模型中

简单示范

Get

using System;
using System.Net.Http;
using System.Threading.Tasks;
​
namespace HttpClientStatus
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var client = new HttpClient();
​
            var result = await client.GetAsync("http://webcode.me");
            Console.WriteLine(result.StatusCode);
        }
    }
}
​
​

Post

using System;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
​
namespace HttpClientPost
{
    class Person
    {
        public string Name { get; set; }
        public string Occupation { get; set; }
​
        public override string ToString()
        {
            return $"{Name}: {Occupation}";
        }
    }
​
    class Program
    {
        static async Task Main(string[] args)
        {
            var person = new Person();
            person.Name = "John Doe";
            person.Occupation = "gardener";
​
            var json = JsonConvert.SerializeObject(person);
            var data = new StringContent(json, Encoding.UTF8, "application/json");
​
            var url = "https://httpbin.org/post";
            using var client = new HttpClient();
​
            var response = await client.PostAsync(url, data);
​
            string result = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(result);
        }
    }
}
​
​

Json请求

using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
​
namespace HttpClientJson
{
    class Contributor
    {
        public string Login { get; set; }
        public short Contributions { get; set; }
​
        public override string ToString()
        {
            return $"{Login,20}: {Contributions} contributions";
        }
    }
​
    class Program
    {
        private static async Task Main()
        {
            using var client = new HttpClient();
​
            client.BaseAddress = new Uri("https://api.github.com");
            client.DefaultRequestHeaders.Add("User-Agent", "C# console program");
            client.DefaultRequestHeaders.Accept.Add(
                    new MediaTypeWithQualityHeaderValue("application/json"));
​
            var url = "repos/symfony/symfony/contributors";
            HttpResponseMessage response = await client.GetAsync(url);
            response.EnsureSuccessStatusCode();
            var resp = await response.Content.ReadAsStringAsync();
​
            List<Contributor> contributors = JsonConvert.DeserializeObject<List<Contributor>>(resp);
            contributors.ForEach(Console.WriteLine);
        }
    }
}
​
​

身份验证

using System;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
​
namespace HttpClientAuth
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var userName = "user7";
            var passwd = "passwd";
            var url = "https://httpbin.org/basic-auth/user7/passwd";
​
            using var client = new HttpClient();
​
            var authToken = Encoding.ASCII.GetBytes($"{userName}:{passwd}");
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
                    Convert.ToBase64String(authToken));
​
            var result = await client.GetAsync(url);
​
            var content = await result.Content.ReadAsStringAsync();
            Console.WriteLine(content);
        }
    }
}
​
​

在这里还可以贴一个实际开发的案例

  //汽车票识别
        public async Task<string> BusTicketAnalysis(byte[] image)
        {
            string host = "https://aip.baidubce.com/rest/2.0/ocr/v1/bus_ticket?access_token=" + Token;
            var base64 = Convert.ToBase64String(image);
            //Encode编码
            base64 = WebUtility.UrlEncode(base64);
            //拼接表单提交
            string str = $"image={base64}";
​
            var request = new HttpRequestMessage();
            request.Method = HttpMethod.Post;
            request.RequestUri = new Uri(host);
            request.Content = new StringContent(str);
            request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
            var response = await httpClient.SendAsync(request);
            response.EnsureSuccessStatusCode();
            var result = await response.Content.ReadAsStringAsync();
​
            return result;
        }

Httpcontent

在发请求的时候,发现其中的content是有不一样的,这边就需要注意一下。