如何在ASP.NET Core Razor应用中调用ASP.NET API并展示页面数据
核心问题说明
你当前的实现存在3个直接导致报错、功能异常的问题:
HttpClient使用方式错误:每次调用都新建实例极易触发套接字耗尽,且你使用的52189是IIS Express随机分配的临时端口,项目重启后端口大概率变化,同时开发环境自签名HTTPS证书未做适配也会触发连接拒绝,最终抛出Cannot assign requested address异常。- Razor Pages生命周期逻辑错误:在PageModel构造函数中调用
async void类型的异步方法,构造函数不会等待异步任务执行完成,页面开始渲染时数据往往还未拉取完成,同时async void会导致异步异常无法被框架捕获,直接触发进程级错误。 - 职责划分错误:不应该在MVC控制器中编写静态方法供Razor Pages调用,控制器的职责是处理入站HTTP请求,跨服务调用逻辑应该独立抽离,硬编码API地址的方式也会大幅提升后续维护成本。
落地实现步骤
1. 固定API端口、注册HttpClient实例
首先进入API项目的Properties/launchSettings.json文件,将applicationUrl配置的端口固定为一个不常用的固定值(比如https://localhost:7001),避免每次启动端口随机变化。
然后在Razor Pages Web项目的Program.cs中,使用IHttpClientFactory注册专用的HTTP客户端,彻底解决手动new HttpClient导致的套接字耗尽问题:
// 注册调用Incident接口专用的HttpClient builder.Services.AddHttpClient("IncidentApiClient", client => { client.BaseAddress = new Uri("https://localhost:7001/"); // 仅开发环境使用,用于绕过自签名证书验证,生产环境必须删除此行 client.ServerCertificateCustomValidationCallback = (_, _, _, _) => true; });
2. 抽离独立的API调用服务
不要把API调用逻辑写在控制器中,在Web项目下新建Services文件夹,添加专门的事件服务类处理接口调用:
// Services/IncidentService.cs public class IncidentService { private readonly IHttpClientFactory _clientFactory; public IncidentService(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public async Task<List<IncidentModel>> GetAllIncidentsAsync() { var client = _clientFactory.CreateClient("IncidentApiClient"); var response = await client.GetAsync("api/incident"); // 先验证接口请求状态,避免接口报错时反序列化空内容抛出异常 response.EnsureSuccessStatusCode(); var resultStr = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject<List<IncidentModel>>(resultStr); } }
回到Program.cs将服务注册到依赖注入容器:
builder.Services.AddScoped<IncidentService>();
3. 修正PageModel的异步调用逻辑
删除构造函数中的业务调用,使用Razor Pages原生提供的OnGetAsync生命周期方法处理页面加载时的异步数据拉取,框架会自动等待该方法执行完成后再渲染页面:
// Pages/Index.cshtml.cs public class IndexModel : PageModel { private readonly ILogger<IndexModel> _logger; private readonly IncidentService _incidentService; // 使用明确的强类型代替dynamic,避免视图渲染时出现动态类型解析错误 public List<IncidentModel> IncidentList { get; set; } public IndexModel(ILogger<IndexModel> logger, IncidentService incidentService) { _logger = logger; _incidentService = incidentService; } public async Task OnGetAsync() { IncidentList = await _incidentService.GetAllIncidentsAsync(); } }
4. Razor页面渲染数据
在Index.cshtml中通过Model访问拉取到的列表数据,遍历渲染即可:
@page @model IndexModel @{ ViewData["Title"] = "事件列表"; } <h1>事件列表</h1> <table class="table table-bordered"> <thead> <tr> <th>事件ID</th> <th>事件标题</th> <th>创建时间</th> </tr> </thead> <tbody> @if (Model.IncidentList != null && Model.IncidentList.Any()) { @foreach (var item in Model.IncidentList) { <tr> <td>@item.Id</td> <td>@item.Title</td> <td>@item.CreateTime.ToString("yyyy-MM-dd HH:mm")</td> </tr> } } else { <tr> <td colspan="3" class="text-center">暂无数据</td> </tr> } </tbody> </table>
验证注意事项
- 调试时需要同时启动API项目和Razor Pages Web项目,确保API端口有服务正常监听,启动后先手动在浏览器访问API地址,确认能正常返回JSON数据再调试Web项目。
- 检查本机防火墙、代理软件规则,确保没有拦截localhost对应端口的请求。
- 除了事件处理场景,不要在ASP.NET Core中使用
async void编写异步方法,所有业务异步方法都要返回Task或Task<T>类型,避免异常逃逸、执行顺序混乱的问题。
内容的提问来源于stack exchange,提问作者Robert Rachita
相关产品推荐
相关产品推荐

