修改 REST API 响应以仅包含查询参数中请求的字段
Posted
技术标签:
【中文标题】修改 REST API 响应以仅包含查询参数中请求的字段【英文标题】:Modify REST API response to only include fields requested in query parameter 【发布时间】:2020-03-06 04:30:40 【问题描述】:我想发送仅包含 GET api 请求字段的响应。减少响应的总大小。
例如, 默认行为,何时请求特定字段
GET /v1/users/
"data" :
[
"name" : "User1",
"phone" : "800-999-9999",
"city" : "XYZ1",
"country" : "PQR1"
,
"name" : "User2",
"phone" : "800-999-9999",
"city" : "XYZ2",
"country" : "PQR2"
]
用例,需要的字段作为查询参数传递
GET /v1/users/?fields=name,city
"data" :
[
"name" : "User1",
"city" : "XYZ1"
,
"name" : "User2",
"city" : "XYZ2"
]
遇到了“https://github.com/monitorjbl/json-view”。却被小队击落。
如何使用 Spring Boot 实现此功能?使用 java 微服务的组织如何实现此功能?谢谢!
【问题讨论】:
【参考方案1】:方法一:对象转Map,去掉多余key,Map转Json
@GetMapping("/v1/users")
@ResponseBody
public List<Map> getUsers(@RequestParam(required = false) Set<String> fields)
List<User> users = ...;
ObjectMapper oMapper = new ObjectMapper();
return users.stream().map(u -> oMapper.convertValue(u, Map.class))
.peek(map ->
if (fields != null) map.keySet().retainAll(fields)
)
.collect(Collectors.toList());
方法二:使用Gson
@GetMapping("/v1/users")
@ResponseBody
public String getUsers(@RequestParam(required = false) Set<String> fields)
List<User> users = ...;
Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy()
@Override
public boolean shouldSkipField(FieldAttributes f)
return f.getDeclaringClass().equals(User.class)
&& fields != null
&& !fields.contains(f.getFieldName())
;
@Override
public boolean shouldSkipClass(Class<?> clazz)
return false;
).create();
return gson.toJson(users);
遇到了“https://github.com/monitorjbl/json-view”。却被小队击落。
顺便说一句,Jackson 支持 @JsonView
注释,但它不允许根据需要动态包含/排除字段。您可以定义字段子集,然后返回这些子集
public class Views
public static class Short
public class User
@JsonView(Views.Short.class)
public String city;
@JsonView(Views.Short.class)
public String name;
public String phone;
public String country;
@GetMapping("/v1/users")
@ResponseBody
public String getUsers(@RequestParam(required = false) boolean shortView)
List<User> users = ...;
ObjectMapper mapper = new ObjectMapper();
if (shortView) mapper = mapper.writerWithView(Views.Short.class);
return mapper.writeValueAsString(users);
【讨论】:
【参考方案2】:这是另一种方法:
假设 1:我认为将控制器的返回类型定义为域对象并让 spring 单独处理将其转换为 JSON 会更好(与 @Alexander Pavlov 的回答不同)。 IMO 对于一般代码的可读性和像 swagger 这样的工具来说更好 内省控制器,应该知道应该返回什么。
假设 2:您的 User 类中没有原语。
好的,现在控制器显然调用了一些“服务”来对数据库进行一些查询,或者通常执行一些最终生成用户列表(您返回给客户端的列表)的逻辑。返回对象应该是这样的:
class Users
private List<User> data;
class User // + constructors, getters, setters, etc.
private String name;
private String city;
private String phone;
private String country;
在这种情况下,您可以在服务中将“null”放在您不想返回的字段中:
User user = new User("John", "LA", null, null); // you only have name and city
现在您可以将注释 @JsonInclude(Include.NON_NULL)
放在类 User
上,这将指示 Jackson 根本不将值为“null”的字段包含到序列化响应中。
如果您希望全局应用此行为,可以配置ObjectMapper
bean:
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
【讨论】:
+ 假设 3:域模型允许空值。有时强制的不可为空性(例如对于集合)通过简化对此类对象的处理来支付自己的费用。仅允许空值用于自定义序列化可能是不好的权衡。以上是关于修改 REST API 响应以仅包含查询参数中请求的字段的主要内容,如果未能解决你的问题,请参考以下文章
Salesforce REST API 如何避免在查询参数中泄露敏感数据
REST API 最佳实践:查询字符串中的参数与请求正文中的参数
Elasticsearch Java Rest Client API