主题切换
响应数据格式化使用指南
📖 概述
响应数据格式化功能通过两个核心注解实现:
@ResponseFormat
:必须标注在 Controller 接口方法上,用于启用响应格式化功能@ResponseFormatField
:标注在实体类字段上,定义具体的格式化规则
通过 AOP 切面自动拦截 Controller 响应,对标记字段进行格式化处理。它提供了数据脱敏、格式转换、内容转义等多种功能,帮助开发者轻松处理敏感数据和格式化需求。
✨ 核心特性
🔒 数据安全
- 多种脱敏类型:手机号、邮箱、身份证、银行卡、姓名、地址等
- 自定义脱敏规则:灵活配置保留位数和脱敏字符
- 安全转义:HTML、JSON、XML、SQL、URL 等内容转义
🎨 格式化处理
- 日期时间:自定义格式、时区转换
- 数字货币:千分位分隔符、小数控制、货币符号
- 字符串:长度限制、大小写转换、HTML 标签清理
- 字典映射:状态码转换、枚举值映射
🚀 高级功能
- 自定义格式化器:扩展复杂格式化逻辑
- 响应式支持:兼容 Mono、Flux 等响应式类型
- 类型安全:枚举类型提供更好的 IDE 支持
- 性能优化:智能缓存和批量处理
🚀 快速开始
基础用法
java
@Data
public class UserVO {
// 手机号脱敏
@ResponseFormatField(desensitizeType = DesensitizeType.PHONE)
private String phone; // 13812345678 → 138****5678
// 日期格式化
@ResponseFormatField(type = FormatType.DATE_FORMAT, datePattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
// 货币格式化
@ResponseFormatField(type = FormatType.CURRENCY, currencySymbol = "¥", numberPattern = "#,##0.00")
private BigDecimal salary; // 12345.67 → ¥12,345.67
}
Controller 示例
java
@RestController
public class UserController {
// 必须在接口方法上添加 @ResponseFormat 注解才能启用格式化功能
@ResponseFormat
@GetMapping("/user/{id}")
public UserVO getUser(@PathVariable Long id) {
// 返回的 UserVO 对象会自动应用 @ResponseFormatField 格式化
return userService.getUserById(id);
}
// 不添加 @ResponseFormat 注解的接口不会进行格式化处理
@GetMapping("/user/raw/{id}")
public UserVO getUserRaw(@PathVariable Long id) {
// 返回原始数据,不进行格式化
return userService.getUserById(id);
}
}
📋 注解属性详解
@ResponseFormat 注解(Controller 方法级别)
@ResponseFormat
注解必须标注在 Controller 接口方法上,用于启用响应格式化功能。
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
enabled | boolean | true | 是否启用格式化 |
@ResponseFormatField 注解(字段级别)
@ResponseFormatField
注解标注在实体类字段上,定义具体的格式化规则。
核心属性
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
enabled | boolean | true | 是否启用格式化 |
type | FormatType | NONE | 格式化类型 |
desensitizeType | DesensitizeType | NONE | 脱敏类型 |
格式化配置
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
datePattern | String | "yyyy-MM-dd HH:mm:ss" | 日期格式模式 |
timeZone | String | "" | 时区设置 |
numberPattern | String | "" | 数字格式模式 |
currencySymbol | String | "" | 货币符号 |
字符串处理
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
maxLength | int | 0 | 最大长度限制 |
stripHtml | boolean | false | 移除 HTML 标签 |
toUpperCase | boolean | false | 转换为大写 |
toLowerCase | boolean | false | 转换为小写 |
trim | boolean | true | 去除首尾空格 |
高级配置
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
desensitizeRule | String | "" | 自定义脱敏规则 |
dictType | String | "" | 字典类型 |
unit | String | "" | 单位后缀 |
formatter | Class | ResponseFormatter.class | 自定义格式化器 |
params | String[] | {} | 格式化器参数 |
formatFieldName | String | "" | 格式化后的字段名称。如果不设置或为空,则直接将格式化后的值设置到当前字段;如果设置了字段名,格式化后的值将以String类型存储在此字段中,原字段保持不变 |
clearOriginalValue | boolean | false | 是否清空原有值,true时只保留格式化后的值 |
🎯 格式化类型 (FormatType)
转义类型
类型 | 说明 | 示例 |
---|---|---|
HTML_ESCAPE | HTML 转义 | <script> → <script> |
JSON_ESCAPE | JSON 转义 | "hello" → \"hello\" |
XML_ESCAPE | XML 转义 | <root> → <root> |
SQL_ESCAPE | SQL 转义 | O'Reilly → O''Reilly |
URL_ENCODE | URL 编码 | hello world → hello%20world |
格式化类型
类型 | 说明 | 示例 |
---|---|---|
DATE_FORMAT | 日期格式化 | 根据 datePattern 格式化 |
NUMBER_FORMAT | 数字格式化 | 12345.67 → 12,345.67 |
CURRENCY | 货币格式化 | 1000 → $1,000.00 |
PHONE | 手机号格式化 | 13812345678 → 138-1234-5678 |
EMAIL | 邮箱格式化 | 转换为小写并验证格式 |
🔒 脱敏类型 (DesensitizeType)
个人信息脱敏
类型 | 说明 | 示例 |
---|---|---|
PHONE | 手机号脱敏 | 13812345678 → 138****5678 |
EMAIL | 邮箱脱敏 | user@example.com → us***@example.com |
ID_CARD | 身份证脱敏 | 110101199001011234 → 1101**********1234 |
NAME | 姓名脱敏 | 张三丰 → 张*丰 |
CHINESE_NAME | 中文姓名脱敏 | 王小明 → 王*明 |
金融信息脱敏
类型 | 说明 | 示例 |
---|---|---|
BANK_CARD | 银行卡脱敏 | 6222021234567890123 → 6222********0123 |
PASSWORD | 密码脱敏 | password123 → ****** |
其他信息脱敏
类型 | 说明 | 示例 |
---|---|---|
ADDRESS | 地址脱敏 | 北京市朝阳区某某街道 → 北京市朝阳区**** |
CAR_LICENSE | 车牌号脱敏 | 京A12345 → 京A***5 |
PASSPORT | 护照号脱敏 | G12345678 → G1****78 |
FIXED_PHONE | 固定电话脱敏 | 010-12345678 → 010-****78 |
自定义脱敏
java
// 保留前3位和后4位,中间用*替换
@ResponseFormatField(desensitizeType = DesensitizeType.CUSTOM, desensitizeRule = "3,4")
private String customData; // "1234567890123" → "123******0123"
// 保留前2位,其余用*替换
@ResponseFormatField(desensitizeType = DesensitizeType.CUSTOM, desensitizeRule = "2")
private String shortData; // "abcdefg" → "ab*****"
格式化字段名称使用
formatFieldName
属性用于控制格式化后的值存储位置:
java
@Data
public class UserVO {
// 方式1:直接替换原字段值(默认行为)
@ResponseFormatField(desensitizeType = DesensitizeType.PHONE)
private String phone; // 原值被替换:13812345678 → 138****5678
// 方式2:保留原值,格式化值存储到新字段
@ResponseFormatField(
desensitizeType = DesensitizeType.PHONE,
formatFieldName = "phoneDisplay"
)
private String phoneNumber; // 原值保持:13812345678,新增字段 phoneDisplay: "138****5678"
// 方式3:自定义格式化字段名
@ResponseFormatField(
type = FormatType.CURRENCY,
currencySymbol = "¥",
numberPattern = "#,##0.00",
formatFieldName = "salaryText"
)
private BigDecimal salary; // 原值保持:12345.67,新增字段 salaryText: "¥12,345.67"
// 方式4:结合 clearOriginalValue 使用
@ResponseFormatField(
desensitizeType = DesensitizeType.ID_CARD,
formatFieldName = "idCardMasked",
clearOriginalValue = true
)
private String idCard; // 原值被清空:null,新增字段 idCardMasked: "1101**********1234"
}
响应示例:
json
{
"phone": "138****5678", // 直接替换原值
"phoneNumber": "13812345678", // 保留原值
"phoneDisplay": "138****5678", // 格式化后的值
"salary": 12345.67, // 保留原值
"salaryText": "¥12,345.67", // 格式化后的值
"idCard": null, // 原值被清空
"idCardMasked": "1101**********1234" // 格式化后的值
}
⚙️ 配置管理
yaml
wueasy:
response-format:
enabled: true # 全局开关,默认false
🎯 实用示例
1. 用户信息脱敏
java
@Data
public class UserProfileVO {
@ResponseFormatField(desensitizeType = DesensitizeType.CHINESE_NAME)
private String realName; // 李小明 → 李*明
@ResponseFormatField(desensitizeType = DesensitizeType.PHONE)
private String phoneNumber; // 13812345678 → 138****5678
@ResponseFormatField(desensitizeType = DesensitizeType.EMAIL)
private String email; // user@example.com → us***@example.com
@ResponseFormatField(desensitizeType = DesensitizeType.ID_CARD)
private String idCard; // 110101199001011234 → 1101**********1234
@ResponseFormatField(desensitizeType = DesensitizeType.ADDRESS)
private String address; // 北京市朝阳区建国路1号 → 北京市朝阳区****
}
java
@RestController
public class UserController {
@ResponseFormat // 必须添加此注解才能启用格式化
@GetMapping("/user/profile/{id}")
public UserProfileVO getUserProfile(@PathVariable Long id) {
return userService.getUserProfile(id);
}
}
2. 金融数据格式化
java
@Data
public class FinancialVO {
@ResponseFormatField(
type = FormatType.CURRENCY,
currencySymbol = "¥",
numberPattern = "#,##0.00"
)
private BigDecimal balance; // 12345.67 → ¥12,345.67
@ResponseFormatField(desensitizeType = DesensitizeType.BANK_CARD)
private String bankCard; // 6222021234567890123 → 6222********0123
@ResponseFormatField(
type = FormatType.NUMBER_FORMAT,
numberPattern = "#,##0.####",
unit = "%"
)
private BigDecimal interestRate; // 0.0325 → 3.25%
}
java
@RestController
public class FinancialController {
@ResponseFormat // 必须添加此注解才能启用格式化
@GetMapping("/financial/account/{id}")
public FinancialVO getFinancialInfo(@PathVariable Long id) {
return financialService.getFinancialInfo(id);
}
}
3. 内容安全处理
java
@Data
public class ContentVO {
@ResponseFormatField(type = FormatType.HTML_ESCAPE, stripHtml = true)
private String userContent; // 清理并转义用户输入内容
@ResponseFormatField(type = FormatType.JSON_ESCAPE)
private String jsonData; // JSON 字符串转义
@ResponseFormatField(type = FormatType.URL_ENCODE)
private String urlParam; // URL 参数编码
@ResponseFormatField(maxLength = 100, trim = true)
private String description; // 限制长度并去除空格
}
java
@RestController
public class ContentController {
@ResponseFormat // 必须添加此注解才能启用格式化
@GetMapping("/content/{id}")
public ContentVO getContent(@PathVariable Long id) {
return contentService.getContent(id);
}
}
4. 日期时间格式化
java
@Data
public class TimeVO {
@ResponseFormatField(
type = FormatType.DATE_FORMAT,
datePattern = "yyyy年MM月dd日 HH:mm:ss"
)
private Date createTime; // 2024-01-15 14:30:00 → 2024年01月15日 14:30:00
@ResponseFormatField(
type = FormatType.DATE_FORMAT,
datePattern = "yyyy-MM-dd'T'HH:mm:ss'Z'",
timeZone = "UTC"
)
private Date updateTime; // UTC 时间格式
@ResponseFormatField(
type = FormatType.DATE_FORMAT,
datePattern = "MM/dd/yyyy"
)
private LocalDate birthDate; // 美式日期格式
}
java
@RestController
public class TimeController {
@ResponseFormat // 必须添加此注解才能启用格式化
@GetMapping("/time/info")
public TimeVO getTimeInfo() {
return timeService.getTimeInfo();
}
}
5. 字典转换服务接口
字典转换功能需要实现 DictConvertService
接口来提供字典值到显示文本的转换:
接口定义
java
/**
* 字典服务接口
* @author: fallsea
* @version 1.0
*/
public interface DictConvertService {
/**
* 根据字典类型和值获取显示文本
* @param dictType 字典类型
* @param value 字典值
* @return 显示文本
*/
String getDictText(String dictType, Object value);
}
实现示例
java
/**
* 字典转换服务实现
*/
@Service
public class DictConvertServiceImpl implements DictConvertService {
@Override
public String getDictText(String dictType, Object value) {
// 返回对应的显示文本,如果找不到则返回原值
return "";
}
}
使用示例
java
@Data
public class UserOrderVO {
private Long orderId;
// 用户状态字典转换
@ResponseFormatField(dictType = "user_status")
private Integer userStatus; // 1 → "正常"
// 订单类型字典转换
@ResponseFormatField(dictType = "order_type")
private String orderType; // "ONLINE" → "线上订单"
// 性别字典转换
@ResponseFormatField(dictType = "gender")
private String gender; // "M" → "男"
// 订单状态字典转换
@ResponseFormatField(dictType = "order_status")
private String orderStatus; // "PENDING" → "待支付"
// 组合使用:字典转换 + 单位
@ResponseFormatField(dictType = "priority_level", unit = "级")
private Integer priority; // 1 → "高级"
}
java
@RestController
public class OrderController {
@ResponseFormat // 必须添加此注解才能启用格式化
@GetMapping("/order/{id}")
public UserOrderVO getOrder(@PathVariable Long id) {
return orderService.getOrder(id);
}
}
🔧 自定义格式化器
创建自定义格式化器
java
/**
* 自定义手机号格式化器
* 支持多种手机号格式化样式
*/
public class CustomPhoneFormatter implements ResponseFormatter {
@Override
public Object format(Object value, ResponseFormatField annotation) {
if (value == null) {
return null;
}
String phone = String.valueOf(value).replaceAll("[^0-9]", "");
String[] params = annotation.params();
String style = params.length > 0 ? params[0] : "default";
if (phone.length() == 11 && phone.startsWith("1")) {
switch (style) {
case "dash":
return phone.substring(0, 3) + "-" + phone.substring(3, 7) + "-" + phone.substring(7);
case "space":
return phone.substring(0, 3) + " " + phone.substring(3, 7) + " " + phone.substring(7);
case "bracket":
return "(" + phone.substring(0, 3) + ") " + phone.substring(3, 7) + "-" + phone.substring(7);
default:
return phone.substring(0, 3) + " " + phone.substring(3, 7) + " " + phone.substring(7);
}
}
return value;
}
@Override
public boolean supports(Object value, ResponseFormatField annotation) {
if (value == null) {
return false;
}
String phone = String.valueOf(value).replaceAll("[^0-9]", "");
return phone.length() == 11 && phone.startsWith("1");
}
}
使用自定义格式化器
java
@Data
public class ContactVO {
// 默认格式:138 1234 5678
@ResponseFormatField(formatter = CustomPhoneFormatter.class)
private String phone1;
// 短横线格式:138-1234-5678
@ResponseFormatField(formatter = CustomPhoneFormatter.class, params = {"dash"})
private String phone2;
// 括号格式:(138) 1234-5678
@ResponseFormatField(formatter = CustomPhoneFormatter.class, params = {"bracket"})
private String phone3;
}
java
@RestController
public class ContactController {
@ResponseFormat // 必须添加此注解才能启用格式化
@GetMapping("/contact/{id}")
public ContactVO getContact(@PathVariable Long id) {
return contactService.getContact(id);
}
}
高级自定义格式化器
java
/**
* 多功能文本格式化器
* 支持多种文本处理功能
*/
public class AdvancedTextFormatter implements ResponseFormatter {
@Override
public Object format(Object value, ResponseFormatField annotation) {
if (value == null) {
return null;
}
String text = String.valueOf(value);
String[] params = annotation.params();
for (String param : params) {
switch (param) {
case "capitalize":
text = capitalize(text);
break;
case "removeNumbers":
text = text.replaceAll("\\d", "");
break;
case "onlyLetters":
text = text.replaceAll("[^a-zA-Z\\u4e00-\\u9fa5]", "");
break;
case "addPrefix":
text = "【重要】" + text;
break;
}
}
return text;
}
private String capitalize(String text) {
if (text.isEmpty()) return text;
return text.substring(0, 1).toUpperCase() + text.substring(1).toLowerCase();
}
@Override
public boolean supports(Object value, ResponseFormatField annotation) {
return value != null;
}
}
🎯 最佳实践
1. 性能优化
java
@Data
public class OptimizedVO {
// ✅ 推荐:对确实需要格式化的字段使用注解
@ResponseFormatField(desensitizeType = DesensitizeType.PHONE)
private String sensitivePhone;
// ✅ 推荐:对不需要格式化的字段禁用处理
@ResponseFormatField(enabled = false)
private String internalId;
// ❌ 避免:在大量数据的列表字段上使用复杂格式化
// @ResponseFormatField(formatter = ComplexFormatter.class)
// private List<String> largeDataList;
}
2. 安全考虑
java
@Data
public class SecurityVO {
// ✅ 推荐:敏感数据必须脱敏
@ResponseFormatField(desensitizeType = DesensitizeType.ID_CARD)
private String idCard;
// ✅ 推荐:用户输入内容进行转义
@ResponseFormatField(type = FormatType.HTML_ESCAPE, stripHtml = true)
private String userComment;
// ✅ 推荐:密码等敏感信息完全脱敏
@ResponseFormatField(desensitizeType = DesensitizeType.PASSWORD)
private String password;
}
3. 可维护性
java
// ✅ 推荐:使用常量定义格式模式
public class FormatConstants {
public static final String CHINESE_DATE_PATTERN = "yyyy年MM月dd日 HH:mm:ss";
public static final String CURRENCY_PATTERN = "#,##0.00";
public static final String PHONE_DESENSITIZE_RULE = "3,4";
}
@Data
public class MaintainableVO {
@ResponseFormatField(
type = FormatType.DATE_FORMAT,
datePattern = FormatConstants.CHINESE_DATE_PATTERN
)
private Date createTime;
@ResponseFormatField(
type = FormatType.CURRENCY,
currencySymbol = "¥",
numberPattern = FormatConstants.CURRENCY_PATTERN
)
private BigDecimal amount;
}
4. 错误处理
java
@Data
public class RobustVO {
// ✅ 推荐:为可能为空的字段提供默认处理
@ResponseFormatField(
desensitizeType = DesensitizeType.PHONE,
params = {"allowNull"} // 自定义参数处理空值
)
private String optionalPhone;
// ✅ 推荐:使用 try-catch 包装可能出错的格式化
@ResponseFormatField(formatter = SafeFormatter.class)
private String riskyData;
}
public class SafeFormatter implements ResponseFormatter {
@Override
public Object format(Object value, ResponseFormat annotation) {
try {
// 格式化逻辑
return doFormat(value);
} catch (Exception e) {
// 记录错误日志
log.warn("格式化失败,返回原始值: {}", e.getMessage());
return value;
}
}
private Object doFormat(Object value) {
// 实际格式化逻辑
return value;
}
@Override
public boolean supports(Object value, ResponseFormat annotation) {
return value != null;
}
}
📝 重要说明
注解使用规则
@ResponseFormat 注解:
- 必须标注在 Controller 接口方法上
- 用于启用响应格式化功能
- 只有添加了此注解的接口才会进行格式化处理
@ResponseFormatField 注解:
- 标注在实体类字段上
- 定义具体的格式化规则
- 只有在接口方法添加了 @ResponseFormat 注解时才会生效
完整示例
java
// 实体类 - 使用 @ResponseFormatField 定义格式化规则
@Data
public class UserVO {
@ResponseFormatField(desensitizeType = DesensitizeType.PHONE)
private String phone;
@ResponseFormatField(type = FormatType.DATE_FORMAT, datePattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
// Controller - 必须使用 @ResponseFormat 启用格式化
@RestController
public class UserController {
@ResponseFormat // 必须添加,否则格式化不生效
@GetMapping("/user/{id}")
public UserVO getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@GetMapping("/user/raw/{id}") // 未添加 @ResponseFormat,返回原始数据
public UserVO getUserRaw(@PathVariable Long id) {
return userService.getUserById(id);
}
}
💡 提示:建议在开发环境中充分测试格式化效果,确保满足业务需求后再部署到生产环境。