- 获取网页源码:使用
HttpClient类向目标网址发送 HTTP 请求,并获取其响应内容(HTML 源码)。 - 提供下载:将获取到的源码作为文件,通过 HTTP 响应流的方式发送给用户的浏览器,触发下载。
下面我将为你提供详细的步骤、代码示例和最佳实践。

(图片来源网络,侵删)
核心步骤
- 创建 ASP.NET 项目:你可以使用 Visual Studio 创建一个 ASP.NET Web Forms、MVC 或 Razor Pages 项目,本示例将以最简洁的 Razor Pages 为例。
- 编写获取源码的逻辑:创建一个方法,使用
HttpClient获取指定 URL 的 HTML。 - 创建下载接口:创建一个 Action 或 Page Handler,调用获取源码的方法,并设置正确的 HTTP 响应头,将内容作为附件返回。
- 前端调用:在前端页面上添加一个按钮或链接,触发后端的下载接口。
详细代码示例 (ASP.NET Core Razor Pages)
这个例子将展示如何创建一个页面,用户可以在输入框中输入一个网址,然后点击按钮下载该网页的源码。
项目结构
假设你创建了一个名为 WebScraper 的 ASP.NET Core Razor Pages 项目。
创建页面模型 (Pages/Index.cshtml.cs)
这个文件将包含后端逻辑。
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Text;
namespace WebScraper.Pages
{
public class IndexModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
// 通过依赖注入获取 IHttpClientFactory,这是最佳实践
public IndexModel(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[BindProperty(SupportsGet = true)]
public string Url { get; set; }
public async Task<IActionResult> OnPostDownloadAsync()
{
// 1. 参数校验
if (string.IsNullOrWhiteSpace(Url))
{
ModelState.AddModelError(string.Empty, "请输入一个有效的网址。");
return Page();
}
// 确保 URL 有协议头,否则 HttpClient 可能会失败
if (!Uri.TryCreate(Url, UriKind.Absolute, out var uriResult)
|| !(uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps))
{
ModelState.AddModelError(string.Empty, "网址格式不正确。");
return Page();
}
try
{
// 2. 使用 HttpClient 获取网页内容
var client = _httpClientFactory.CreateClient();
// 设置一个合理的超时时间
client.Timeout = TimeSpan.FromSeconds(30);
// 发送 GET 请求
var response = await client.GetAsync(Url);
// 确保请求成功 (状态码 2xx)
response.EnsureSuccessStatusCode();
// 读取响应内容 (HTML 源码)
var htmlContent = await response.Content.ReadAsStringAsync();
// 3. 准备下载响应
// 将字符串转换为字节数组
var byteArray = Encoding.UTF8.GetBytes(htmlContent);
// 创建 FileContentResult
// "text/plain" 是 MIME 类型,表示纯文本文件
// "source_code.txt" 是下载时默认显示的文件名
var fileContentResult = new FileContentResult(byteArray, "text/plain")
{
FileDownloadName = "source_code.txt"
};
return fileContentResult;
}
catch (HttpRequestException ex)
{
// 处理网络错误或 HTTP 错误
ModelState.AddModelError(string.Empty, $"无法获取网页内容: {ex.Message}");
return Page();
}
catch (TaskCanceledException ex)
{
// 处理超时错误
ModelState.AddModelError(string.Empty, $"请求超时: {ex.Message}");
return Page();
}
catch (Exception ex)
{
// 处理其他未知错误
ModelState.AddModelError(string.Empty, $"发生未知错误: {ex.Message}");
return Page();
}
}
}
}
创建 Razor 页面 (Pages/Index.cshtml)
这是用户交互的前端界面。

(图片来源网络,侵删)
@page
@model WebScraper.Pages.IndexModel
@{
ViewData["Title"] = "网页源码下载器";
}
<div class="text-center">
<h1 class="display-4">网页源码下载器</h1>
<p>输入一个网址,下载其 HTML 源码。</p>
<form method="post">
<div class="form-group">
<label for="url">目标网址:</label>
<input type="url" class="form-control" id="url" name="url" asp-for="Url" placeholder=" https://www.example.com" style="width: 400px; display: inline-block;">
</div>
<br />
<button type="submit" class="btn btn-primary" asp-page-handler="Download">下载源码</button>
</form>
@if (!ModelState.IsValid)
{
<div class="alert alert-danger mt-3">
@foreach (var modelState in ViewData.ModelState.Values)
{
foreach (var error in modelState.Errors)
{
<p>@error.ErrorMessage</p>
}
}
</div>
}
</div>
关键点解释
IHttpClientFactory (最佳实践)
在示例中,我们没有直接使用 new HttpClient(),而是通过依赖注入的方式获取 IHttpClientFactory 的实例来创建 HttpClient。
- 为什么?
- 连接池管理:
HttpClient实现了IDisposable,但频繁地new和dispose会导致 Socket 耗尽问题。HttpClientFactory内部管理一个HttpClient池,可以复用连接,提高性能和稳定性。 - 配置集中:你可以在
Program.cs或Startup.cs中统一配置HttpClient的设置,比如默认请求头、超时时间等。 - 生命周期管理:它可以与 ASP.NET Core 的生命周期集成,自动处理
HttpClient的创建和释放。
- 连接池管理:
设置正确的 HTTP 响应头
为了让浏览器知道这是一个需要下载的文件而不是一个需要显示的页面,我们需要在响应中设置特定的头信息。
Content-Type: text/plain:告诉浏览器这个内容是纯文本,对于 HTML 源码,这个类型是合适的。Content-Disposition: attachment; filename="...":这是最关键的头。attachment:指示浏览器应该将内容作为附件下载,而不是在浏览器窗口内显示。filename="...":指定下载时默认显示的文件名。
在我们的代码中,FileContentResult 帮助我们自动处理了这些头信息的设置。
错误处理
网络请求是不可靠的,必须进行充分的错误处理。

(图片来源网络,侵删)
- URL 校验:使用
Uri.TryCreate确保用户输入的是有效的网址。 - HTTP 错误:
response.EnsureSuccessStatusCode()会在响应状态码不是 2xx 时抛出HttpRequestException。 - 网络/超时:
HttpClient的操作是异步的,可能会因为网络问题或服务器无响应而超时,需要捕获TaskCanceledException。 - 异常捕获:使用
try-catch块捕获所有可能的异常,并向用户显示友好的错误信息,而不是让程序崩溃。
其他技术栈(ASP.NET Web Forms / MVC)
如果你使用的是 ASP.NET Web Forms 或 ASP.NET MVC 4/5,核心逻辑是相似的,只是语法和项目结构略有不同。
ASP.NET MVC 示例 (Controller)
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
public class HomeController : Controller
{
// 在 ASP.NET MVC 中,你可以直接使用 new HttpClient,但推荐使用类似 Unity/Autofac 的容器来管理它
private readonly HttpClient _httpClient = new HttpClient();
public ActionResult Index()
{
return View();
}
[HttpPost]
public async Task<ActionResult> DownloadSource(string url)
{
if (string.IsNullOrWhiteSpace(url))
{
ModelState.AddModelError("", "请输入一个有效的网址。");
return View("Index");
}
try
{
_httpClient.Timeout = TimeSpan.FromSeconds(30);
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var htmlContent = await response.Content.ReadAsStringAsync();
var byteArray = Encoding.UTF8.GetBytes(htmlContent);
return File(byteArray, "text/plain", "source_code.txt");
}
catch (Exception ex)
{
// 记录日志
// 返回错误页面或信息
return Content($"下载失败: {ex.Message}");
}
}
}
通过以上步骤,你就可以在 ASP.NET 应用中实现一个功能完善的“获取网页源码并下载”的功能,记住以下几点最佳实践:
- 优先使用
HttpClientFactory(在 ASP.NET Core 中)。 - 始终进行输入验证和错误处理。
- 设置正确的
Content-Type和Content-Disposition响应头来触发下载。 - 为长时间运行的网络请求设置合理的超时时间。
