讲师中心 微信公众号
AI工具推荐 视频效率加速

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

夜磊大大_7099

夜磊大大_7099

发布时间:2026-10-01 15:00:35

|

660人浏览过

|

来源于php中文网

原创

如何修复 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);
}

该路由模板明确要求 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<ResultExpenseByAccountingDto>>(jsonData);
        return Json(values);
    }

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

前端 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/>");
                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/${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); // ✅ 推荐 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 有数据、页面无响应”的典型路由失配问题。

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

热门AI工具

更多
Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

相关专题

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

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

1995

2023.08.07

json是什么
json是什么

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

2802

2023.08.23

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

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

956

2023.10.13

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

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

3159

2025.09.10

ajax教程
ajax教程

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

2460

2023.06.14

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

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

340

2023.08.31

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

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

2293

2023.11.15

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

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

1141

2024.09.24

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

0

2026.09.30

热门下载

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

精品课程

更多
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn