在api设计中,直接返回混合类型或非类型化的列表(如`list
在构建RESTful API时,清晰、一致且易于理解的数据契约至关重要。API的响应结构是其与消费者之间的“合同”,明确了数据类型、字段及其含义。然而,一种常见的误区是直接返回一个包含多种类型元素的非类型化列表,例如List
考虑一个API,最初设计为返回一系列Rating对象:
public class Rating {
private Long movieId;
private Integer rating;
public Rating(Long movieId, Integer rating) {
this.movieId = movieId;
this.rating = rating;
}
// Getters and Setters
public Long getMovieId() { return movieId; }
public void setMovieId(Long movieId) { this.movieId = movieId; }
public Integer getRating() { return rating; }
public void setRating(Integer rating) { this.rating = rating; }
}其响应可能如下:
[{"movieId":5870,"rating":5},{"movieId":1234,"rating":3}]现在,假设我们需要对API进行增强,例如,添加一个字段来指示这些Rating属于谁(例如,“John Doe”)。一种直观但错误的做法可能是尝试将这个额外的信息直接添加到现有列表中,并将返回类型更改为List
@GetMapping("/problematic-ratings")
public List此时的API响应可能变为:
[{"movieId":5870,"rating":5},{"movieId":1234,"rating":3},"John Doe"]这种将不同类型数据混合在一个List
对于API消费者而言,处理List
解决上述问题的最佳实践是,将API响应数据封装在一个专门的数据传输对象(DTO - Data Transfer Object)中。这个DTO应该清晰地定义所有相关数据及其类型。
针对上述示例,我们可以设计一个RatedActor DTO:
public class RatedActor {
private String name;
private List ratings;
public RatedActor(String name, List ratings) {
this.name = name;
this.ratings = ratings;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List getRatings() { return ratings; }
public void setRatings(List ratings) { this.ratings = ratings; }
} 然后,API可以返回这个结构化的RatedActor对象:
@GetMapping("/structured-ratings")
public RatedActor getStructuredRatings() {
List ratings = new ArrayList<>();
ratings.add(new Rating(5870L, 5));
ratings.add(new Rating(1234L, 3));
return new RatedActor("John Doe", ratings);
} 此时的API响应将是清晰、结构化的JSON对象:
{
"name": "John Doe",
"ratings": [
{"movieId":5870,"rating":5},
{"movieId":1234,"rating":3}
]
}通过使用DTO封装API响应,我们获得了多方面的优势:
在API设计中,避免直接返回非类型化的List