Go语言高效率Web开发二:如何优雅的对外隐藏数据字段
需求场景:
文章列表接口: /articles 接口所需字段:文章id、文章标题、文章图片
文章详情接口: /articles/id 接口所需字段:文章id、文章标题、文章图片、文章详情 代码实现:
技术选型:模型转换函数使用copier
package domain
type Article struct {
ID int
Title string
ImageUrl string
Content string
}
---------------------------------------------------------------------------
package response
type Article struct {
ID int `json:"id"`
Title string `json:"title"`
ImageURL string `json:"imageURL"`
Content string `json:"content"`
}
---------------------------------------------------------------------------
package controller
type Article struct {}
func (ctr *Article) List(c *gin.Context) {
articles := []*domain.Article{
{ID: 1, Title: "标题1", Content: "内容1", ImageURL: "1.jpg", Author: &domain.Author{ID: 1, Name: "作者1", Desc: "介绍1"}},
{ID: 2, Title: "标题2", Content: "内容2", ImageURL: "2.jpg", Author: &domain.Author{ID: 2, Name: "作者2", Desc: "介绍2"}},
}
var response []*response.ArticleList
copier.Copy(&response, articles)
c.JSON(http.StatusOK, gin.H{"data": response})
}
func (ctr *Article) Detail(c *gin.Context) {
article := &domain.Article{ID: 1, Title: "标题1", Content: "内容1", ImageURL: "1.jpg"}
var response response.ArticleDetail
copier.Copy(&response, article)
c.JSON(http.StatusOK, gin.H{"data": response})
}
以上代码实现方式会导致文章列表接口的文章详情也会对外展示。
在项目开发中,你一定遇到这种场景,同样的模型这个接口需要全部数据字段,另外的接口只需要部分数据字段,还有的场景有些数据字段比较敏感,只能对某些接口展示;比较暴力的方式就是每一个接口建立对应的response模型,这种方式在Java开发中非常常见,那么在Go语言开发中有没有更好的解决方式呢?
网上有很多文章介绍有多种方式如何隐藏字段,如下:
方式一:
type ArticleList struct {
Article
Content string `json:"-"`
}
返回结果如下,content字段只是为空,不隐藏
{
"data": [
{
"id": 1,
"title": "标题1",
"imageUrl": "1.jpg",
"content": ""
},
{
"id": 2,
"title": "标题2",
"imageUrl": "2.jpg",
"content": ""
}
]
}
方式二:
type Omit *struct{}
type ArticleList struct {
Article
Content Omit `json:"content,omitempty"`
}
返回结果如下,content字段变成了json对象,不隐藏,omitempty零值隐藏
{
"data": [
{
"id": 1,
"title": "标题1",
"imageUrl": "1.jpg",
"content": {}
},
{
"id": 2,
"title": "标题2",
"imageUrl": "2.jpg",
"content": {}
}
]
}
解决这个问题也经历了很长时间,刚开始也是模仿java的方式,每一个接口建立对应的response模型,后来无意间发现了比较简单的方法可以优雅的隐藏字段,代码如下:
type ArticleList struct {
Article
Content string `json:"content,omitempty" copier:"-"`
}
接口articles: 对应响应结构体ArticleList
接口articles/:id: 对应响应结构体ArticleDetail
实现原理:copier模型转换函数会忽略标签"-"的字段,导致目标字段为零值,再通过omitempty零值隐藏,从而实现对字段隐藏。
如何实现复合结构体的字段隐藏,代码如下:
package domain
type Article struct {
ID int
Title string
ImageUrl string
Content string
Author *Author
}
type Author struct {
ID int
Name string
Desc string
}
-----------------------------------------------------------------------------
package response
type Author struct {
ID int `json:"id"`
Name string `json:"name"`
Desc string `json:"desc"`
}
type ArticleListAuthor struct {
Author
Desc string `json:"desc,omitempty" copier:"-"`
}
type Article struct {
ID int `json:"id"`
Title string `json:"title"`
ImageUrl string `json:"imageUrl"`
Content string `json:"content"`
}
type ArticleList struct {
Article
Content string `json:"content,omitempty" copier:"-"`
ArticleListAuthor `json:"author"`
}
项目开发过程中,不需要这么死板,如果一个模型有很多属性,只需要对外展示两三个字段,用上述方式需要隐藏大多数属性,还不如直接建立一个新的模型,只包含所需要对外展示的少数字段,灵活度大家自己把握。
封装response层模型转换
// domain层Article模型 => response层ArticleList模型数组
var response []*response.ArticleList
copier.Copy(&response, articles)
// domain层Article模型 => response层ArticleDetail模型
var response response.ArticleDetail
copier.Copy(&response, article)
模型转换过程放在controller感觉特别别扭,由response层模型负责转换自己比较合适,代码如下:
package response
type ArticleLists []*ArticleList
func (resp *ArticleLists) Map(articles []*domain.Article) *ArticleLists {
mapper.Map(resp, articles)
return resp
}
type ArticleDetail struct {
Article
}
func (resp *ArticleDetail) Map(article *domain.Article) *ArticleDetail {
mapper.Map(resp, article)
return resp
}
Slice对应不同的Json类型
var response = new(response.ArticleLists)
c.JSON(http.StatusOK, gin.H{"data": response.Map(articles)})
如果articles slice为空slice
返回json:
{
"data": null
}
response := response.ArticleLists{}
c.JSON(http.StatusOK, gin.H{"data": response.Map(articles)})
如果articles slice为空slice
返回json:
{
"data": []
}
代码分析:
var response = new(response.ArticleLists) 实例了一个空指针,所以为null
response := response.ArticleLists{} 实例了一个空slice,所以为[]
开发中,前端对于数组类型很少判断null,所以请使用第二种方式。
转载自:https://juejin.cn/post/7041544953394102285