/companyId/userId/ticket
we're providing ticket body containing assigneeId:
{ "assigneeId": 10 }
we need to validate that assigneeId belongs to company in URL - companyId path variable
@RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
public void createTicket(@Valid @RequestBody Ticket newTicket, @PathVariable Long companyId, @PathVariable Long userId) {
  ...
}
TicketValidator) (even with dependencies) and validate Ticket instancecompanyId to this validator though! We need to verify that ticket.assigneeId belongs to company with companyId.Any ideas how do I achieve the desired output here?
If we assume that our custom validator knows desired property name, then we can do something like this:
Approach one:
1) We can move this getting path variables logic to some kind of a base validator:
public abstract class BaseValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz)
    {
        // supports logic
    }
    @Override
    public void validate(Object target, Errors errors)
    {
        // some base validation logic or empty if there isn't any
    }
    protected String getPathVariable(String name) {
        // Getting current request (Can be autowired - depends on your implementation)
        HttpServletRequest req = HttpServletRequest((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (req != null) {
            // getting variables map from current request
            Map<String, String> variables = req.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            return variables.get(name);
        }
        return null;
    }
}
2) Extend it with your TicketValidator implementation:
public class TicketValidator extends BaseValidator {
    @Override
    public void validate(Object target, Errors errors)
    {
        // Getting our companyId var
        String companyId = getPathVariable("companyId");
        ...
        // proceed with your validation logic. Note, that all path variables
        // is `String`, so you're going to have to cast them (you can do 
        // this in `BaseValidator` though, by passing `Class` to which you 
        // want to cast it as a method param). You can also get `null` from 
        // `getPathVariable` method - you might want to handle it too somehow
    }
}
Approach two:
I think it worth to mention that you can use @PreAuthorize annotation with SpEL to do this kind of validation (You can pass path variables and request body to it). You'll be getting HTTP 403 code though if validation woudnt pass, so I guess it's not exaclty what you want. 
You could always do this:
@Controller
public class MyController {
    @Autowired
    private TicketValidator ticketValidator;
    @RequestMapping(value="/{companyId}/{userId}/ticket", method=POST)
    public void createTicket(@RequestBody Ticket newTicket,
            @PathVariable Long companyId, @PathVariable Long userId) {
        ticketValidator.validate(newTicket, companyId, userId);
        // do whatever
    }
}
Edit in response to the comment:
It doesn't make sense to validate Ticket independently of companyId when the validity of Ticket depends on companyId.
If you cannot use the solution above, consider grouping Ticket with companyId in a DTO, and changing the mapping like this:
@Controller
public class MyController {
    @RequestMapping(value="/{userId}/ticket", method=POST)
    public void createTicket(@Valid @RequestBody TicketDTO ticketDto,
            @PathVariable Long userId) {
        // do whatever
    }
}
public class TicketDTO {
    private Ticket ticket;
    private Long companyId;
    // setters & getters
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With