c# webclient 和 httpclient 的区别

9次阅读

WebClient 将非2xx状态码视为异常直接抛出WebException,而httpClient仅在网络错误时抛HttpRequestException,正常HTTP错误需手动检查StatusCode。

c# webclient 和 httpclient 的区别

WebClient 一调就报 WebException,HttpClient 却不抛异常?

这是最常踩的坑:用 WebClient 请求一个返回 404 或 500 的 API,代码直接崩在 DownloadString 上,抛出 WebException;而换成 HttpClientGetAsync 照常返回 HttpResponseMessage,得手动检查 response.IsSuccessStatusCode 才知道失败了。

  • WebClient 把所有非 2xx 状态码当“异常”处理(哪怕 404 是你预期的业务结果)
  • HttpClient 只在真正出问题时才抛 HttpRequestException(比如 dns 失败、连接超时、服务器完全不可达)
  • 这意味着:用 WebClient 你得 try-catch 每次调用;用 HttpClient 你可以统一判断 StatusCode,再分情况处理(如 401 跳登录、404 提示资源不存在)

为什么 new HttpClient() 用几次就卡死或报 SocketException?

这不是 bug,是误用。很多人照着示例写 using (var client = new HttpClient()),结果高并发下出现 SocketException: Only one usage of each socket address is normally permitted

  • HttpClient 设计为**长生命周期复用**,不是一次性的 —— 它背后维护 TCP 连接池
  • 每次 new HttpClient() 都新建连接池,频繁创建销毁会快速耗尽本地端口(TIME_WaiT 状态积)
  • WebClient 虽也有类似风险,但因默认同步+低并发场景多,问题不明显;而 HttpClient 异步高频使用时立刻暴露
  • 正确做法:全局单例、DI 注入 IHttpClientFactory,或至少在类级别复用实例

上传文件、下载大文件,该选哪个?

看需求粒度。如果只是“把本地文件发到某个 URL”,WebClient.UploadFile 一行搞定;但如果要监控进度、设超时、加 Token、支持断点续传、或上传流式数据(比如压缩中上传),HttpClient 是唯一选择。

  • WebClient:只支持完整文件路径上传,不支持 stream 或分块;无进度回调;不能设 Timeout(只能靠底层 WebRequest 默认值)
  • HttpClient:可传 StreamContent、设 client.Timeout = TimeSpan.FromSeconds(60)、用 Progress 监听上传进度、支持 CancelToken 中断
  • 注意:WebClientUploadFileAsync 声称异步,但底层仍是同步 I/O 封装,无法真正释放线程HttpClient 的异步才是真正的 awaitable I/O

新项目里还能用 WebClient 吗?

能,但不建议。.net 官方已将 WebClient 标记为 [Obsolete](https://learn.microsoft.com/en-us/do.net/api/system.net.webclient?view=net-8.0#remarks)(自 .NET 6 起警告,.NET 8+ 默认启用警告)。

  • 它不支持 HTTP/2、不支持请求拦截器、不能配置消息处理器链(比如自动加 Auth Header)、无法集成 Polly 重试
  • 所有现代库(如 Refit、Flurl)都基于 HttpClient 构建;第三方认证库(microsoft.Identity.Web)也只提供 HttpClient 扩展
  • 例外场景:写个临时控制台工具快速抓网页内容,new WebClient().DownloadString(url) 确实快——但这种“快”是以牺牲可维护性和未来扩展性换来的

真正容易被忽略的点是:异常语义差异不是风格偏好,而是设计契约。把 404 当异常捕获,等于把“资源不存在”当成程序错误;而把它作为正常响应处理,才能写出可预测、可观测、易测试的 HTTP 客户端逻辑。

text=ZqhQzanResources