Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use problem-spring-web only for @RestController classes, but retain regular handling for fullstack (Controller + Thymeleaf + databinding) #701

Open
jackdpeterson opened this issue Oct 27, 2021 · 2 comments
Labels

Comments

@jackdpeterson
Copy link

jackdpeterson commented Oct 27, 2021

This may be a question that others have answered previously; however, I haven't found a good resource on this topic.

Context:
I have an existing REST controller based microservice where I need to add a few fullstack pages to. I've attempted adding,

@ControllerAdvice(assignableTypes = RestController.class)
public final class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {

in order to constrain the exception handling to just my REST controllers; however, that didn't quite do the trick -- I got the usual whitelabel error page instead. I'm sure I'm missing some additional detail to fallback to the regular spring error handling so thymeleaf-based forms can receive the usual validation errors when say a ConstraintViolation is encountered.

Are there some documentation references that would be of use in this blended REST + Fullstack situation?

Thanks!

@crizzis
Copy link

crizzis commented Feb 10, 2022

Note sure if I follow, but... RestController is an annotation, which is why assignableTypes won't work.

Why not simply make the REST controllers implement a marker interface in addition to annotating them with @RestController? I believe assignableTypes would work with an interface.

This is a Spring question, BTW, and not directly related to problem-spring-web

@patrickpilch
Copy link

I've been struggling with this as well. Unfortunately I can't think of a way to make assignableTypes or even something like @ControllerAdvice(annotations = [RestController::class]) work with the library's recommended approach, due to Spring Internals.

The problem seems to boils down to all exception resolution getting lumped together in the global HandlerExceptionResolverComposite bean, and an AuthenticationException not having an associated handler or controller. Assuming HttpSecurity's authenticationEntryPoint is set to SecurityProblemSupport, it calls AuthenticationEntryPoint#commence which delegates to the global composite resolver HandlerExceptionResolver#resolveException with a null handler parameter.
Eventually the exception resolver iterates over all of its exception handling advices, but the problem-spring-web advice bean will never satisfy the condition due to it's HandlerTypePredicate only knowing null as the controllerType, but also having a restriction/selector to operate only on handlers annotated with RestController in my example.

Here's a similar problem presented on SO: https://stackoverflow.com/q/47679897

In the end, I've burned enough time on it to settle on what feels hacky, but lets me accomplish my goal of only having RestControllers return problem responses. I've stopped using SecurityProblemSupport in favor of a custom implementation:

@ControllerAdvice(annotations = [RestController::class])
class TheAdvice : ProblemHandling, SecurityAdviceTrait

@Component
class ApiSecurityExceptionHandler(
    private val objectMapper: ObjectMapper
) : AuthenticationEntryPoint, AccessDeniedHandler, SecurityAdviceTrait {
    
    override fun commence(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authException: AuthenticationException
    )  = writeProblemResponse(response, handleAuthentication(authException, ServletWebRequest(request)))

    override fun handle(
        request: HttpServletRequest,
        response: HttpServletResponse,
        accessDeniedException: AuthenticationException
    ) = writeProblemResponse(response, handleAccessDenied(accessDeniedException, ServletWebRequest(request)))

    private fun writeProblemResponse(response: HttpServletResponse, problem: ResponseEntity<Problem>) {
        response.status = problem.statusCodeValue
        response.contentType = MediaType.APPLICATION_JSON.toString()
        objectMapper.writeValue(response.writer, problem.body)
    }
}

@MALPI MALPI changed the title How to use problem-spring-web only for @RestController classes, but retain regular handling for fullstack (Controller + Thymelead + databinding) How to use problem-spring-web only for @RestController classes, but retain regular handling for fullstack (Controller + Thymeleaf + databinding) Jan 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants