binding.go 6.6 KB

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