Controller 层优化四步曲
Controller 层优化四步曲
前言
在开发过程中,Controller 层作为系统与外界交互的桥梁,承担着接收请求、解析参数、调用业务逻辑、处理异常等职责。
然而,随着业务复杂度的增加,Controller 层的代码往往会变得臃肿且难以维护。
为了提升代码的可读性、可维护性以及系统的健壮性,我们可以通过以下四个步骤对 Controller 层进行改造:
- 统一返回结构
- 统一包装处理
- 参数校验
- 自定义异常与统一拦截异常

1.统一返回结构
在前后端分离的架构中,统一的返回结构能够显著提升接口的可读性和可维护性。
通过定义统一的返回格式,前端开发人员可以更清晰地判断接口调用是否成功,而不需要依赖返回值的具体内容。
定义返回数据结构
首先,我们可以定义一个通用的返回结构,包含状态码、状态信息以及返回的数据。
以下是一个示例:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {private Integer code;private String message;private T data;public static <T> Result<T> success(T data) {return new Result<>(200, "操作成功", data);}public static <T> Result<T> error(String message) {return new Result<>(500, message, null);}
}
在这个结构中,code 表示状态码,message 表示状态信息,data 则是具体的返回数据。
通过这种方式,所有的接口都可以返回统一的格式,便于前端处理。
2.统一包装处理
为了进一步简化 Controller 层的代码,我们可以使用 Spring 提供的 ResponseBodyAdvice 来统一处理返回值的包装。
这样,Controller 层只需要返回业务数据,而不需要手动封装返回结构。
使用 ResponseBodyAdvice 进行统一包装
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof Result) {return body;}return Result.success(body);}
}
通过这种方式,所有的 Controller 返回值都会被自动包装成 Result 对象,减少了重复代码。
3. 参数校验
参数校验是保证系统健壮性的重要手段。通过将参数校验逻辑从业务代码中剥离出来,不仅可以减少代码的耦合度,还能提升代码的可读性。
使用 @Validated 进行参数校验
Spring 提供了 @Validated 注解,可以方便地对请求参数进行校验。
我们可以在 DTO 对象中定义校验规则,并在 Controller 方法中使用 @Validated 注解进行校验。
@Data
public class UserDTO {@NotBlank(message = "用户名不能为空")private String username;@NotBlank(message = "密码不能为空")@Length(min = 6, max = 20, message = "密码长度必须在6到20之间")private String password;@Email(message = "邮箱格式不正确")private String email;
}@RestController
@RequestMapping("/user")
public class UserController {@PostMapping("/register")public Result<String> register(@RequestBody @Validated UserDTO userDTO) {// 业务逻辑return Result.success("注册成功");}
}
通过这种方式,参数校验的逻辑与业务逻辑完全解耦,代码更加清晰。
自定义异常与统一拦截异常
在实际开发中,系统可能会抛出各种异常。
为了提升系统的健壮性,我们可以自定义异常,并通过统一的异常拦截器来处理这些异常。
自定义异常
我们可以根据业务需求定义不同的异常类,例如 BusinessException 和 ForbiddenException:
public class BusinessException extends RuntimeException {public BusinessException(String message) {super(message);}
}public class ForbiddenException extends RuntimeException {public ForbiddenException(String message) {super(message);}
}
统一异常拦截
通过 @RestControllerAdvice 和 @ExceptionHandler,我们可以统一处理系统中的异常,并返回统一的错误信息。
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public Result<String> handleBusinessException(BusinessException ex) {return Result.error(ex.getMessage());}@ExceptionHandler(ForbiddenException.class)public Result<String> handleForbiddenException(ForbiddenException ex) {return Result.error("无权访问");}@ExceptionHandler(Exception.class)public Result<String> handleException(Exception ex) {return Result.error("系统异常,请稍后重试");}
}
通过这种方式,所有的异常都会被统一处理,并返回给前端统一的错误格式。
总结
通过对 Controller 层的改造,我们可以显著提升代码的可读性、可维护性以及系统的健壮性。
统一的返回结构、自动化的参数校验、自定义异常与统一拦截异常,这些措施不仅减少了重复代码,还使得代码更加清晰和易于维护。
在实际开发中,我们可以根据具体的业务需求,进一步优化和扩展这些策略。
– 欢迎点赞、关注、转发、收藏【我码玄黄】,各大平台同名。
相关文章:
Controller 层优化四步曲
Controller 层优化四步曲 前言 在开发过程中,Controller 层作为系统与外界交互的桥梁,承担着接收请求、解析参数、调用业务逻辑、处理异常等职责。 然而,随着业务复杂度的增加,Controller 层的代码往往会变得臃肿且难以维护。 …...
Python数据分析-Python语法基础,IPython和Jupyter-Notebooks(二)
title: ‘Python数据分析:Python语法基础,IPython和Jupyter Notebooks(二)’ tags: python数据分析 categories:python数据分析 keywords:python数据分析 cover: …/img/404_icecream_whale.png description: 本文介绍python的基础语法和jup…...
Nginx 开发总结
文章目录 1. Nginx 基础概念1-1、什么是 Nginx1-2、Nginx 的工作原理1-3、Nginx 的核心特点1-4、Nginx 的常见应用场景1-5、Nginx 与 Apache 的区别1-6、 Nginx 配置的基本结构1-7、Nginx 常见指令 2. Nginx 配置基础2-1、Nginx 配置文件结构2-2、全局配置 (Global Block)2-3、…...
centos7安装SVN
[rootVM-16-3-centos ~]# yum install subversion -y [rootVM-16-3-centos ~]# svnserve --version // 创建目录 [rootVM-16-3-centos ~]# mkdir -p /opt/svn/repos // 创建新的空版本库,执行后会在repos文件夹下建立多个文件,待修改 [rootVM-16-3-cento…...
LTV预估 | 多视角对比学习框架CMLTV
😄 cmltv的loss好多哟,花样好多哟~ 文章目录 1 精简总结2 背景&挑战3 方法4 实验 ✅【arxiv-2023 华为 CMLTV】《Contrastive Multi-view Framework for Customer Lifetime Value Prediction》 论文链接: https://arxiv.or…...
llama.cpp LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK2
llama.cpp LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK2 1. LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK22. LLM_ARCH_DEEPSEEK and LLM_ARCH_DEEPSEEK23. struct ggml_cgraph * build_deepseek() and struct ggml_cgraph * build_deepseek2()References 不宜吹捧中国大语言模型的同…...
C语言自定义数据类型详解(二)——结构体类型(下)
书接上回,前面我们已经给大家介绍了如何去声明和创建一个结构体,如何初始化结构体变量等这些关于结构体的基础知识。下面我们将继续给大家介绍和结构体有关的知识: 今天的主题是:结构体大小的计算并简单了解一下位段的相关知识。…...
DeepSeek学术写作测评第二弹:数据分析、图表解读,效果怎么样?
我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 针对最近全球热议的DeepSeek开源大模型,娜姐昨天分析了关于论文润色、中译英的详细效果测评: DeepSeek学术写作测评第一弹:论文润色&#…...
深入理解 Python 中的 `__all__`:控制模块的公共接口
在 Python 编程中,模块化设计是构建可维护和可扩展代码的关键。模块不仅帮助我们组织代码,还能通过隐藏实现细节来提高代码的可读性和安全性。Python 提供了多种机制来控制模块的可见性,其中 __all__ 是一个非常重要但常被忽视的特性。本文将…...
虚幻基础07:蓝图接口
能帮到你的话,就给个赞吧 😘 文章目录 作用原理事件函数 作用 实现对象间的通知。 A 通知 B 做什么。 原理 将接口抽象为蓝图,使得任意蓝图都能直接访问。 只需要再传入对象地址,就能执行对象的功能。 事件 黄色:…...
数据结构---哈希表
基本概念 哈希函数(Hash Function)是一种将输入的数据(通常是任意大小的)映射到固定大小的输出(通常是一个固定长度的值)的函数。这个输出值通常称为“哈希值”(Hash Value)或“哈希…...
DataWhale组队学习 leetCode task4
1. 滑动窗口算法介绍 想象你正在用一台望远镜观察一片星空。望远镜的镜头大小是固定的,你可以通过滑动镜头来观察不同的星区。滑动窗口算法就像这台望远镜,它通过一个固定或可变大小的“窗口”来观察数组或字符串中的连续区间。 滑动操作:就像…...
【ESP32】ESP-IDF开发 | WiFi开发 | UDP用户数据报协议 + UDP客户端和服务器例程
1. 简介 UDP协议(User Datagram Protocol),全称用户数据报协议,它是一种面向非连接的协议,面向非连接指的是在正式通信前不必与对方先建立连接, 不管对方状态就直接发送。至于对方是否可以接收到这些数据内…...
【PyQt5】数据库连接失败: Driver not loaded Driver not loaded
报错内容如下: 可以看到目前所支持的数据库驱动仅有[‘QSQLITE’, ‘QMARIADB’, ‘QODBC’, ‘QODBC3’, ‘QPSQL’, ‘QPSQL7’] 我在网上查找半天解决方法未果,其中有一篇看评论反馈是可以使用的,但是PyQt5的版本有点低,5.12…...
Unity游戏(Assault空对地打击)开发(1) 创建项目和选择插件
目录 前言 创建项目 插件导入 地形插件 前言 这是游戏开发第一篇,进行开发准备。 创作不易,欢迎支持。 我的编辑器布局是【Tall】,建议调整为该布局,如下。 创建项目 首先创建一个项目,过程略,名字请勿…...
Rust:如何动态调用字符串定义的 Rhai 函数?
在 Rust 中使用 Rhai 脚本引擎时,你可以动态地调用传入的字符串表示的 Rhai 函数。Rhai 是一个嵌入式脚本语言,专为嵌入到 Rust 应用中而设计。以下是一个基本示例,展示了如何在 Rust 中调用用字符串传入的 Rhai 函数。 首先,确保…...
A星算法两元障碍物矩阵转化为rrt算法四元障碍物矩阵
对于a星算法obstacle所表示的障碍物障碍物信息,每行表示一个障碍物的坐标,例如2 , 3; % 第一个障碍物在第二行第三列,也就是边长为1的正方形障碍物右上角横坐标是2,纵坐标为3,障碍物的宽度和高度始终为1.在rrt路径规划…...
【C++】设计模式详解:单例模式
文章目录 Ⅰ. 设计一个类,不允许被拷贝Ⅱ. 请设计一个类,只能在堆上创建对象Ⅲ. 请设计一个类,只能在栈上创建对象Ⅳ. 请设计一个类,不能被继承Ⅴ. 请设计一个类,只能创建一个对象(单例模式)&am…...
单细胞分析基础-第一节 数据质控、降维聚类
scRNA_pipeline\1.Seurat 生物技能树 可进官网查询 添加链接描述 分析流程 准备:R包安装 options("repos"="https://mirrors.ustc.edu.cn/CRAN/") if(!require("BiocManager")) install.packages("BiocManager",update = F,ask =…...
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude
多项日常使用测试,带你了解如何选择AI工具 Deepseek VS ChatGpt VS Claude 注:因为考虑到绝大部分人的使用,我这里所用的模型均为免费模型。官方可访问的。ChatGPT这里用的是4o Ai对话,编程一直以来都是人们所讨论的话题。Ai的出现…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
