binding.go 7.2 KB

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