맞춤 유효성 검사 오류

20379 단어 gogin
너무 짧은 암호를 사용하여 새 계정을 만들려고 하면 오류Key: 'User.Password' Error:Field validation for 'Password' failed on the 'min' tag가 발생합니다. 이는 실제로 사용자에게 친숙하지 않으므로 더 나은 사용자 경험을 위해 변경해야 합니다. 이를 사용자 지정 오류 메시지로 변환하는 방법을 살펴보겠습니다. 이를 위해 internal/server/middleware.go 파일에 새로운 Gin 처리기 함수를 생성합니다.

func customErrors(ctx *gin.Context) {
  ctx.Next()
  if len(ctx.Errors) > 0 {
    for _, err := range ctx.Errors {
      // Check error type
      switch err.Type {
      case gin.ErrorTypePublic:
        // Show public errors only if nothing has been written yet
        if !ctx.Writer.Written() {
          ctx.AbortWithStatusJSON(ctx.Writer.Status(), gin.H{"error": err.Error()})
        }
      case gin.ErrorTypeBind:
        errMap := make(map[string]string)
        if errs, ok := err.Err.(validator.ValidationErrors); ok {
          for _, fieldErr := range []validator.FieldError(errs) {
            errMap[fieldErr.Field()] = customValidationError(fieldErr)
          }
        }

        status := http.StatusBadRequest
        // Preserve current status
        if ctx.Writer.Status() != http.StatusOK {
          status = ctx.Writer.Status()
        }
        ctx.AbortWithStatusJSON(status, gin.H{"error": errMap})
      default:
        // Log other errors
        log.Error().Err(err.Err).Msg("Other error")
      }
    }

    // If there was no public or bind error, display default 500 message
    if !ctx.Writer.Written() {
      ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": InternalServerError})
    }
  }
}

func customValidationError(err validator.FieldError) string {
  switch err.Tag() {
  case "required":
    return fmt.Sprintf("%s is required.", err.Field())
  case "min":
    return fmt.Sprintf("%s must be longer than or equal %s characters.", err.Field(), err.Param())
  case "max":
    return fmt.Sprintf("%s cannot be longer than %s characters.", err.Field(), err.Param())
  default:
    return err.Error()
  }
}


상수InternalServerErrorinternal/server/server.go에서 정의됩니다.

const InternalServerError = "Something went wrong!"

internal/server/router.go에서 새로운 Gin 미들웨어를 사용해 보겠습니다.

func setRouter() *gin.Engine {
  // Creates default gin router with Logger and Recovery middleware already attached
  router := gin.Default()

  // Enables automatic redirection if the current route can't be matched but a
  // handler for the path with (without) the trailing slash exists.
  router.RedirectTrailingSlash = true

  // Create API route group
  api := router.Group("/api")
  api.Use(customErrors)
  {
    api.POST("/signup", gin.Bind(store.User{}), signUp)
    api.POST("/signin", gin.Bind(store.User{}), signIn)
  }

  authorized := api.Group("/")
  authorized.Use(authorization)
  {
    authorized.GET("/posts", indexPosts)
    authorized.POST("/posts", createPost)
    authorized.PUT("/posts", updatePost)
    authorized.DELETE("/posts/:id", deletePost)
  }

  router.NoRoute(func(ctx *gin.Context) { ctx.JSON(http.StatusNotFound, gin.H{}) })

  return router
}


보시다시피 현재 customErrors 그룹에서 api 미들웨어를 사용하고 있습니다. 그러나 그것이 유일한 변화는 아닙니다. 로그인 및 등록을 위한 업데이트된 경로 알림:

api.POST("/signup", gin.Bind(store.User{}), signUp)
api.POST("/signin", gin.Bind(store.User{}), signIn)


이러한 변경 사항을 통해 우리는 signUp 및 signIn 핸들러를 누르기 전에 요청 데이터를 바인딩하려고 합니다. 즉, 양식 유효성 검사가 통과된 경우에만 핸들러에 도달하게 됩니다. 이와 같은 설정을 사용하면 핸들러에 도달하면 아무 것도 없었기 때문에 핸들러는 바인딩 오류에 대해 생각할 필요가 없습니다. 이를 염두에 두고 다음 두 핸들러를 업데이트해 보겠습니다.

func signUp(ctx *gin.Context) {
  user := ctx.MustGet(gin.BindKey).(*store.User)
  if err := store.AddUser(user); err != nil {
    ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
  }
  ctx.JSON(http.StatusOK, gin.H{
    "msg": "Signed up successfully.",
    "jwt": generateJWT(user),
  })
}

func signIn(ctx *gin.Context) {
  user := ctx.MustGet(gin.BindKey).(*store.User)
  user, err := store.Authenticate(user.Username, user.Password)
  if err != nil {
    ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Sign in failed."})
    return
  }

  ctx.JSON(http.StatusOK, gin.H{
    "msg": "Signed in successfully.",
    "jwt": generateJWT(user),
  })
}


핸들러는 이제 훨씬 더 간단해졌으며 데이터베이스 오류만 처리합니다. 너무 짧은 사용자 이름과 암호로 다시 계정을 만들려고 시도하면 더 읽기 쉽고 설명적인 오류가 표시됩니다.



이제 Post 핸들러로 동일한 작업을 수행할 수 있습니다. 여기에서 솔루션을 보여드리지 않으므로 연습을 위해 직접 시도해 볼 수 있지만 RGB GitHub repo에서 찾을 수 있습니다.

좋은 웹페이지 즐겨찾기