【ASP.NET Core】Controller 层 Action 返回值精讲:JsonResult(AJAX 交互核心)

目录

    • [一、先搞懂:JsonResult 到底是什么?(生活类比)](#一、先搞懂:JsonResult 到底是什么?(生活类比))
    • [二、JsonResult 核心用法(代码示例 + 场景)](#二、JsonResult 核心用法(代码示例 + 场景))
      • [2.1 基础用法:返回简单 JSON 数据](#2.1 基础用法:返回简单 JSON 数据)
      • [2.2 自定义 JSON 序列化配置](#2.2 自定义 JSON 序列化配置)
      • [2.3 结合状态码返回](#2.3 结合状态码返回)
    • [三、JsonResult 使用流程图](#三、JsonResult 使用流程图)
    • [四、常踩的坑(附解决方案 + 生活类比)](#四、常踩的坑(附解决方案 + 生活类比))
      • [4.1 坑 1:循环引用导致序列化报错](#4.1 坑 1:循环引用导致序列化报错)
      • [4.2 坑 2:数据类型序列化异常(如 DateTime / 枚举)](#4.2 坑 2:数据类型序列化异常(如 DateTime / 枚举))
      • [4.3 坑 3:忘记配置 [FromBody] 导致参数接收不到](#4.3 坑 3:忘记配置 [FromBody] 导致参数接收不到)
      • [4.4 坑 4:返回超大 JSON 导致性能问题](#4.4 坑 4:返回超大 JSON 导致性能问题)
      • [4.5 坑 5:跨域导致前端无法接收 JSON](#4.5 坑 5:跨域导致前端无法接收 JSON)
    • 五、总结
    • 六、互动环节

在ASP.NET Core 开发中,Controller 层是处理前端请求的 "中转站",而 Action 方法的返回值则是 "中转站" 给前端的 "快递包裹"------JsonResult 作为 AJAX 交互的核心返回类型,几乎是前后端分离项目的标配,但新手很容易在使用时踩坑,比如返回格式不对、循环引用报错、数据序列化异常等。今天就带大家彻底搞懂 JsonResult,从代码示例到避坑指南,用最通俗的方式讲明白!

一、先搞懂:JsonResult 到底是什么?(生活类比)

先举个生活例子:你(前端)去奶茶店(Controller)点一杯珍珠奶茶(请求数据),店员(Action 方法)不会直接把奶茶原料给你,而是按标准杯型、甜度、冰度(JSON 格式)打包好递给你 ------JsonResult 就是这个 "标准化打包的奶茶",它把后端数据按 JSON 格式序列化,返回给前端 AJAX 请求,保证前后端能 "看懂" 彼此的数据。
核心定义: JsonResult 是ASP.NET Core 中专门用于返回 JSON 格式数据的 Action 返回类型,继承自 ActionResult,内置 JSON 序列化逻辑,是 AJAX/axios/fetch 等前端请求的 "最佳搭档"。

二、JsonResult 核心用法(代码示例 + 场景)

2.1 基础用法:返回简单 JSON 数据

场景:前端 AJAX 请求获取用户信息

csharp 复制代码
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace JsonResultDemo.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class UserController : ControllerBase
    {
        // 基础版:返回单个对象
        [HttpGet("getuser/{id}")]
        public JsonResult GetUser(int id)
        {
            // 模拟从数据库查询用户数据
            var user = new
            {
                Id = id,
                Name = "张三",
                Age = 28,
                Phone = "13800138000",
                IsVip = true
            };

            // 直接返回JsonResult,框架自动序列化
            return new JsonResult(user);
        }

        // 进阶版:返回集合数据
        [HttpGet("getusers")]
        public JsonResult GetUsers()
        {
            var userList = new List<object>
            {
                new { Id = 1, Name = "张三", Age = 28 },
                new { Id = 2, Name = "李四", Age = 30 },
                new { Id = 3, Name = "王五", Age = 25 }
            };

            // 也可以用ControllerBase的Json()快捷方法(推荐)
            return Json(new { code = 200, msg = "查询成功", data = userList });
        }
    }
}

小节: 基础用法核心是 "数据 + 序列化",Json()快捷方法比直接 new JsonResult 更简洁,且自动继承框架默认的 JSON 配置,新手优先用。

2.2 自定义 JSON 序列化配置

场景:前端需要返回驼峰命名、忽略空值、格式化日期

csharp 复制代码
[HttpGet("getusercustom/{id}")]
public JsonResult GetUserCustom(int id)
{
    var user = new
    {
        UserId = id,
        UserName = "张三",
        CreateTime = DateTime.Now,
        Remark = (string)null // 空值字段
    };

    // 自定义序列化选项
    var jsonOptions = new JsonSerializerOptions
    {
        // 驼峰命名(前端常用,如userName而非UserName)
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        // 忽略空值字段
        IgnoreNullValues = true,
        // 日期格式化
        WriteIndented = true, // 格式化JSON(便于调试)
        Converters = { new DateTimeConverter("yyyy-MM-dd HH:mm:ss") }
    };

    return new JsonResult(user, jsonOptions);
}

// 自定义日期转换器
public class DateTimeConverter : JsonConverter<DateTime>
{
    private readonly string _format;
    public DateTimeConverter(string format) => _format = format;

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTime.Parse(reader.GetString()!);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(_format));
    }
}

小节: 自定义配置解决了 "后端大驼峰、前端小驼峰""空值字段冗余""日期格式乱码" 等问题,是实际项目中必用的技巧。

2.3 结合状态码返回

场景:请求失败时返回错误码 + 提示信息

csharp 复制代码
[HttpPost("adduser")]
public JsonResult AddUser([FromBody] UserDto userDto)
{
    // 模拟参数校验
    if (string.IsNullOrEmpty(userDto.Name))
    {
        // 返回错误状态码+JSON
        Response.StatusCode = StatusCodes.Status400BadRequest;
        return Json(new { code = 400, msg = "用户名不能为空" });
    }

    // 模拟添加用户成功
    return Json(new { code = 200, msg = "添加成功", data = new { Id = 1001, Name = userDto.Name } });
}

// 定义DTO类
public class UserDto
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Phone { get; set; }
}

小节: 结合 HTTP 状态码返回,前端能更精准判断请求结果(如 400 参数错误、500 服务器错误),符合 RESTful 规范。

三、JsonResult 使用流程图

否 是 前端AJAX请求 Controller接收请求 Action方法处理业务逻辑 数据是否合法? 设置错误状态码,构造错误JSON 构造正常业务数据 配置JSON序列化选项 可选 返回JsonResult 框架序列化JSON并响应 前端接收并解析JSON

四、常踩的坑(附解决方案 + 生活类比)

4.1 坑 1:循环引用导致序列化报错

现象:

返回包含关联对象的数据(如 User 包含 Order,Order 又包含 User),运行时报错:A possible object cycle was detected。
生活类比:

你介绍自己的家庭关系,说 "我是我爸的儿子,我爸是我的爸爸",无限循环,别人根本听不懂。
解决方案:

csharp 复制代码
// 方案1:忽略循环引用(推荐)
var jsonOptions = new JsonSerializerOptions
{
    ReferenceHandler = ReferenceHandler.IgnoreCycles,
    WriteIndented = true
};
return new JsonResult(userWithOrder, jsonOptions);

// 方案2:使用DTO隔离关联对象(更规范)
// 定义只包含必要字段的DTO,避免关联对象
public class UserSimpleDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 只留需要的字段,不包含Order关联
}

小节: 循环引用是新手最常踩的坑,优先用 DTO 隔离,其次用忽略循环引用配置,避免返回冗余关联数据。

4.2 坑 2:数据类型序列化异常(如 DateTime / 枚举)

现象:

日期返回成2025-11-26T10:00:00(带 T),枚举返回数字而非文字,前端解析混乱。
生活类比:

你给快递员写地址时用 "沪 A12345",但快递系统只认 "上海市 + 车牌号",格式不匹配导致快递送错。
解决方案:

csharp 复制代码
// 1. 日期格式化(前面已讲,此处简化)
// 2. 枚举序列化为字符串
var jsonOptions = new JsonSerializerOptions
{
    Converters = { 
        new JsonStringEnumConverter(), // 枚举转字符串
        new DateTimeConverter("yyyy-MM-dd HH:mm:ss") 
    }
};

// 示例:枚举类型
public enum UserType
{
    Normal,
    Vip,
    Admin
}

// Action中返回枚举
[HttpGet("getusertype/{id}")]
public JsonResult GetUserType(int id)
{
    var user = new { Id = id, Type = UserType.Vip };
    return new JsonResult(user, jsonOptions); // 返回Type:"Vip"而非1
}

小节: 特殊类型(日期、枚举、小数)需自定义序列化规则,保证前后端数据格式一致。

4.3 坑 3:忘记配置 [FromBody] 导致参数接收不到

现象:

前端 POST 传递 JSON 数据,后端 Action 的 DTO 参数全为 null。
生活类比:

你给快递员递包裹,但没填收货地址([FromBody]),快递员不知道该寄去哪,自然拿不到包裹。
解决方案:

csharp 复制代码
// 错误写法:缺少[FromBody]
[HttpPost("adduser")]
public JsonResult AddUser(UserDto userDto) { ... }

// 正确写法:添加[FromBody](POST JSON必须加)
[HttpPost("adduser")]
public JsonResult AddUser([FromBody] UserDto userDto) { ... }

小节: POST 请求接收 JSON 参数时,必须加[FromBody]特性,GET 请求则用[FromQuery]/[FromRoute]。

4.4 坑 4:返回超大 JSON 导致性能问题

现象:

返回上万条数据的 JSON,前端加载慢,后端内存占用高。
生活类比:

你点奶茶时非要店员一次性给你 100 杯,店员忙不过来,你也拿不动,效率极低。
解决方案:

csharp 复制代码
// 方案1:分页返回
[HttpGet("getuserspage")]
public JsonResult GetUsersPage([FromQuery] int page = 1, [FromQuery] int size = 10)
{
    // 模拟分页查询
    var total = 1000; // 总条数
    var userList = new List<object>();
    // 分页逻辑:跳过(page-1)*size条,取size条
    for (int i = (page-1)*size + 1; i <= page*size; i++)
    {
        userList.Add(new { Id = i, Name = $"用户{i}", Age = 20 + i%10 });
    }

    return Json(new { 
        code = 200, 
        msg = "查询成功", 
        data = userList,
        total = total, // 总条数
        page = page,
        size = size
    });
}

// 方案2:启用JSON压缩(全局配置)
// Program.cs中添加
builder.Services.AddResponseCompression(options =>
{
    options.Providers.Add<GzipCompressionProvider>();
    options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json" });
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.Optimal;
});
// 启用中间件
app.UseResponseCompression();

小节: 超大 JSON 需分页 + 压缩,既减少前端加载时间,也降低后端服务器压力。

4.5 坑 5:跨域导致前端无法接收 JSON

现象:

前端控制台报错Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy。
生活类比:

你去另一个小区取快递,但小区保安(浏览器)不让进,因为你不是本小区的(跨域)。
解决方案:

csharp 复制代码
// Program.cs中配置跨域
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll", policy =>
    {
        policy.AllowAnyOrigin() // 允许所有源(生产环境需指定具体域名)
              .AllowAnyMethod() // 允许所有请求方法(GET/POST等)
              .AllowAnyHeader(); // 允许所有请求头
    });
});
// 启用跨域中间件(必须在UseRouting之后,UseEndpoints之前)
app.UseCors("AllowAll");

小节: 跨域是前端接收 JSON 的 "拦路虎",开发环境可允许所有源,生产环境需严格指定前端域名,避免安全风险。

五、总结

JsonResult 是ASP.NET Core 前后端 AJAX 交互的核心,掌握它的关键在于:

1.基础用法:用Json()快捷方法返回数据,优先搭配 DTO;

2.自定义配置:解决序列化格式、循环引用等问题;

3.避坑要点:注意 [FromBody]、跨域、分页、特殊类型序列化。

其实 JsonResult 的使用逻辑很简单 ------ 就像和前端 "对话",你得说对方能听懂的话(JSON 格式),别讲废话(冗余数据),别绕圈子(循环引用),别没头没尾(状态码 + 提示)。

六、互动环节

欢迎在评论区分享:

  • 你使用 JsonResult 时遇到过哪些奇葩问题?
  • 有没有自己的 "避坑小技巧"?
  • 想了解哪些更多关于 Action 返回值的内容(如 IActionResult、FileResult 等)?
    如果这篇文章对你有帮助,别忘了点赞 + 收藏 + 关注,后续会持续更新ASP.NET Core Controller 层的核心知识点!
相关推荐
啃火龙果的兔子1 天前
webview焦点与软键盘键盘交互详解
计算机外设·交互
吴法刚1 天前
Gemini cli 源码分析之-Gemini CLI 项目启动交互模式startInteractiveUI函数
ai·交互·ai编程·gemini·ai编码
宝桥南山2 天前
.NET 10 - Blazor web assembly应用的一些诊断方式
microsoft·微软·c#·asp.net·.net·.netcore
Jackson@ML2 天前
用ASP.NET创建一个Blazer Web应用程序
前端·asp.net·blazor
San303 天前
深入理解 JavaScript 异步编程:从 Ajax 到 Promise
javascript·ajax·promise
yesyesyoucan3 天前
文本与表格格式转换助手:轻松实现TXT/CSV互转及Excel转CSV的实用工具
科技·程序人生·excel·交互·媒体
百***49003 天前
开源模型应用落地-FastAPI-助力模型交互-进阶篇-中间件(四)
开源·交互·fastapi
想不明白的过度思考者3 天前
基于 Spring Boot 的 Web 三大核心交互案例精讲
前端·spring boot·后端·交互·javaee
心疼你的一切4 天前
Unity开发Rokid应用之离线语音指令交互模型
android·开发语言·unity·游戏引擎·交互·lucene