jnemecz
jnemecz

Reputation: 3608

Create own class that transforms HTTP request to object in Spring?

I would like to create own class that will transform HTTP request and initializes object from this HTTP request in my Spring MVC application. I can create object by defining parameters in method but I need to do mapping in my own way and do it manually.

How can I do it with my own implementation that will pass to Spring and it will use it seamlessly?

Update1

Solution that kindly provided Bohuslav Burghardt doesn't work:

HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalStateException: An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the @RequestBody or the @RequestPart arguments to which they apply: public java.lang.String cz.deriva.derivis.api.oauth2.provider.controllers.OAuthController.authorize(api.oauth2.provider.domain.AuthorizationRequest,org.springframework.ui.Model,org.springframework.validation.BindingResult,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)

Maybe I should mention that I use own validator:

public class RequestValidator {

    public boolean supports(Class clazz) {
        return AuthorizationRequest.class.equals(clazz);
    }

    public void validate(Object obj, Errors e) {
        AuthorizationRequest request = (AuthorizationRequest) obj;
        if ("foobar".equals(request.getClientId())) {
            e.reject("clientId", "nomatch");
        }
    }

}

and declaration of my method in controller (please not there is needed a validation - @Valid):

@RequestMapping(value = "/authorize", method = {RequestMethod.GET, RequestMethod.POST})
    public String authorize(
            @Valid AuthorizationRequest authorizationRequest,
            BindingResult result
    ) {

}

I have two configurations classes in my application.

@Configuration
@EnableAutoConfiguration
@EnableWebMvc
@PropertySource("classpath:/jdbc.properties")
public class ApplicationConfig {
}

and

@Configuration
@EnableWebMvc
public class WebappConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new AuthorizationRequestArgumentResolver());
    }

}

What is wrong?

Update 2

The problem is with param BindingResult result, when I remove it it works. But I need the result to process it when some errors occur.

Upvotes: 2

Views: 1799

Answers (1)

Bohuslav Burghardt
Bohuslav Burghardt

Reputation: 34776

If I understand your requirements correctly, you could implement custom HandlerMethodArgumentResolver for that purpose. See example below for implementation details:


Model object

public class AuthorizationRequestHolder {
    @Valid
    private AuthorizationRequest authorizationRequest;
    private BindingResult bindingResult;
    // Constructors, accessors omitted
}

Resolver

public class AuthorizationRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return AuthorizationRequestHolder.class.isAssignableFrom(parameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        // Map the authorization request
        AuthorizationRequest authRequest = mapFromServletRequest(request);
        AuthorizationRequestHolder authRequestHolder = new AuthorizationRequestHolder(authRequest);
        // Validate the request
        if (parameter.hasParameterAnnotation(Valid.class)) {
            WebDataBinder binder = binderFactory.createBinder(webRequest, authRequestHolder, parameter.getParameterName());
            binder.validate();
            authRequestHolder.setBindingResult(binder.getBindingResult());
        }
        return authRequestHolder;
    }
}

Configuration

@Configuration
@EnableWebMvc
public class WebappConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new AuthorizationRequestMethodArgumentResolver());
    }
}

Usage

@RequestMapping("/auth")
public void doSomething(@Valid AuthRequestHolder authRequestHolder) {
    if (authRequestHolder.getBindingResult().hasErrors()) {
        // Process errors
    }
    AuthorizationRequest authRequest = authRequestHolder.getAuthRequest();
    // Do something with the authorization request
}

Edit: Updated answer with workaround to non-supported usage of @Valid with HandlerMethodArgumentResolver parameters.

Upvotes: 1

Related Questions