MVC调用API无数据返回问题排查(Postman可正常获取数据)
问题排查与解决方案
API通过Postman请求正常返回数据,但MVC调用后前端无数据显示且无错误,可从以下几点逐一排查:
1. 空搜索参数的路由匹配问题
当搜索框为空时,MVC控制器的search参数为null,拼接后的API URL会变成https://localhost:7198/api/Expense/ListExpenseByAccountingSearch/,而API路由{search}要求必须传入参数,导致API返回404,但MVC控制器会返回空列表,前端因此拿到空数据。
解决方法:
- 方案一:修改API路由允许可选参数:
[HttpGet("ListExpenseByAccountingSearch/{search?}")] public async Task<IActionResult> ListExpenseByAccountingSearch(string search = "") { var values = await _expenseService.itemListExpenseByAccountingSearch(search ?? ""); return Ok(values); } - 方案二:在MVC控制器中处理空参数:
var searchParam = string.IsNullOrEmpty(search) ? "" : search; var responseMessage = await client.GetAsync($"https://localhost:7198/api/Expense/ListExpenseByAccountingSearch/{searchParam}");
2. 特殊字符的URL编码问题
搜索文本包含空格、&、=等特殊字符时,直接拼接URL会导致参数传递失真,API接收到的参数与预期不符,返回空结果。
解决方法:
- 前端Ajax编码搜索参数:
url: "/Management/Accounting/ListExpenseByAccountingSearch/" + encodeURIComponent(searchText), - 或在MVC控制器中编码参数:
var encodedSearch = Uri.EscapeDataString(search ?? ""); var responseMessage = await client.GetAsync($"https://localhost:7198/api/Expense/ListExpenseByAccountingSearch/{encodedSearch}");
3. JSON反序列化不匹配问题
MVC控制器中反序列化DTO时,若ResultExpenseByAccountingDto的属性名与API返回的JSON字段名不匹配(如大小写、驼峰/下划线命名差异),会导致反序列化失败,得到空列表。
解决方法:
- 检查DTO属性与API返回的JSON字段是否一致,可添加
JsonProperty特性映射:public class ResultExpenseByAccountingDto { [JsonProperty("periodId")] // 对应API返回的字段名 public int periodID { get; set; } [JsonProperty("createUser")] public string createUser { get; set; } // 其他属性同理 } - 临时在MVC控制器中打印API返回的原始JSON,确认结构:
var jsonData = await responseMessage.Content.ReadAsStringAsync(); Console.WriteLine(jsonData); // 查看控制台输出的JSON结构 var values = JsonConvert.DeserializeObject<List<ResultExpenseByAccountingDto>>(jsonData);
4. 表格渲染的DOM选择器错误
Ajax代码中清空了#tableExpense tbody,但渲染时却把行添加到#tableExpense根节点,导致DOM结构错误,浏览器无法正常渲染行。
解决方法:
修改表格行追加的目标选择器:
$("#tableExpense tbody").append(tablerow);
5. HTTPS证书信任问题(开发环境)
若MVC项目是HTTP协议,调用HTTPS的API可能因证书未信任导致请求失败,但MVC控制器会返回空列表,前端无错误提示。
解决方法:
在MVC中配置HttpClient忽略证书验证(仅开发环境使用):
var handler = new HttpClientHandler(); handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true; var client = new HttpClient(handler);
相关代码参考
API控制器
[HttpGet("ListExpenseByAccountingSearch/{search}")] public async Task<IActionResult> ListExpenseByAccountingSearch(string search) { var values = await _expenseService.itemListExpenseByAccountingSearch(search); return Ok(values); }
MVC控制器
[HttpGet] public async Task<IActionResult> ListExpenseByAccountingSearch(string search) { var client = _httpClientFactory.CreateClient(); var searchParam = string.IsNullOrEmpty(search) ? "" : search; var encodedSearch = Uri.EscapeDataString(searchParam); var responseMessage = await client.GetAsync($"https://localhost:7198/api/Expense/ListExpenseByAccountingSearch/{encodedSearch}"); if (responseMessage.IsSuccessStatusCode) { var jsonData = await responseMessage.Content.ReadAsStringAsync(); var values = JsonConvert.DeserializeObject<List<ResultExpenseByAccountingDto>>(jsonData); return Json(values); } return Json(new List<ResultExpenseByAccountingDto>()); }
搜索输入框
<input class="form-control form-control-sm" id="txtSearch" type="text" oninput="listExpenseBySearch()" placeholder="Search...">
Ajax代码
function listExpenseBySearch() { var searchText = $("#txtSearch").val(); $.ajax({ type: "GET", url: "/Management/Accounting/ListExpenseByAccountingSearch/" + encodeURIComponent(searchText), success: function (value) { console.log(value); $("#tableExpense tbody").empty(); var tablerow; $.each(value, (index, item) => { tablerrow = $("<tr/>"); tablerow.append(`<td><a class="fw-semibold text-primary">#${item.periodID}</a></td>`) tablerow.append(`<td>${item.createUser}</td>`) tablerow.append(`<td>${item.periodMonth}</td>`) tablerow.append(`<td>${item.periodYear}</td>`) switch (item.paymentStatus) { case 9999: tablerow.append(`<td><span class="badge bg-warning-transparent">Bekleniyor</span></td>`) break; case 9998: tablerow.append(`<td><span class="badge bg-warning-transparent">Onay Sürecinde - Yönetici</span></td>`) break; case 9997: tablerow.append(`<td><span class="badge bg-warning-transparent">Onay Sürecinde - Muhasebe</span></td>`) break; case 1: tablerow.append(`<td><span class="badge bg-warning-transparent">Ödeme Bekleniyor</span></td>`) break; case 2: tablerow.append(`<td><span class="badge bg-success-transparent">Ödeme Yapıldı</span></td>`) break; case 3: tablerow.append(`<td><span class="badge bg-dark-transparent">Gecikmiş Ödeme</span></td>`) break; case 4: tablerow.append(`<td><span class="badge bg-danger-transparent">İptal Edilen Ödeme</span></td>`) break; } let formattedAmount = parseFloat(item.totalAmount).toLocaleString('tr-TR', { minimumFractionDigits: 2 }); tablerow.append(`<td>₺${formattedAmount}</td>`) tablerow.append(`<td><a href="#" class="btn btn-primary-light btn-icon btn-sm"><i class="ri-eye-line"></i></a> <a href="/Management/Accounting/ExpenseSummary/${item.expenseID}" class="btn btn-primary-light btn-icon btn-sm"><i class="ri-send-plane-2-line"></i></a></td>`) $("#tableExpense tbody").append(tablerow); }) }, error: function (xhr, status, error) { console.log(xhr, status, error); } }); }
内容的提问来源于stack exchange,提问作者Gökmen Ada
相关产品推荐
相关产品推荐

