api.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package api
  2. import (
  3. "errors"
  4. "github.com/0xJacky/Nginx-UI/internal/logger"
  5. "github.com/gin-gonic/gin"
  6. "github.com/go-playground/validator/v10"
  7. "net/http"
  8. "reflect"
  9. "regexp"
  10. "strings"
  11. )
  12. func ErrHandler(c *gin.Context, err error) {
  13. logger.GetLogger().Errorln(err)
  14. c.JSON(http.StatusInternalServerError, gin.H{
  15. "message": err.Error(),
  16. })
  17. }
  18. type ValidError struct {
  19. Key string
  20. Message string
  21. }
  22. func BindAndValid(c *gin.Context, target interface{}) bool {
  23. err := c.ShouldBindJSON(target)
  24. if err != nil {
  25. logger.Error("bind err", err)
  26. var verrs validator.ValidationErrors
  27. ok := errors.As(err, &verrs)
  28. if !ok {
  29. c.JSON(http.StatusNotAcceptable, gin.H{
  30. "message": "Requested with wrong parameters",
  31. "code": http.StatusNotAcceptable,
  32. })
  33. return false
  34. }
  35. t := reflect.TypeOf(target).Elem()
  36. errorsMap := make(map[string]interface{})
  37. for _, value := range verrs {
  38. var path []string
  39. namespace := strings.Split(value.StructNamespace(), ".")
  40. // logger.Debug(t.Name(), namespace)
  41. if t.Name() != "" && len(namespace) > 1 {
  42. namespace = namespace[1:]
  43. }
  44. getJsonPath(t, namespace, &path)
  45. insertError(errorsMap, path, value.Tag())
  46. }
  47. c.JSON(http.StatusNotAcceptable, gin.H{
  48. "errors": errorsMap,
  49. "message": "Requested with wrong parameters",
  50. "code": http.StatusNotAcceptable,
  51. })
  52. return false
  53. }
  54. return true
  55. }
  56. // findField recursively finds the field in a nested struct
  57. func getJsonPath(t reflect.Type, fields []string, path *[]string) {
  58. field := fields[0]
  59. // used in case of array
  60. var index string
  61. if field[len(field)-1] == ']' {
  62. re := regexp.MustCompile(`(\w+)\[(\d+)\]`)
  63. matches := re.FindStringSubmatch(field)
  64. if len(matches) > 2 {
  65. field = matches[1]
  66. index = matches[2]
  67. }
  68. }
  69. f, ok := t.FieldByName(field)
  70. if !ok {
  71. return
  72. }
  73. *path = append(*path, f.Tag.Get("json"))
  74. if index != "" {
  75. *path = append(*path, index)
  76. }
  77. if len(fields) > 1 {
  78. subFields := fields[1:]
  79. getJsonPath(f.Type, subFields, path)
  80. }
  81. }
  82. // insertError inserts an error into the errors map
  83. func insertError(errorsMap map[string]interface{}, path []string, errorTag string) {
  84. if len(path) == 0 {
  85. return
  86. }
  87. jsonTag := path[0]
  88. if len(path) == 1 {
  89. // Last element in the path, set the error
  90. errorsMap[jsonTag] = errorTag
  91. return
  92. }
  93. // Create a new map if necessary
  94. if _, ok := errorsMap[jsonTag]; !ok {
  95. errorsMap[jsonTag] = make(map[string]interface{})
  96. }
  97. // Recursively insert into the nested map
  98. subMap, _ := errorsMap[jsonTag].(map[string]interface{})
  99. insertError(subMap, path[1:], errorTag)
  100. }