修改 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

如何使用 django rest 框架从 GET 请求的查询参数中过滤多个 id?

响应中的 Spring rest api 过滤器字段

为 Zapier ForPublic API 编写 REST API 查询