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,
};
3. Cookie 示例
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 中,有以下請求相關的方法
- CancelPendingRequests
- DeleteAsync
- GetAsync
- GetByteArrayAsync
- GetStreamAsync
- GetStringAsync
- PostAsync
- PutAsync
- SendAsync
其中, CancelPendingRequests 是取消該實例所有掛起的請求,不是請求類型。
SendAsync 用於處理送 HttpRequestMessage(表示一條 HTTP 請求消息),比較原生。
對於 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();
}
}
2. Header
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);
}
}
}