binding.go 7.1 KB

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