C# HttpClient 请求认证、数据传输笔记

2019年12月15日 2415点热度 0人点赞 0条评论
内容目录

C# HttpClient 请求认证、数据传输笔记

一,授权认证

客户端请求服务器时,需要通过授权认证许可,方能获取服务器资源,目前比较常见的认证方式有 Basic 、JWT、Cookie。

HttpClient 是 C# 中的 HTTP/HTTPS 客户端,用于发送 HTTP 请求和接收来自通过 URI 确认的资源的 HTTP 响应。下面以具体代码做示范。

1. 基础认证示例

        // Basic基础认证
        public async Task Basic(string user, string password, string url)
        {
            // 如果认证页面是 https 的,请参考一下 jwt 认证的 HttpClientHandler
            // 创建  client 
            HttpClient client = new HttpClient();

            // 创建身份认证
            // using System.Net.Http.Headers;
            AuthenticationHeaderValue authentication = new AuthenticationHeaderValue(
                "Basic",
                Convert.ToBase64String(Encoding.UTF8.GetBytes($"{user}:{password}")
                ));


            client.DefaultRequestHeaders.Authorization = authentication;

            byte[] response = await client.GetByteArrayAsync(url);
            client.Dispose();
        }

可以看到 Basic 认证的安全程度非常低,多用于路由器和嵌入式设备,而且往往不会使用 HTTPS。

2. JWT 认证示例

        // Jwt认证
        public async Task Bearer(string token, string url)
        {
            // HttpClientHandler及其派生类使开发人员能够配置各种选项, 包括从代理到身份验证。
            // helpLink https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclienthandler?view=netframework-4.8
            var httpclientHandler = new HttpClientHandler();

            // 如果服务器有 https 证书,但是证书不安全,则需要使用下面语句
            // => 也就是说,不校验证书,直接允许
            httpclientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true;

            using (var httpClient = new HttpClient(httpclientHandler))
            {
                // 创建身份认证
                // System.Net.Http.Headers.AuthenticationHeaderValue;
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
                await httpClient.GetAsync(url);
                httpClient.Dispose();
            }
        }

JWT 认证,需要客户端携带 token ,token 是一段加密过的字符串,关于原理这里不多说,token 是通过客户端 header 携带的。

另外,对于测试的 Web 应用或者内网应用, HTTPS 证书可能不是公网国际认证的证书,就需要跳过认证,直接允许访问使用。

            var httpclientHandler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,
            };

HttpClient 中,Cookie 有两种处理方式。

一种是已经知道 Cookie ,直接将 Cookie 存储到 HttpClient 中;另一种是还没有 Cookie ,通过账号密码登录获取到 Cookie ,自动存储到 HttpClient 对象中,接着使用当前 HttpClient 对象请求 URL。

两种方式的设定,是通过 HttpClientHandler 的 UseCookies 属性设置的。

示例

            var httpclientHandler = new HttpClientHandler()
            {
                UseCookies = true
            };

​ UseCookies 获取或设置一个值,该值指示处理程序是否使用 CookieContainer 属性存储服务器 Cookie,并在发送请求时使用这些 Cookie。

方式1:

        // 先用账号密码登陆再请求
        public async Task Cookie(string user, string password, string loginUrl, string url)
        {
            var httpclientHandler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,
                UseCookies = true
            };
            // 如果服务器有 https 证书,但是证书不安全,则需要使用下面语句
            // => 也就是说,不校验证书,直接允许

            var loginContent = new FormUrlEncodedContent(new[]
            {
             new KeyValuePair<string,string>("user",user),
             new KeyValuePair<string, string>("password",password)
            });
            using (var httpClient = new HttpClient(httpclientHandler))
            {
                // 先登陆
                var result = await httpClient.PostAsync(loginUrl, loginContent);
                // 登陆成功后,客户端会自动携带 cookie ,不需要再手动添加
                //if (result.IsSuccessStatusCode)
                //{
                //    /*
                //     * 如果请求成功
                //     */
                //}

                var result2 = await httpClient.GetAsync(url);
                // httpclient 已经携带 Cookie ,可以多次使用
                // var result3 = await httpClient.GetAsync(url3);
                // var result4 = await httpClient.GetAsync(url4);
                httpClient.Dispose();
            }
        }

方式2:

        //已经拿到 cookie ,直接使用 cookie 请求
        public async Task Cookie(string cookie, string url)
        {
            var httpclientHandler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,
                UseCookies = false
            };
            // 如果服务器有 https 证书,但是证书不安全,则需要使用下面语句
            // => 也就是说,不校验证书,直接允许


            using (var httpClient = new HttpClient(httpclientHandler))
            {
                httpClient.DefaultRequestHeaders.Add("Cookie", cookie);
                await httpClient.GetAsync(url);
                httpClient.Dispose();
            }
        }

二,请求类型

HTTP 请求里,有 GET、POST、DELETE、PUT 等请求方式。

HttpClient 中,有以下请求相关的方法

  1. CancelPendingRequests
  2. DeleteAsync
  3. GetAsync
  4. GetByteArrayAsync
  5. GetStreamAsync
  6. GetStringAsync
  7. PostAsync
  8. PutAsync
  9. SendAsync

其中, CancelPendingRequests 是取消该实例所有挂起的请求,不是请求类型。

SendAsync 用于处理送 HttpRequestMessage(表示一条 HTTP 请求消息),比较原生。

s

