Skip to content

响应数据格式化使用指南

📖 概述

响应数据格式化功能通过两个核心注解实现:

  • @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 接口方法上,用于启用响应格式化功能。

属性类型默认值说明
enabledbooleantrue是否启用格式化

@ResponseFormatField 注解(字段级别)

@ResponseFormatField 注解标注在实体类字段上,定义具体的格式化规则。

核心属性

属性类型默认值说明
enabledbooleantrue是否启用格式化
typeFormatTypeNONE格式化类型
desensitizeTypeDesensitizeTypeNONE脱敏类型

格式化配置

属性类型默认值说明
datePatternString"yyyy-MM-dd HH:mm:ss"日期格式模式
timeZoneString""时区设置
numberPatternString""数字格式模式
currencySymbolString""货币符号

字符串处理

属性类型默认值说明
maxLengthint0最大长度限制
stripHtmlbooleanfalse移除 HTML 标签
toUpperCasebooleanfalse转换为大写
toLowerCasebooleanfalse转换为小写
trimbooleantrue去除首尾空格

高级配置

属性类型默认值说明
desensitizeRuleString""自定义脱敏规则
dictTypeString""字典类型
unitString""单位后缀
formatterClassResponseFormatter.class自定义格式化器
paramsString[]{}格式化器参数
formatFieldNameString""格式化后的字段名称。如果不设置或为空,则直接将格式化后的值设置到当前字段;如果设置了字段名,格式化后的值将以String类型存储在此字段中,原字段保持不变
clearOriginalValuebooleanfalse是否清空原有值,true时只保留格式化后的值

🎯 格式化类型 (FormatType)

转义类型

类型说明示例
HTML_ESCAPEHTML 转义<script>&lt;script&gt;
JSON_ESCAPEJSON 转义"hello"\"hello\"
XML_ESCAPEXML 转义<root>&lt;root&gt;
SQL_ESCAPESQL 转义O'ReillyO''Reilly
URL_ENCODEURL 编码hello worldhello%20world

格式化类型

类型说明示例
DATE_FORMAT日期格式化根据 datePattern 格式化
NUMBER_FORMAT数字格式化12345.6712,345.67
CURRENCY货币格式化1000$1,000.00
PHONE手机号格式化13812345678138-1234-5678
EMAIL邮箱格式化转换为小写并验证格式

🔒 脱敏类型 (DesensitizeType)

个人信息脱敏

类型说明示例
PHONE手机号脱敏13812345678138****5678
EMAIL邮箱脱敏user@example.comus***@example.com
ID_CARD身份证脱敏1101011990010112341101**********1234
NAME姓名脱敏张三丰张*丰
CHINESE_NAME中文姓名脱敏王小明王*明

金融信息脱敏

类型说明示例
BANK_CARD银行卡脱敏62220212345678901236222********0123
PASSWORD密码脱敏password123******

其他信息脱敏

类型说明示例
ADDRESS地址脱敏北京市朝阳区某某街道北京市朝阳区****
CAR_LICENSE车牌号脱敏京A12345京A***5
PASSPORT护照号脱敏G12345678G1****78
FIXED_PHONE固定电话脱敏010-12345678010-****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;
    }
}

📝 重要说明

注解使用规则

  1. @ResponseFormat 注解

    • 必须标注在 Controller 接口方法上
    • 用于启用响应格式化功能
    • 只有添加了此注解的接口才会进行格式化处理
  2. @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);
    }
}

💡 提示:建议在开发环境中充分测试格式化效果,确保满足业务需求后再部署到生产环境。