这篇文章主要记录在用wpf开发应用程序从服务器端获取数据之前的准备工作。
1 HTTP标头信息
首先需要说明一下HTTP标头的信息,在使用wpf发送请求的过程中需要设置部分信息,大部分暂时用不到。
HTTP头信息
头信息由"键:值"组成。它们描述客户端或者服务器的属性、被传输的资源以及应该实现连接。
四种不同类型的头标
通用头标:即可用于请求,也可用于响应,是作为一个整体而不是特定资源与事务相关联。
请求头标:允许客户端传递关于自身的信息和希望的响应形式。
响应头标:服务器和于传递自身信息的响应。
实体头标:定义被传送资源的信息。即可用于请求,也可用于响应。
头标格式::
在Web的请求标头(Request Headers)中,有许多属性都是有用的,它们提供了关于客户端请求和期望响应的详细信息。以下是一些常见的有用请求标头属性:
User-Agent:
描述:向访问网站提供所使用的浏览器类型、操作系统及版本、CPU类型、浏览器渲染引擎、浏览器语言、浏览器插件等信息的标识。
用途:服务器可以根据User-Agent的值来判断客户端的类型和版本,从而提供合适的响应。
例如:Mozilla/5.0 (Windows NT 6.1;Win64;x64) AppleWebKit537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Accept:
描述:浏览器客户端用来告诉服务端能接受什么类型的响应。
用途:通过指定MIME类型(如text/html、application/json等),客户端可以告诉服务器它期望接收哪种类型的响应内容。
例如:Accept:text/html代表浏览器可以接受服务器回发html文当,如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(nonacceptable)通配符代表任意类型。如:Accept:/*代表浏览器可以处理所有类型
Accept-Encoding:
描述:浏览器客户端用来告诉服务器能接受什么编码格式,包括字符编码、压缩方式等。
用途:客户端可以告诉服务器它支持哪些压缩算法(如gzip、deflate等),以便服务器可以选择最合适的压缩方式来传输数据。
例如:Accept-Encoding:gzip,deflate
Accept-Language:
描述:浏览器客户端用来告诉服务器能接受什么语言。
用途:客户端可以指定它希望接收哪种语言的响应内容,这对于提供多语言支持的网站特别有用。
例如:Accept-Language:zh-CN,zh;q=0.9
Connection:
描述:客户端或服务端用来告诉对方当前TCP连接的状态。
用途:客户端可以告诉服务器它希望保持连接(keep-alive)还是希望每次请求后都关闭连接。
例如:Connection:close在响应结束后关闭连接
Host:
描述:指定要请求的资源所在的主机和端口。
用途:这是每个HTTP请求都必须包含的标头,它告诉服务器请求是针对哪个主机和端口的。
例如:我们在地址栏输入:http://www.baidu.com Host:www.baidu.com
Referer:
描述:浏览器客户端用来告诉服务器这个请求是从哪个页面链接过来的,即请求来源。
用途:服务器可以使用Referer标头来跟踪请求的来源,从而进行访问分析或防止恶意请求。
Cache-Control:
描述:客户端浏览器用来判断是否需要用本地缓存。
用途:客户端可以告诉服务器它是否希望使用缓存的数据,以及缓存数据的有效期等。
作用:客户端浏览器用来判断是否需要用本地缓存。默认值为private;常用值有private、no-cache、max-age、must-revalidate。具体场景举例:
a.打开新窗口时
值为private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。而如果指定了max-age值(单位为秒),那么在此值内的时间里就不会重新访问服务器,例如Cache-control:max-age=5(表示当访问此网页后的5秒内再次访问不会去服务器)
b.在地址栏回车
值为private或must-revalidate则只有第一次访问时会访问服务器,以后就不再访问。值为no-cache,那么每次都会访问。值为max-age,则在过期之前不会重复访问。
c.按后退按扭
值为private、must-revalidate、max-age,则不会重访问,值为no-cache,则每次都重复访问
d.按刷新按扭
无论为何值,都会重复访问
Cookie:
描述:客户端浏览器用来存储一些用户信息以便让服务器辨别用户身份的。
用途:服务器可以使用Cookie来跟踪用户的会话状态、存储用户偏好等。
Authorization:
描述:用于传递身份验证信息,如令牌(token)或基本身份验证的凭证。
用途:当请求需要身份验证时,客户端可以在Authorization标头中包含必要的凭证。
If-Modified-Since:
描述:把浏览器端缓存页面的最后修改时间发送到服务器去。
用途:如果资源自指定时间以来没有修改,服务器可以返回304状态码,客户端就可以直接使用本地缓存。
例如:Wed,30May201808:32:42GMT
If-None-Match:
描述:与ETag一起工作,用于判断资源是否已被修改。
用途:如果资源的ETag没有改变,服务器可以返回304状态码,客户端就可以使用本地缓存。
例如:lf-None-Match:W/"3119-1437038474000"
注意:lf-Modify-Since和lf-None-Match都可以给服务器用来判断所请求的文件距离上次访问之间是否被修改过,不过lf-Modify-Since只能精确到秒,而lf-None-Match只要文件修改过就会变化。Etag的使用场景:1.有些文件需要频繁更新,但是文件内容并没有变化。
这些请求标头属性在Web开发中非常有用,它们允许客户端和服务器之间进行更精细的交互和控制。开发者在编写网络应用程序时,需要了解这些标头的含义和用法,以便正确地设置和解析它们。
2 实现
2.1 设置标头
HttpClient 的 GetAsync 方法来发送 GET 请求,并希望设置与之前 fetch 请求中相同的请求头(除了 referrer 和 referrerPolicy,因为它们在 HttpClient 中没有直接的对应项),您可以按照以下方式修改代码:
csharp
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public class ApiClient
{
private readonly HttpClient httpClient;
private readonly string bearerToken;
public ApiClient(string token)
{
httpClient = new HttpClient();
bearerToken = token;
}
public async Task<string> GetDeptAndHospitalInfoAsync()
{
try
{
// 设置请求头
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
httpClient.DefaultRequestHeaders.Add("sec-ch-ua", "\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Google Chrome\";v=\"122\"");
httpClient.DefaultRequestHeaders.Add("sec-ch-ua-mobile", "?0");
httpClient.DefaultRequestHeaders.Add("sec-ch-ua-platform", "\"Windows\"");
httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue { NoCache = true };
httpClient.DefaultRequestHeaders.ConnectionClose = false; // 相当于Connection: keep-alive,但HttpClient默认就是keep-alive,所以通常不需要设置
// 注意:Cookie头通常不应该在HttpClient中手动设置,除非您正在处理特定的会话或认证需求。
// 注意:这里没有设置 Referer 头,因为 referrerPolicy 为 no-referrer
// 如果需要设置空的 Referer 头,可以取消以下行的注释
// httpClient.DefaultRequestHeaders.Referer = new Uri(""); // 但这通常不是有效的做法,因为 Uri 不能为空
// 发送 GET 请求
HttpResponseMessage response = await httpClient.GetAsync("Your rul.");
response.EnsureSuccessStatusCode();
// 读取响应内容
string content = await response.Content.ReadAsStringAsync();
return content;
}
catch (HttpRequestException e)
{
// 处理请求异常
Console.WriteLine($"Request error: {e.Message}");
throw;
}
}
}
// 使用示例
public class Program
{
public static async Task Main(string[] args)
{
string bearerToken = "eyJhbGciOiJIUzUxMiJ9..."; // 您的 Bearer Token
ApiClient apiClient = new ApiClient(bearerToken);
string response = await apiClient.GetDeptAndHospitalInfoAsync();
Console.WriteLine(response);
}
}
请注意以下几点:
- 使用了 httpClient.DefaultRequestHeaders 来设置全局的请求头。这些头将应用于通过此 HttpClient 实例发送的所有请求。如果您希望为单个请求设置不同的头,您可以在创建 HttpRequestMessage 时设置它们,然后使用 SendAsync 方法发送请求。但是,由于您正在发送一个简单的 GET 请求,并且所有头都是相同的,因此使用 DefaultRequestHeaders 是更简洁的方法。
- 取消了设置 Referer 头的代码行,因为您指定了 referrerPolicy 为 no-referrer,这意味着浏览器不会发送 Referer 头。在 HttpClient 中,如果您确实需要发送一个空的 Referer 头(尽管这通常不是有效的做法,因为 Referer 头通常包含一个有效的 URL),您可以尝试创建一个空的 Uri 对象,但这通常会导致异常。因此,我建议不要设置 Referer 头,除非服务器明确要求它。
- 使用了 GetAsync 方法来发送 GET 请求,并读取了响应内容。
- 请确保将 bearerToken 替换为您的实际令牌。在上面的代码中,我省略了令牌的一部分以保持简洁性。
2.2 获取Token令牌
在Web平台登录后,获取Authorization头的Bearer令牌的过程通常涉及与服务器进行身份验证交互。以下是获取Bearer令牌的一般步骤:
用户登录
用户在Web平台的登录页面输入用户名和密码,并提交登录请求。
后端服务器接收到登录请求后,验证用户名和密码的正确性。
令牌生成与返回
验证成功:如果用户名和密码验证成功,服务器会生成一个Bearer令牌。这个令牌是一个加密的字符串,用于在后续请求中验证用户的身份和权限。
令牌返回:服务器将生成的Bearer令牌添加到HTTP响应的头部,通常以Authorization: Bearer 的形式返回给客户端。有时,令牌也可能作为响应体的一部分返回。
客户端存储令牌
客户端接收到Bearer令牌后,需要将其存储起来,以便在后续的请求中使用。
令牌可以存储在浏览器的Cookie、LocalStorage、SessionStorage等位置,具体取决于应用的需求和安全策略。
使用Bearer令牌进行后续请求
在后续的请求中,客户端需要在HTTP请求的头部添加Authorization: Bearer ,以证明用户的身份和权限。
服务器接收到请求后,会验证请求头中的Bearer令牌是否有效。如果令牌有效,服务器会允许该请求继续执行;否则,服务器会拒绝该请求并返回相应的错误信息。
注意事项
安全性:Bearer令牌包含了用户的身份验证信息,因此必须妥善保管,避免泄露给未经授权的第三方。
有效期:Bearer令牌通常有一个有效期。在有效期内,令牌可以用于访问受保护资源。过期后,客户端需要重新进行身份验证以获取新的令牌。
令牌刷新:对于某些应用,可能提供了令牌刷新机制。当令牌即将过期时,客户端可以使用刷新令牌(Refresh Token)向服务器请求新的Bearer令牌,而无需用户重新输入用户名和密码。
示例代码
在WPF(Windows Presentation Foundation)应用程序中,我们通常不会直接处理底层的HTTP请求来获取Bearer令牌,而是会利用一些高级库或框架来简化这一过程,例如HttpClient类(.NET Framework 4.5及以上版本提供)或RestSharp等第三方库。
以下是一个使用HttpClient在WPF应用程序中发送POST请求以获取Bearer令牌的示例代码:
csharp
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace WpfAuthExample
{
public partial class MainWindow : Window
{
private static readonly HttpClient client = new HttpClient();
public MainWindow()
{
InitializeComponent();
// 假设有一个按钮点击事件来获取令牌
LoginButton.Click += async (s, e) => await GetBearerTokenAsync();
}
private async Task GetBearerTokenAsync()
{
// 从UI获取用户名和密码(这里假设有TextBox控件命名为UsernameTextBox和PasswordTextBox)
string username = UsernameTextBox.Text;
string password = PasswordTextBox.Text;
// 构造登录请求的URL(替换为实际的登录URL)
string loginUrl = "https://your-auth-server.com/login";
// 构造JSON格式的登录请求体
var json = new StringContent(
$"{{\"username\": \"{username}\", \"password\": \"{password}\"}}",
Encoding.UTF8,
"application/json");
// 发送POST请求并等待响应
HttpResponseMessage response = await client.PostAsync(loginUrl, json);
// 确保响应成功
response.EnsureSuccessStatusCode();
// 读取响应内容(这里假设响应是一个JSON对象,其中包含一个名为"token"的字段)
string responseBody = await response.Content.ReadAsStringAsync();
// 实际上,你需要使用JSON解析库(如Json.NET或System.Text.Json)来解析这个JSON字符串
// 这里为了简化,我们假设已经通过某种方式获取到了token字符串
// string token = ParseTokenFromJson(responseBody); // 你需要实现这个方法
// 在这个示例中,我们直接模拟了一个token字符串
string token = "your-mocked-bearer-token";
// 在这里,你可以将token存储起来,例如在App.xaml.cs中的静态属性中,
// 或者在用户的设置中,或者在某个全局变量中,取决于你的应用架构
// App.Current.Properties["BearerToken"] = token; // 示例:存储在Application级别的属性中
// 显示token(或执行其他操作)
MessageBox.Show($"Bearer Token: {token}");
}
// 注意:你需要实现一个方法来从JSON响应中解析出token,这里为了示例直接返回了一个模拟的token
// private string ParseTokenFromJson(string jsonResponse)
// {
// // 使用Json.NET或System.Text.Json来解析JSON并提取token字段
// // ...
// return "parsed-token";
// }
}
}
注意事项:
- 安全性:在实际应用中,不要将用户名和密码硬编码在代码中,也不要以明文形式存储或传输它们。此外,确保你的登录请求通过HTTPS发送,以保护凭证的安全。
- 错误处理:上面的代码示例没有包含详细的错误处理逻辑。在实际应用中,你应该处理可能发生的异常,例如网络错误、身份验证失败等,并向用户显示适当的错误消息。
- JSON解析:上面的代码示例假设你已经从响应中解析出了token,但实际上你需要使用JSON解析库(如Json.NET或System.Text.Json)来完成这一任务。
- 令牌存储:在实际应用中,你可能需要将Bearer令牌存储在一个更安全的地方,例如使用加密的存储或安全令牌服务(STS)。
- UI更新:上面的代码示例使用了MessageBox.Show来显示Bearer令牌,但在实际应用中,你可能需要更新UI的其他部分来反映登录状态或显示token信息。这通常涉及到数据绑定和MVVM(Model-View-ViewModel)模式等WPF核心概念。