/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