对于 GetAsync、PostAsync等请求方法,使用过程类似,下面是使用示例

        public async void Request(string url)
        {

            using (var httpClient = new HttpClient())
            {
                // HttpClient 中,所有 Get 请求都是异步的
                HttpResponseMessage result = await httpClient.GetAsync(url);

                // Task<>.Result 可以获取异步结果
                result = httpClient.GetAsync(url).Result;

                //var result1 = await httpClient.GetByteArrayAsync(url);
                //var result1 = await httpClient.GetStreamAsync(url);
                //var result1 = await httpClient.GetStringAsync(url);

                // ByteArrayContent

                FormUrlEncodedContent fromContent = new FormUrlEncodedContent(new[]
                {
                    new KeyValuePair<string,string>("Email","123@qq.com"),
                    new KeyValuePair<string, string>("Number","666")
                });
                // 使用 Post ,必须携带 继承 HttpContent 的对象
                // 就是说,Post 必须要上传数据
                result = await httpClient.PostAsync(url, fromContent);

                // 如果没有数据要上传,可以使用 null
                result = await httpClient.PostAsync(url, null);


                httpClient.Dispose();
            }

三,数据传输

HTTP/HTTPS 请求中,往往随着数据传输,例如表单提交、JSON上传、文件上传等,下面以代码示范。

1. Query

ASP.NET Core API 可以这样写

        [HttpPost("aaa")]
        public async Task<JsonResult> AAA(int? a, int? b)
        {
            if (a == null || b == null)
                return new JsonResult(new { code = 0, result = "aaaaaaaa" });
            return new JsonResult(new { code = 2000, result = a + "|" + b });
        }

HttpClient

        // URL Query 参数
        public void Query(string a, string b)
        {
            var httpclientHandler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,
            };
            using (var httpClient = new HttpClient(httpclientHandler))
            {

                var result = httpClient.PostAsync($"https://localhost:5001/test?a={a}&b={b}", null).Result;

                httpClient.Dispose();
            }
        }

Header 是以键值形式存储的,HttpClient 示例

        // Header 头
        public void Header()
        {
            var httpclientHandler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,
            };
            using (var httpClient = new HttpClient(httpclientHandler))
            {
                httpClient.DefaultRequestHeaders.Add("MyEmail", "123@qq.com");
                var result = httpClient.GetAsync($"https://localhost:5001/test").Result;

                httpClient.Dispose();
            }
        }

ASP.NET Core API 示例

        [HttpPost("ddd")]
        public async Task<JsonResult> DDD([FromHeader]int? a, [FromHeader]int? b)
        {
            if (a == null || b == null)
                return new JsonResult(new { code = 0, result = "aaaaaaaa" });
            return new JsonResult(new { code = 200, result = a + "|" + b });
        }

3. 表单

        //  表单提交
        // application/x-www-form-urlencoded
        public void From()
        {
            var httpclientHandler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,
            };

            var fromContent = new FormUrlEncodedContent(new[]
            {
             new KeyValuePair<string,string>("Id","1"),
             new KeyValuePair<string,string>("Name","痴者工良"),
             new KeyValuePair<string, string>("Number","666666")
            });

            using (var httpClient = new HttpClient(httpclientHandler))
            {
                var result = httpClient.PostAsync("https://localhost:5001/test", fromContent).Result;

                Console.WriteLine(result.Content.ReadAsStringAsync().Result);
                httpClient.Dispose();
            }
        }

4. JSON

除了 JSON ,还有

  • text/html
    application/javascript
    text/plain
    application/xml

他们都是使用 StringContent 来表示。

        // Json 等
        public void StringAnd(string json)
        {
            var httpclientHandler = new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (message, cert, chain, error) => true,
            };


            var jsonContent = new StringContent(json);

            // Json 是 StringContent,上传时要指定 Content-Type 属性,除此外还有
            // text/html
            // application/javascript
            // text/plain
            // application/xml
            jsonContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

            using (var httpClient = new HttpClient(httpclientHandler))
            {

                var result = httpClient.PostAsync("https://localhost:5001/test", jsonContent).Result;

                Console.WriteLine(result.Content.ReadAsStringAsync().Result);
                httpClient.Dispose();
            }
        }

5. 上传文件

API 这样写

      [HttpPost]    //上传文件是 post 方式,这里加不加都可以
        public async Task<IActionResult> UploadFiles(List<IFormFile> files)
        {
        // ...
        }

HttpClient 写法

        // 上传文件
        public async Task File(string filepath, string fromName, string url)
        {
            using (var client = new HttpClient())
            {
                FileStream imagestream = System.IO.File.OpenRead(filepath);
                // multipartFormDataContent.Add();
                var multipartFormDataContent = new MultipartFormDataContent()
                {
                    {
                        new ByteArrayContent(System.IO.File.ReadAllBytes(filepath)),    // 文件流
                        fromName,                                                       // 对应 服务器 WebAPI 的传入参数
                        Path.GetFileName(filepath)                                      // 上传的文件名称
                    }
                };
                /* 
                 * 如果服务器 API 写法是
                 * ([FromForm]IFromFile files)
                 * 那么上面的 fromName="files"
                 */
                // multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");

                HttpResponseMessage response = await client.PostAsync(url, multipartFormDataContent);
                if (!response.IsSuccessStatusCode)
                {

                    Console.WriteLine("up image error");
                    Console.WriteLine(response.RequestMessage);
                }
            }
        }
一个逗

痴者工良

高级程序员劝退师

文章评论