#How to apply request body validation with error details?

55 messages · Page 1 of 1 (latest)

maiden haven
#

Hello there,
I want to apply request body validation to my Spring project

import jakarta.validation.constraints.*;

data class RequestBodyDTO(
    @NotBlank(message = "ID is required.")
    val iD: String
)

and this endpoint

import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController()
@RequestMapping("/foo")
class Handler {
    @PostMapping
    fun handle(@Valid @RequestBody requestBody: RequestBodyDTO): ResponseEntity<Unit> {
        return ResponseEntity.ok().build()
    }
}

I thought @Valid would handle it for me but actually the API consumer still gets a 400 without any error details. So the consumer doesn't know what's wrong.

After googling around I think Spring doesn't do that for me out of the box. So I created an additional class

import org.springframework.http.HttpStatus
import org.springframework.validation.FieldError
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.*

@ControllerAdvice
class ValidationExceptionHandler {
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun handleValidationException(
        exception: MethodArgumentNotValidException
    ): Map<String, String?> {
        return exception.bindingResult.allErrors.associate { error ->
            val fieldName = (error as FieldError).field
            val errorMessage = error.defaultMessage

            fieldName to errorMessage
        }
    }
}

Unfortunately the result is the same, the API consumer won't get any error details. Is something missing? Do I even need the ValidationExceptionHandler ? How do I achieve this?

Thanks! 🙂

honest creekBOT
#

⌛ This post has been reserved for your question.

Hey @maiden haven! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically marked as dormant after 300 minutes of inactivity.

TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.

dapper geyser
#

you can create a class that sums up all the validation errors and return it to the consumer

#

so you get something like
"error_message": "ID is required."

#

which is basically something that ur trying to do

#

also try using @RestControllerAdvice

maiden haven
dapper geyser
# maiden haven would you mind telling me how to do this? `ValidationExceptionHandler` is wrong?

In this tutorial, we’re gonna look at an Spring Boot example that uses @RestControllerAdvice for exception handling in Restful API. I also show you the comparison between @RestControllerAdvice and @ControllerAdvice along with the use of @ExceptionHandler annotation. Related Posts: – @RestController vs @Controller – Spring Boot, Spring Data JPA –...

#

and no its not wrong

#
 @ExceptionHandler(value = {ResourceNotFoundException.class, CertainException.class})
  @ResponseStatus(value = HttpStatus.NOT_FOUND)
  public ErrorMessage resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ErrorMessage message = new ErrorMessage(
        status,
        date,
        ex.getMessage(),
        description);
    
    return message;
  }
#

from that site

#

and that class error message will be returned to the client

#

but yea first swap to rest controller advice

maiden haven
#

@dapper geyser thank you. I tried to follow the blog post and just changed from @ControllerAdvice to @RestControllerAdvice since ErrorMessage is just a custom type, I want to make use of problem details later on

Still, doesn't work 😦

honest creekBOT
dapper geyser
#

yes you need to create a custom class

#

and it will return that class when an error happens

maiden haven
# dapper geyser and it will return that class when an error happens

you mean like so?

import org.springframework.http.HttpStatus
import org.springframework.validation.FieldError
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestControllerAdvice

@RestControllerAdvice
class ValidationExceptionHandler {
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun handleValidationException(
        exception: MethodArgumentNotValidException
    ): ValidationErrorResponse {
        val fieldErrors = exception.bindingResult.allErrors.associate { error ->
            val fieldName = (error as FieldError).field
            val errorMessage = error.defaultMessage

            fieldName to errorMessage
        }

        return ValidationErrorResponse(fieldErrors)
    }
}

data class ValidationErrorResponse(val fieldErrors: Map<String, String?>)

That still doesn't work 😦

dapper geyser
#

so what happens

#

when the id is invalid

maiden haven
#

nothing, when I set a breakpoint, it won't run the function

dapper geyser
#

but what happens when the id is invalid

#

does it return the full stacktrace

maiden haven
#

you only get a 400 with

{
    "timestamp": "2025-01-12T18:11:01.102+00:00",
    "path": "/foo",
    "status": 400,
    "error": "Bad Request",
    "requestId": "2e927c69-2"
}
dapper geyser
#

and you want the

#

"ID is required"

#

right

maiden haven
#

yes please

dapper geyser
#
exception.getFieldErrors().forEach(fieldError ->
                validationError.addViolation(fieldError.getField(), fieldError.getDefaultMessage()));
#

you can do something like this

#

basically

#

in that method argument not valid exception

#

you run the get field errors

#

and just add it to a lsit

maiden haven
#

sorry, the function handleValidationException never runs

#

if /foo is valid, it executes the rest handler, if it's invalid then Spring responds with a 400, so handleValidationException never runs

dapper geyser
#

change the response status to maybe

#

not found

maiden haven
#

yes, the default Spring behavior for invalid request bodies I guess

dapper geyser
#

and see if that changes anything

#
 @ResponseStatus(HttpStatus.NOT_FOUND)
#

set to this

#

and see if it changes the error code

#

should be 404 instead of 400

maiden haven
#

no, same for INTERNAL_SERVER_ERROR

Is there some Spring magic required? Do I have to place this exception handler file into a specific directory with a conventional name or so...

dapper geyser
#

hmmmmmmmm

#
@RestControllerAdvice
public class CustomExceptionHandler {
    @ExceptionHandler({yourerrorhere.class})
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorHandler handleNotFoundExceptions(RuntimeException exception) {
        return new ErrorHandler(exception.getMessage());
    }

@Getter
@RequiredArgsConstructor
public class ErrorHandler {
    private final String message;
}


#

can you try doing something like this

#

errorhandler is a separate class

#

also make sure you are using the correct notblank annotation

#

from jakarta.validation.constraints

maiden haven
dapper geyser
#

ok so try with the example i sent