如何修复 MVC 客户端调用 API 时数据为空的问题

夜磊大大_7099

夜磊大大_7099

2026-10-01

660人浏览

原创

如何修复 MVC 客户端调用 API 时数据为空的问题

本文详解因 url 参数传递方式错误(路径参数误用为查询参数)导致 api 返回空数据的问题,重点说明 asp.net core 中 restful 路由与 httpclient/ajax 调用的匹配原则,并提供完整修正方案。

本文详解因 url 参数传递方式错误(路径参数误用为查询参数)导致 api 返回空数据的问题,重点说明 asp.net core 中 restful 路由与 httpclient/ajax 调用的匹配原则,并提供完整修正方案。

在 ASP.NET Core 项目中,API 控制器定义了带路径参数(Route Parameter) 的 GET 端点:

[HttpGet("ListExpenseByAccountingSearch/{search}")]
public async Task<iactionresult> ListExpenseByAccountingSearch(string search)
{
    var values = await _expenseService.itemListExpenseByAccountingSearch(search);
    return Ok(values);
}</iactionresult>

该路由模板明确要求 search 作为 URL 路径段(如 /api/Expense/ListExpenseByAccountingSearch/office),而非查询字符串(?search=office)。然而,在 MVC 控制器和前端 AJAX 调用中,却错误地将 search 当作查询参数拼接,导致实际请求 URL 与路由不匹配:

❌ 错误写法(MVC Controller):

client.GetAsync($"https://localhost:7198/api/Expense/ListExpenseByAccountingSearch/{search}");
// 若 search 为空字符串(如用户刚输入但未填内容),URL 变为:
// .../ListExpenseByAccountingSearch/ → 匹配到 {search} = ""(空字符串)
// 但若前端未触发输入、或初始值为空,后端 service 可能返回空集合且无报错

❌ 更严重的是 AJAX 请求(根本未匹配路由):

url: "/Management/Accounting/ListExpenseByAccountingSearch/" + searchText
// 若 searchText = "",则 URL 变为:/Management/Accounting/ListExpenseByAccountingSearch/
// 此路径对应 MVC 控制器的 [HttpGet] 方法,但该方法期望接收 query string ?search=xxx
// 而你实际未加 ?search=,导致 MVC action 的 search 参数为 null 或空字符串

✅ 正确做法是严格对齐路由约定:

  1. API 端保持路径参数设计 → 客户端必须以路径形式传参;
  2. MVC Controller 调用时需 URL 编码防止特殊字符破坏路由;
  3. AJAX 请求也必须使用路径参数格式,并处理空值逻辑。

✅ 修正后的完整代码

MVC Controller(推荐使用 Uri.EscapeDataString 防止特殊字符):

[HttpGet]
public async Task<iactionresult> ListExpenseByAccountingSearch(string search)
{
    // 处理空搜索:可返回全部,或跳过请求(按业务定)
    if (string.IsNullOrWhiteSpace(search))
    {
        return Json(new List<resultexpensebyaccountingdto>());
    }

    var client = _httpClientFactory.CreateClient();
    // ✅ 正确:将 search 作为路径段,且编码
    var encodedSearch = Uri.EscapeDataString(search.Trim());
    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>>(jsonData);
        return Json(values);
    }

    // 记录日志便于排查
    var statusCode = responseMessage.StatusCode;
    Console.WriteLine($"API call failed with status: {statusCode}");
    return Json(new List<resultexpensebyaccountingdto>());
}</resultexpensebyaccountingdto></list></resultexpensebyaccountingdto></iactionresult>

前端 AJAX(同步修正 URL 格式 + 空值校验):

function listExpenseBySearch() {
    var searchText = $("#txtSearch").val().trim();

    // ✅ 空搜索时清空表格,不发起请求(避免 /.../ 无效路径)
    if (!searchText) {
        $("#tableExpense tbody").empty();
        return;
    }

    // ✅ 正确:search 作为路径参数(注意:无需 ?search=)
    $.ajax({
        type: "GET",
        url: "/Management/Accounting/ListExpenseByAccountingSearch/" + encodeURIComponent(searchText),
        success: function (value) {
            console.log("API Response:", value);
            $("#tableExpense tbody").empty();
            var tablerow;
            $.each(value, function (index, item) {
                tablerow = $("<tr></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>`);

                // paymentStatus 渲染逻辑(保持不变)
                let badgeHtml = "";
                switch (item.paymentStatus) {
                    case 9999:
                        badgeHtml = '<span class="badge bg-warning-transparent">Bekleniyor</span>';
                        break;
                    case 9998:
                        badgeHtml = '<span class="badge bg-warning-transparent">Onay Sürecinde - Yönetici</span>';
                        break;
                    case 9997:
                        badgeHtml = '<span class="badge bg-warning-transparent">Onay Sürecinde - Muhasebe</span>';
                        break;
                    case 1:
                        badgeHtml = '<span class="badge bg-warning-transparent">Ödeme Bekleniyor</span>';
                        break;
                    case 2:
                        badgeHtml = '<span class="badge bg-success-transparent">Ödeme Yapıldı</span>';
                        break;
                    case 3:
                        badgeHtml = '<span class="badge bg-dark-transparent">Gecikmiş Ödeme</span>';
                        break;
                    case 4:
                        badgeHtml = '<span class="badge bg-danger-transparent">İptal Edilen Ödeme</span>';
                        break;
                    default:
                        badgeHtml = '<span class="badge bg-secondary-transparent">Bilinmiyor</span>';
                }
                tablerow.append(`<td>${badgeHtml}</td>`);

                let formattedAmount = parseFloat(item.totalAmount || 0).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/%24%7Bitem.expenseID%7D" class="btn btn-primary-light btn-icon btn-sm"><i class="ri-send-plane-2-line"></i></a>
</td>`);
                $("#tableExpense tbody").append(tablerow); // ✅ 推荐 append 到 tbody 而非 table
            });
        },
        error: function (xhr, status, error) {
            console.error("AJAX Error:", xhr.status, status, error);
            alert("Arama sırasında bir hata oluştu. Lütfen konsolu kontrol edin.");
        }
    });
}

⚠️ 关键注意事项

  • 路由必须完全匹配:{search} 是路径参数,不可用 ?search= 替代;否则 MVC 路由引擎无法绑定,参数默认为 null 或空字符串。
  • 空值防御:前端和后端均需校验 search 是否为空/空白,避免发送 .../ListExpenseByAccountingSearch/ 这类不合法路径。
  • URL 编码必不可少:用户输入可能含空格、/、& 等字符,务必使用 encodeURIComponent()(JS)或 Uri.EscapeDataString()(C#)编码。
  • 调试技巧:在浏览器开发者工具 Network 标签页中查看实际发出的请求 URL,确认是否与 API 文档一致;同时检查响应状态码(如 404 表示路由未匹配,400 表示参数解析失败)。
  • 一致性建议:若搜索场景常需支持空值、多条件或分页,更推荐统一改用查询参数([HttpGet("ListExpenseByAccountingSearch")] + [FromQuery] string search),提升灵活性与健壮性。

通过以上修正,即可确保 MVC 前端准确调用 API 并渲染搜索结果,彻底解决“Postman 有数据、页面无响应”的典型路由失配问题。

大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2023.08.07

1995

5

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.23

2802

1

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

2023.10.13

976

3

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

2025.09.10

3159

7

ajax教程
ajax教程

php中文网为大家带来ajax教程合集,Ajax是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。php中文网还为大家带来ajax的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.14

2480

4

ajax中文乱码解决方法
ajax中文乱码解决方法

ajax中文乱码解决方法有设置请求头部的字符编码、在服务器端设置响应头部的字符编码和使用encodeURIComponent对中文进行编码。本专题为大家提供ajax中文乱码相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.31

340

5

ajax传递中文乱码怎么办
ajax传递中文乱码怎么办

ajax传递中文乱码的解决办法:1、设置统一的编码方式;2、服务器端编码;3、客户端解码;4、设置HTTP响应头;5、使用JSON格式。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.11.15

2313

5

ajax网站有哪些
ajax网站有哪些

使用ajax的网站有谷歌、维基百科、脸书、纽约时报、亚马逊、stackoverflow、twitter、hacker news、shopify和basecamp等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.09.24

1141

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习