Spring中如何根据用户传入的fields参数返回自定义响应字段
Spring 实现接口动态指定返回字段方案
直接基于Spring默认集成的Jackson序列化能力做字段裁剪即可,无需改造原有业务查询逻辑,实现成本很低,以下是可直接落地的实现方式:
通用无侵入实现:全局拦截 + Jackson动态过滤
适合大部分场景,一次配置所有接口都能支持fields参数动态选字段,不需要每个接口单独写逻辑。
- 首先给需要支持动态字段的实体类加上Jackson的过滤器注解:
如果你的接口用了统一响应包装类(比如import com.fasterxml.jackson.annotation.JsonFilter; // 过滤器ID全局唯一即可 @JsonFilter("dynamicFieldFilter") public class Employee { private Long id; private String name; private String description; private String department; private Integer age; // 省略getter、setter、构造方法 }Result<T>这种结构),给包装类也加上相同的@JsonFilter注解即可支持嵌套对象的字段过滤。 - 编写全局响应拦截器,从请求中读取
fields参数,动态给序列化器配置过滤规则:
写完直接启动项目就能生效,请求import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.json.MappingJacksonValue; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import java.util.Arrays; import java.util.Set; import java.util.stream.Collectors; @RestControllerAdvice public class DynamicFieldAdvice implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 只拦截走Jackson JSON序列化的响应 return MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 从请求参数读取fields String fieldsParam = request.getServletRequest().getParameter("fields"); // 没传参直接返回原响应,不做任何过滤 if (fieldsParam == null || fieldsParam.trim().isEmpty()) { return body; } // 拆分逗号分隔的字段列表,去前后空格 Set<String> includeFields = Arrays.stream(fieldsParam.split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) .collect(Collectors.toSet()); // 构建序列化过滤器:只保留用户指定的字段,其余全部过滤 SimpleFilterProvider filterProvider = new SimpleFilterProvider() .addFilter("dynamicFieldFilter", SimpleBeanPropertyFilter.filterOutAllExcept(includeFields)); // 包装返回值,Spring会自动用我们配置的过滤器做序列化 MappingJacksonValue result = new MappingJacksonValue(body); result.setFilters(filterProvider); return result; } }?fields=name,description时就只会返回这两个字段,传其他字段列表也会自动适配。
轻量场景:单接口手动实现
如果只有个别接口需要这个能力,不需要做全局配置,直接在Controller层接收参数处理即可:
@GetMapping("/employee/{id}") public MappingJacksonValue getEmployee(@PathVariable Long id, @RequestParam(required = false) String fields) { // 原有业务逻辑直接查全量对象即可 Employee employee = employeeService.getById(id); MappingJacksonValue result = new MappingJacksonValue(employee); if (fields != null && !fields.trim().isEmpty()) { Set<String> fieldSet = Arrays.stream(fields.split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) .collect(Collectors.toSet()); SimpleFilterProvider filters = new SimpleFilterProvider() .addFilter("dynamicFieldFilter", SimpleBeanPropertyFilter.filterOutAllExcept(fieldSet)); result.setFilters(filters); } return result; }
注意事项
- 如果需要支持嵌套对象字段(比如
?fields=name,department.deptName这种子属性),自定义SimpleBeanPropertyFilter的匹配逻辑,按属性路径做匹配即可 - 传入的字段名需要和实体类序列化后的JSON字段名一致,如果字段上加了
@JsonProperty("xxx")注解,参数里要传注解配置的名称 - 常规场景下不需要根据fields参数改数据库查询逻辑,序列化层过滤的性能完全足够;如果单条数据字段极多、单字段内容很大,再考虑动态拼接查询SQL减少字段查询即可
内容的提问来源于stack exchange,提问作者ashu
相关产品推荐
相关产品推荐

