位置:首页 > Java > SpringMVC 多种响应返回方式详解:页面跳转、JSON 返回与常见踩坑一次讲清

SpringMVC 多种响应返回方式详解:页面跳转、JSON 返回与常见踩坑一次讲清

时间:2026-08-23  |  作者:夜鞌不睡  |  阅读:0

目录

  1. SpringMVC 常见返回方式有哪些
  2. 前后端分离项目如何返回 JSON
  3. 几个最容易踩坑的地方
  4. 按场景快速选型:一张表看懂返回方式
  5. 实际项目里怎么选更合适

前言

SpringMVC 的返回值不只是“写法不同”,而是决定了请求最终走页面渲染还是直接输出数据。很多人遇到的 404、跳转失效、JSON 返回异常,本质上都和返回类型、注解位置以及视图解析链路有关。下面按服务端渲染与前后端分离两类场景逐段拆开,帮你快速判断每种写法的执行结果和适用边界。

SpringMVC 的返回值看起来只是方法签名上的一个类型差异,真正影响的却是整条响应链路:到底是走视图解析器、请求转发到页面,还是直接把对象写进响应体。很多 404、跳转失效、JSON 返回异常,往往都不是框架“没生效”,而是返回方式和注解语义混在了一起。

这篇文章按实际开发中的两类场景来拆解:传统服务端渲染如何返回页面,前后端分离项目如何返回 JSON;同时把 void 的 404 坑、@ResponseBody 的生效位置,以及 @RestController 的本质一起讲清楚,方便你看到代码就能判断底层会怎么执行。

SpringMVC 常见返回方式有哪些

返回 String:最常见的视图名称写法

在传统服务端渲染项目里,Controller 方法直接返回字符串,默认含义就是“视图名称”。

SpringMVC 服务端渲染返回链路信息图,展示 String、ModelAndView、Model + String 三种方式如何进入视图解析器并完成页面渲染
服务端渲染的三种主流返回写法把页面跳转场景放在一张图里,更容易看清三种常见返回写法的共同点和差别。
@RequestMapping("/index")
public String index(){
    // 返回视图名
    return "index";
}

这段代码的执行流程可以概括为:

  1. SpringMVC 拿到返回值 index
  2. 视图解析器 ViewResolver 按前缀、后缀拼接出目标资源,例如 /WEB-INF/index.jsp
  3. 请求被转发到对应页面渲染,同时把 Model 中的数据带过去

这里有一个默认规则要先记住:返回普通字符串 = 返回视图名称

但如果字符串带了特殊前缀,含义就会变:

  • return "forward:/page":表示请求转发,不再走视图解析器
  • return "redirect:/login":表示重定向,不再走视图解析器

返回 void:为什么最容易出现 404

void 是最容易让人误判的一种返回方式,因为它看上去“什么都不返回”,但 SpringMVC 并不会什么都不做。

@RequestMapping("/demo")
public void demo(HttpServletRequest request,HttpServletResponse response){
}

如果方法直接返回 void,SpringMVC 会尝试查找一个和当前请求路径同名的视图。找不到,就会出现 404。

也就是说,void 并不等于“结束响应”,除非你在方法内部已经手动把响应处理完。

常见的三种处理方式如下。

方式 1:使用 response 手动输出内容

如果你要直接返回文本或 JSON,可以自己写响应体:

response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{"code":200}");

这种方式能用,但问题也很明显:直接依赖原生 Servlet API,代码偏底层,写多了会比较繁琐,因此不适合在业务代码里大范围使用。

方式 2:在方法里手动 forward 或 redirect

如果你的目标是跳页面,也可以在 void 方法内部手动完成请求转发或重定向。

请求转发:

request.getRequestDispatcher("/success").forward(request,response);

重定向:

response.sendRedirect("/login");

这里有个很重要的约束:一旦已经执行了 forwardredirect,就不要再尝试返回视图名称,也不要继续往响应体里输出内容,否则很容易抛出异常。

返回 ModelAndView:数据和视图一起封装

ModelAndView 的含义比较直接,就是把数据和视图名放进同一个对象里返回。

@RequestMapping("/list")
public ModelAndView list(){
    ModelAndView mv = new ModelAndView();
    mv.addObject("name","张三"); // 存入request域,页面可以获取
    mv.setViewName("list"); // 设置视图名称
    return mv;
}

它的特点是:

  • ModelAndView = Model(数据) + View(视图名称)
  • 在老项目里比较常见
  • 优点是表达集中,缺点是相对没那么轻量

如果项目还保留较多传统 MVC 写法,这种形式依然很常见;但在新代码里,很多团队会更偏向下面的 Model + String

返回 Model + String:服务端渲染里的主流写法

这种写法把“数据”和“视图”拆开表达,通常可读性更高。

@RequestMapping("/list")
public String list(Model model){
    model.addAttribute("name","张三");
    return "list"; // 视图名称
}
  • Model:负责向 request 域存放页面需要的数据
  • 返回字符串:负责指定视图名称

相比 ModelAndView,这种写法更清晰地表达了两个动作:一边组装页面数据,一边决定跳到哪个视图。因此在服务端渲染项目里,它通常是更常见的主流方案。

前后端分离项目如何返回 JSON

@ResponseBody 的核心作用是什么

前面几种方式,本质上都属于“返回页面”这条链路;而前后端分离项目并不需要服务器跳转页面,更常见的目标是直接返回 JSON 数据。

SpringMVC JSON 返回与注解关系信息图,展示 @ResponseBody、@RestController、消息转换器和常见冲突场景
@ResponseBody 与 @RestC前后端分离项目最关键的是看清:什么时候走视图解析,什么时候直接写响应体。

这时核心注解就是 @ResponseBody

// 方式1:注解加在方法上
@RequestMapping("/user")
@ResponseBody
public User getUser(){
    User user = new User();
    user.setName("李四");
    return user; // 框架自动转为JSON
}

@ResponseBody 告诉 SpringMVC:

  • 不要再使用视图解析器
  • 不要再跳转页面
  • 直接把返回值写入响应体

如果返回的是对象,底层会经过这样的链路:

返回对象 → MappingJackson2HttpMessageConverter → Jackson序列化 → JSON字符串 → 输出到response响应体

所以,@ResponseBody 的本质不是“返回 JSON 注解”这么简单,它更准确的语义是:放弃视图解析,改走消息转换器,把返回值直接当响应体输出

@ResponseBody 可以写在哪里

@ResponseBody 有两种常见写法,而且作用范围不同。

第一种,写在方法上,只对当前方法生效:

@Controller
public class UserController{
    @ResponseBody
    @RequestMapping("/user")
    public User getUser(){
        return new User();
    }
}

第二种,写在类上,对当前类中所有方法生效:

@Controller
@ResponseBody
public class UserController{
    // 所有方法返回值都会序列化为JSON,不再解析视图
}

如果一个 Controller 里既有页面跳转又有接口返回,通常更适合把 @ResponseBody 放在具体方法上,而不是直接加在整个类上。

@RestController 到底是什么

很多人把 @RestController 当成一个“专门返回 JSON 的 Controller 注解”,这个理解没错,但还可以更进一步。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
}

从定义上就能看出来,它本质上是一个组合注解:

@RestController = @Controller + @ResponseBody

也就是说,只要类上标了 @RestController,当前 Controller 的所有方法就默认直接返回响应体,通常表现为返回 JSON,而不是跳转视图。

// 等价于 @Controller + @ResponseBody
@RestController
public class UserController{
    @RequestMapping("/user")
    public User getUser(){
        return new User();
    }
}

@Controller 和 @RestController 怎么区分

这两个注解最容易混淆,但判断标准其实很简单。

  1. @Controller
    • 默认返回字符串时,表示视图名称,主要用于页面跳转
    • 如果想返回 JSON,必须额外加 @ResponseBody
  2. @RestController
    • 自带 @ResponseBody
    • 所有方法默认直接返回 JSON 或其他响应体内容

因此,服务端渲染项目通常以 @Controller 为主;前后端分离项目通常以 @RestController 为主。

几个最容易踩坑的地方

误区 1:@RestController 能不能直接返回页面

结论是:不能按普通视图名那样直接跳页面

@RestController 里,如果你直接返回一个字符串,默认会被当作响应体内容输出,而不是交给视图解析器。

原文给出的跳转写法是:

// RestController中跳转写法
return "forward:/index";
return "redirect:/login";

结合下文的注解语义,这里更稳妥的理解是:在带有响应体语义的场景里,返回字符串本身非常容易与“视图跳转”发生冲突。实际开发中,如果某个类既承担页面跳转又承担接口返回,最好还是拆回 @Controller,由页面方法返回视图名,接口方法单独加 @ResponseBody

误区 2:@ResponseBody 和 forward/redirect 同时使用

这是一个非常典型的冲突场景:

@ResponseBody
public String test(){
    // 冲突!@ResponseBody会把"forward:/index"当成普通字符串直接返回给前端,不会执行转发
    return "forward:/index";
}

原因很直接:加了 @ResponseBody 之后,返回值已经进入“写响应体”这条链路,forward:/redirect:/ 不再被当成视图控制前缀,而只是一个普通字符串。

所以这类代码的关键判断原则是:要么走视图解析/跳转,要么走响应体输出,二者不要混写

误区 3:@ResponseBody 能不能解析 JSP 页面

不能。

@ResponseBody 的语义就是“把返回值当作响应体输出”,它天然意味着放弃视图解析。因此它不可能再去解析 JSP,也不会帮你跳到模板页面。

按场景快速选型:一张表看懂返回方式

三类场景可以这样归类

类别 A:服务端渲染(跳转页面,传统 JSP/Thymeleaf)

  1. 返回视图名称 String(配合 Model 传数据)
  2. 返回 ModelAndView

控制器注解:@Controller,不能加 @ResponseBody

类别 B:手动操控原生响应(void 返回)

  1. response 输出内容
  2. 代码内手动 forward / redirect

适合特殊场景,项目中不推荐大量使用

类别 C:前后端分离(返回 JSON,不跳转页面)

  1. @Controller + 方法上 @ResponseBody
  2. @RestController(内置 ResponseBody,直接返回实体对象)

返回方式对照表

返回类型 配套注解 作用
String(普通视图名) @Controller 走视图解析器,跳转页面
String(forward:/ redirect:/) @Controller 转发/重定向
ModelAndView @Controller 封装数据+视图,跳转页面
void @Controller 需要手动 response 输出/转发/重定向,否则 404
实体类/集合 @Controller + @ResponseBody 自动序列化为 JSON 返回
实体类/集合 @RestController 全部方法默认返回 JSON

实际项目里怎么选更合适

如果是传统服务端渲染项目,优先使用 @Controller,方法返回视图名称,并配合 Model 传递页面数据。

如果是前后端分离项目,例如 Vue、小程序或其他接口型应用,通常统一使用 @RestController,让方法直接返回实体对象或统一结果对象。

如果一个 Controller 里同时存在页面跳转和 JSON 接口,最稳妥的做法是继续使用 @Controller:页面方法正常返回视图名,接口方法单独加上 @ResponseBody。这样职责更清晰,也更不容易踩到返回值语义冲突的问题。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多