binding.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package binding
  2. import (
  3. "git.sxidc.com/go-framework/baize/api"
  4. "git.sxidc.com/go-framework/baize/domain"
  5. "git.sxidc.com/go-tools/utils/strutils"
  6. "git.sxidc.com/service-supports/fserr"
  7. "net/http"
  8. "strings"
  9. )
  10. func PostBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) {
  11. item.bind(router, http.MethodPost, middlewares...)
  12. }
  13. func DeleteBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) {
  14. item.bind(router, http.MethodDelete, middlewares...)
  15. }
  16. func PutBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) {
  17. item.bind(router, http.MethodPut, middlewares...)
  18. }
  19. func GetBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) {
  20. item.bind(router, http.MethodGet, middlewares...)
  21. }
  22. func Bind[O any](router api.Router, item *BindItem[O], middlewares ...api.Handler) {
  23. item.bind(router, middlewares...)
  24. }
  25. func Static(router api.Router, item *StaticBindItem) {
  26. item.bind(router)
  27. }
  28. func StaticFile(router api.Router, item *StaticFileBindItem) {
  29. item.bind(router)
  30. }
  31. type DTOBindFunc func(c *api.Context, dto DTO) error
  32. type FormDomainObjectsFunc func(c *api.Context, dto DTO) ([]domain.Object, error)
  33. type ServiceFunc[O any] func(c *api.Context, dto DTO, objects []domain.Object) (O, error)
  34. type ResponseFunc[O any] func(c *api.Context, statusCode int, data O, err error)
  35. // SimpleBindItem 路由条目
  36. type SimpleBindItem[O any] struct {
  37. // URL相对路径
  38. Path string
  39. // 使用的dto,非必传,当dto为nil时,说明该接口没有参数
  40. DTO DTO
  41. // 可选的dto绑定函数
  42. // 非必传,POST和PUT请求默认为JsonBody,DELETE默认为PathParams,GET默认为QueryParams
  43. // 额外还提供了一些转化函数:
  44. // MultipartForm: 绑定multipart form
  45. // FormBody: 绑定form body
  46. // XMLBody: 绑定XML body
  47. DTOBindFunc DTOBindFunc
  48. // 通过DTO构造使用的领域对象,之后在ServiceFunc中会按照构造实体的顺序进行回调
  49. // 非必传,如果为nil,则说明没有领域对象
  50. // 与Objects字段二选一使用,如果都指定,会按照该字段处理
  51. FormDomainObjectsFunc FormDomainObjectsFunc
  52. // 使用的领域对象,当使用Tag对实体进行标注后,可以直接通过该字段给定实体,之后在ServiceFunc中会按照给定实体的顺序进行回调
  53. // 非必传,如果为nil或长度为0,则说明没有领域对象
  54. // 与FormObjectsFunc字段二选一使用,如果都指定,会按照FormObjectsFunc字段处理
  55. Objects []domain.Object
  56. // 应用服务泛型函数
  57. ServiceFunc ServiceFunc[O]
  58. // 响应泛型函数,如果不响应,需要使用NoResponse零值占位
  59. ResponseFunc ResponseFunc[O]
  60. }
  61. func (item *SimpleBindItem[O]) bind(router api.Router, method string, middlewares ...api.Handler) {
  62. bindingItem := &BindItem[O]{
  63. Method: method,
  64. SimpleBindItem: item,
  65. }
  66. bindingItem.bind(router, middlewares...)
  67. }
  68. // BindItem 路由条目结构
  69. type BindItem[O any] struct {
  70. Method string
  71. *SimpleBindItem[O]
  72. }
  73. func (item *BindItem[O]) bind(router api.Router, middlewares ...api.Handler) {
  74. if strutils.IsStringEmpty(item.Path) {
  75. panic("需要指定路径")
  76. }
  77. if strutils.IsStringEmpty(item.Method) {
  78. panic("需要指定方法")
  79. }
  80. if item.ResponseFunc == nil {
  81. panic("需要指定响应函数")
  82. }
  83. if item.ServiceFunc == nil {
  84. panic("需要指定应用服务函数")
  85. }
  86. // 给单个路由增加中间件
  87. handlers := []api.Handler{
  88. func(c *api.Context) {
  89. dto := item.DTO
  90. // 有请求数据
  91. if dto != nil {
  92. // 将请求数据解析到dto中
  93. if item.DTOBindFunc != nil {
  94. err := item.DTOBindFunc(c, dto)
  95. if err != nil {
  96. var outputZero O
  97. item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
  98. return
  99. }
  100. } else {
  101. switch item.Method {
  102. case http.MethodPost:
  103. fallthrough
  104. case http.MethodPut:
  105. err := JsonBody(c, dto)
  106. if err != nil {
  107. var outputZero O
  108. item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
  109. return
  110. }
  111. case http.MethodGet:
  112. err := QueryParams(c, dto)
  113. if err != nil {
  114. var outputZero O
  115. item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
  116. return
  117. }
  118. case http.MethodDelete:
  119. err := PathParams(c, dto)
  120. if err != nil {
  121. var outputZero O
  122. item.ResponseFunc(c, http.StatusBadRequest, outputZero, err)
  123. return
  124. }
  125. }
  126. }
  127. }
  128. // 进行领域对象转化
  129. var domainObjects []domain.Object
  130. if item.FormDomainObjectsFunc != nil {
  131. innerDomainObjects, err := item.FormDomainObjectsFunc(c, dto)
  132. if err != nil {
  133. var outputZero O
  134. item.ResponseFunc(c, http.StatusOK, outputZero, err)
  135. return
  136. }
  137. domainObjects = innerDomainObjects
  138. } else {
  139. // TODO Tag处理
  140. }
  141. // 执行业务函数
  142. statusCode := http.StatusOK
  143. outputModel, err := item.ServiceFunc(c, dto, domainObjects)
  144. if err != nil {
  145. statusCode = fserr.ParseCode(err).HttpCode
  146. }
  147. item.ResponseFunc(c, statusCode, outputModel, err)
  148. return
  149. },
  150. }
  151. handlers = append(handlers, middlewares...)
  152. // 所有的函数加入到执行链中
  153. router.AddRoute(item.Method, item.Path, handlers...)
  154. }
  155. // StaticBindItem 静态路由item
  156. type StaticBindItem struct {
  157. RelativePath string
  158. Root string
  159. WithBasePath bool
  160. }
  161. func (item *StaticBindItem) bind(router api.Router) {
  162. if item.WithBasePath {
  163. router.Static(strings.TrimPrefix(item.RelativePath, router.BasePath()), item.Root)
  164. } else {
  165. router.Static(item.RelativePath, item.Root)
  166. }
  167. }
  168. type StaticFileBindItem struct {
  169. RelativePath string
  170. FilePath string
  171. WithBasePath bool
  172. }
  173. func (item *StaticFileBindItem) bind(router api.Router) {
  174. if item.WithBasePath {
  175. router.StaticFile(strings.TrimPrefix(item.RelativePath, router.BasePath()), item.FilePath)
  176. } else {
  177. router.StaticFile(item.RelativePath, item.FilePath)
  178. }
  179. }