How to validate a Map<String, String> using Spring Validator programmatically - java

I have a Map that I receive from a browser redirection back from a third party to my Spring Controller as below -
#RequestMapping(value = "/capture", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void capture(#RequestParam
final Map<String, String> response)
{
// TODO : perform validations first.
captureResponse(response);
}
Before using this payload, I need to do non-trivial validation, involving first checking for non-null values of a map, and then using those values in a checksum validation. So, I would like to validate my payload programmatically using the Spring Validator interface. However, I could not find any validator example for validating a Map.
For validating a Java Object, I understand how a Validator is invoked by passing the object and a BeanPropertyBindingResult to contain the errors to the Validator as below -
final Errors errors = new BeanPropertyBindingResult(object, objectName);
myValidator.validate(object, errors);
if (errors.hasErrors())
{
throw new MyWebserviceValidationException(errors);
}
For a Map, I can see that there is a MapBindingResult class that extends AbstractBindingResult. Should I simply use it, and pass my map in the Object object and in the validator cast it back to a Map? Also, how would the Validator method of supports(final Class<?> clazz) be implemented in my validator? Would it simply be like below code snippet where there can only be one validator supporting this generic class of HashMap? Somehow doesn't feel right. (Although this does not matter to me as I will be injecting my validator and use it directly and not through a validator registry, but still curious.)
#Override
public boolean supports(final Class<?> clazz)
{
return HashMap.class.equals(clazz);
}
Since, there is a MapBindingResult, I'm positive that Spring must be supporting Maps for validation, would like to know how. So would like to know if this is the way to go, or am I heading in the wrong direction and there is a better way of doing this.
Please note I would like to do this programmatically and not via annotations.

Just like I thought, Spring Validator org.springframework.validation.Validator does support validation of a Map. I tried it out myself, and it works!
I created a org.springframework.validation.MapBindingResult by passing in the map I need to validate and an identifier name for that map (for global/root-level error messages). This Errors object is passed in the validator, along with the map to be validated as shown in the snippet below.
final Errors errors = new MapBindingResult(responseMap, "responseMap");
myValidator.validate(responseMap, errors);
if (errors.hasErrors())
{
throw new MyWebserviceValidationException(errors);
}
The MapBindingResult extends AbstractBindingResult and overrides the method getActualFieldValue to give it's own implementation to get field from a map being validated.
private final Map<?, ?> target;
#Override
protected Object getActualFieldValue(String field) {
return this.target.get(field);
}
So, inside the Validator I was able to use all the useful utility methods provided in org.springframework.validation.ValidationUtils just like we use in a standard object bean validator. For example -
ValidationUtils.rejectIfEmpty(errors, "checksum", "field.required");
where "checksum" is a key in my map. Ah, the beauty of inheritance! :)
For the other non-trivial validations, I simply cast the Object to Map and wrote my custom validation code.
So the validator looks something like -
#Override
public boolean supports(final Class<?> clazz)
{
return HashMap.class.equals(clazz);
}
#Override
public void validate(final Object target, final Errors errors)
{
ValidationUtils.rejectIfEmpty(errors, "transactionId", "field.required");
ValidationUtils.rejectIfEmpty(errors, "checksum", "field.required");
final Map<String, String> response = (HashMap<String, String>) target;
// do custom validations with the map's attributes
// ....
// if validation fails, reject the whole map -
errors.reject("response.map.invalid");
}

Validate the parameters inside the map
For the validation of your Map following a specific mapping you will need a custom validator.
As this may be the usecase for some, validation of #RequestParam can be done using org.springframework.validation annotations, e.g.
#GetMapping(value = "/search")
public ResponseEntity<T> search(#RequestParam
Map<#NotBlank String,#NotBlank String> searchParams,
While #NotBlank checks if your string is not "",
#NotNull can validate non-null parameters which I guess was something you needed.

An alternative is to create your custom constraint annotation for a Map.
You can take a look the following link:
https://www.baeldung.com/spring-mvc-custom-validator

Related

ConfigurationProperties for String properties with multiple dots

I'm trying to fix a bug in our company's homemade framework. Basically, we should be able to inject EclipseLink properties into the EntityManager through the following class which is part of our framework:
#ConfigurationProperties(prefix = "our.framework.eclipselink")
public class CustomEclipseLinkProperties {
private Map<String, Object> properties;
public Map<String, Object> getProperties() {
return properties;
}
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
public String getBatchSize() {
return (String) properties.get(PersistenceUnitProperties.BATCH_WRITING_SIZE);
}
}
Our service built on top of that framework has the following properties file (application.properties):
our.framework.eclipselink.properties.eclipselink.logging.level=FINEST
our.framework.eclipselink.properties.eclipselink.logging.level.cache=FINEST
our.framework.eclipselink.properties.eclipselink.logging.level.sql=FINEST
our.framework.eclipselink.properties.eclipselink.logging.parameters=true
our.framework.eclipselink.properties.eclipselink.jdbc.batch-writing.size=1000
our.framework.eclipselink.properties.eclipselink.jdbc.bind-parameters=true
our.framework.eclipselink.properties.eclipselink.jdbc.batch-writing=JDBC
our.framework.eclipselink.properties.eclipselink.jpa.uppercase-column-names=false
our.framework.eclipselink.properties.eclipselink.jpa.uppercase-columns=false
When I put a breakpoint after the CustomEclipseLinkProperties has been initialized, I can see that getBatchSize() returns null. If I look into getProperties(), I do see the values were detected, but they were inserted as a LinkedHashMap.
The expected behavior would be that we would obtain a Map that would use the entire suffix as the String key, rather than getting this LinkedHashMap that has essentially called String#split() on the properties list. This would mean that the call to getBatchSize() would return 1000.
I've seen a few answers such as this one but they don't seem generic enough to my liking. Is there not a way to simply get the entire suffix as the key when injected by #ConfigurationProperties? Else, it seems like it would require intervention whenever we would want to support a different type of property.
Turns out "suffix as key" is the default behavior if I swap from Map<String, Object> to Map<String, String>.
The Object value isn't actually useful in our case, so that solves this problem.

How can I access the request object when handling a MethodArgumentNotValidException?

I'm handling a MethodArgumentNotValidException thrown after a failed validation of a request object. All the usual stuff is in place: #Valid, #ControllerAdvice, and an extended ResponseEntityExceptionHandler, in which I override handleMethodArgumentNotValid().
As it happens, I need to access that same request object in order to form a customized error response. One way would be to intercept the request before it hits the controller and create a #RequestScope bean with the needed fields in case validation fails later.
Is there a better way?
Thanks to a suggestion from a colleague, I've found that the BindingResult within MethodArgumentNotValidException has a method named getTarget() that returns the validated object. As seen from the method signature (Object getTarget()), the return value needs a cast.
You should have the error fields in the MethodArgumentNotValidException class. Your handleMethodArgumentNotValid function might look like something as follows.
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public CustomInputErrorResponse handleMethodArgumentNotValid(MethodArgumentNotValidException e) {
String message = "Invalid inputs";
ArrayList<String> fieldNames = new ArrayList<String>();
for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
fieldNames.add(fieldError.getField());
}
return new CustomInputErrorResponse(message, fieldNames);
}
Considering you have a CustomInputErrorResponse class that takes two arguments for a custom message and error field names.

Using decorator pattern without adding "different" behaviour

I have facade interface where users can ask for information about lets say Engineers. That information should be transferred as JSON of which we made a DTO for. Now keep in mind that I have multiple datasources that can provide an item to this list of DTO.
So I believe right now that I can use Decorative Pattern by adding handler of the datasource to the myEngineerListDTO of type List<EngineerDTO>. So by that I mean all the datasources have the same DTO.
This picture below shows that VerticalScrollbar and HorizontalScrollBar have different behaviours added. Which means they add behaviour to the WindowDecorator interface.
My question, does my situation fit the decorator pattern? Do I specifically need to add a behaviour to use this pattern? And is there another pattern that does fit my situation? I have already considered Chain of Responsibility pattern, but because I don't need to terminate my chain on any given moment, i thought maybe Decorator pattern would be better.
Edit:
My end result should be: List<EngineersDTO> from all datasources. The reason I want to add this pattern is so that I can easily add another datasource behind the rest of the "pipeline". This datasource, just like the others, will have addEngineersDTOToList method.
To further illustrate on how you can Chain-of-responsibility pattern I put together a small example. I believe you should be able to adapt this solution to suit the needs of your real world problem.
Problem Space
We have an unknown set of user requests which contain the name of properties to be retrieved. There are multiple datasources which each have varying amounts of properties. We want to search through all possible data sources until all of the properties from the request have been discovered. Some data types and data sources might look like bellow (note I am using Lombok for brevity):
#lombok.Data
class FooBarData {
private final String foo;
private final String bar;
}
#lombok.Data
class FizzBuzzData {
private final String fizz;
private final String buzz;
}
class FooBarService {
public FooBarData invoke() {
System.out.println("This is an expensive FooBar call");
return new FooBarData("FOO", "BAR");
}
}
class FizzBuzzService {
public FizzBuzzData invoke() {
System.out.println("This is an expensive FizzBuzz call");
return new FizzBuzzData("FIZZ", "BUZZ");
}
}
Our end user might require multiple ways to resolve the data. The following could be a valid user input and expected response:
// Input
"foobar", "foo", "fizz"
// Output
{
"foobar" : {
"foo" : "FOO",
"bar" : "BAR"
},
"foo" : "FOO",
"fizz" : "FIZZ"
}
A basic interface and simple concrete implementation for our property resolver might look like bellow:
interface PropertyResolver {
Map<String, Object> resolve(List<String> properties);
}
class UnknownResolver implements PropertyResolver {
#Override
public Map<String, Object> resolve(List<String> properties) {
Map<String, Object> result = new HashMap<>();
for (String property : properties) {
result.put(property, "Unknown");
}
return result;
}
}
Solution Space
Rather than using a normal "Decorator pattern", a better solution may be a "Chain-of-responsibility pattern". This pattern is similar to the decorator pattern, however, each link in the chain is allowed to either work on the item, ignore the item, or end the execution. This is helpful for deciding if a call needs to be made, or terminating the chain if the work is complete for the request. Another difference from the decorator pattern is that resolve will not be overriden by each of the concrete classes; our abstract class can call out to the sub class when required using abstract methods.
Back to the problem at hand... For each resolver we need two components. A way to fetch data from our remote service, and a way to extract all the required properties from the data retrieved. For fetching the data we can provide an abstract method. For extracting a property from the fetched data we can make a small interface and maintain a list of these extractors seeing as multiple properties can be pulled from a single piece of data:
interface PropertyExtractor<Data> {
Object extract(Data data);
}
abstract class PropertyResolverChain<Data> implements PropertyResolver {
private final Map<String, PropertyExtractor<Data>> extractors = new HashMap<>();
private final PropertyResolver successor;
protected PropertyResolverChain(PropertyResolver successor) {
this.successor = successor;
}
protected abstract Data getData();
protected final void setBinding(String property, PropertyExtractor<Data> extractor) {
extractors.put(property, extractor);
}
#Override
public Map<String, Object> resolve(List<String> properties) {
...
}
}
The basic idea for the resolve method is to first evaluate which properties can be fulfilled by this PropertyResolver instance. If there are eligible properties then we will fetch the data using getData. For each eligible property we extract the property value and add it to a result map. Each property which cannot be resolved, the successor will be requested to be resolve that property. If all properties are resolved the chain of execution will end.
#Override
public Map<String, Object> resolve(List<String> properties) {
Map<String, Object> result = new HashMap<>();
List<String> eligibleProperties = new ArrayList<>(properties);
eligibleProperties.retainAll(extractors.keySet());
if (!eligibleProperties.isEmpty()) {
Data data = getData();
for (String property : eligibleProperties) {
result.put(property, extractors.get(property).extract(data));
}
}
List<String> remainingProperties = new ArrayList<>(properties);
remainingProperties.removeAll(eligibleProperties);
if (!remainingProperties.isEmpty()) {
result.putAll(successor.resolve(remainingProperties));
}
return result;
}
Implementing Resolvers
When we go to implement a concrete class for PropertyResolverChain we will need to implement the getData method and also bind PropertyExtractor instances. These bindings can act as an adapter for the data returned by each service. This data can follow the same structure as the data returned by the service, or have a custom schema. Using the FooBarService from earlier as an example, our class could be implemented like bellow (note that we can have many bindings which result in the same data being returned).
class FooBarResolver extends PropertyResolverChain<FooBarData> {
private final FooBarService remoteService;
FooBarResolver(PropertyResolver successor, FooBarService remoteService) {
super(successor);
this.remoteService = remoteService;
// return the whole object
setBinding("foobar", data -> data);
// accept different spellings
setBinding("foo", data -> data.getFoo());
setBinding("bar", data -> data.getBar());
setBinding("FOO", data -> data.getFoo());
setBinding("__bar", data -> data.getBar());
// create new properties all together!!
setBinding("barfoo", data -> data.getBar() + data.getFoo());
}
#Override
protected FooBarData getData() {
return remoteService.invoke();
}
}
Example Usage
Putting it all together, we can invoke the Resolver chain as shown bellow. We can observe that the expensive getData method call is only performed once per Resolver only if the property is bound to the resolver, and that the user gets only the exact fields which they require:
PropertyResolver resolver =
new FizzBuzzResolver(
new FooBarResolver(
new UnknownResolver(),
new FooBarService()),
new FizzBuzzService());
Map<String, Object> result = resolver.resolve(Arrays.asList(
"foobar", "foo", "__bar", "barfoo", "invalid", "fizz"));
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
System.out.println(mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(result));
Output
This is an expensive FizzBuzz call
This is an expensive FooBar call
{
"foobar" : {
"foo" : "FOO",
"bar" : "BAR"
},
"__bar" : "BAR",
"barfoo" : "BARFOO",
"foo" : "FOO",
"invalid" : "Unknown",
"fizz" : "FIZZ"
}

Should I put the ID of my entity in the URL or into the form as a hidden field?

I think in terms of REST, the ID should be placed into the URL, something like:
https://example.com/module/[ID]
and then I call GET, PUT, DELETE on that URL. That's kind of clear I think. In Spring MVC controllers, I'd get the ID with #PathVariable. Works.
Now, my practical problem with Spring MVC is, that if I do this, I have to NOT include the ID as part of the form (as well), Spring emits warnings of type
Skipping URI variable 'id' since the request contains a bind value with the same name.
otherwise. And it also makes kind of sense to only send it once, right? What would you do if they don't match??
That would be fine, but I do have a custom validator for my form backing bean, that needs to know the ID! (It needs to check if a certain unique name is already being used for a different entity instance, but cannot without knowing the ID of the submitted form).
I haven't found a good way to tell the validator that ID from #PathVariable, since the validation happens even before code in my controller method is executed.
How would you solve this dilemma?
This is my Controller (modified):
#Controller
#RequestMapping("/channels")
#RoleRestricted(resource = RoleResource.CHANNEL_ADMIN)
public class ChannelAdminController
{
protected ChannelService channelService;
protected ChannelEditFormValidator formValidator;
#Autowired
public ChannelAdminController(ChannelService channelService, ChannelEditFormValidator formValidator)
{
this.channelService = channelService;
this.formValidator = formValidator;
}
#RequestMapping(value = "/{channelId}/admin", method = RequestMethod.GET)
public String editChannel(#PathVariable Long channelId, #ModelAttribute("channelForm") ChannelEditForm channelEditForm, Model model)
{
if (channelId > 0)
{
// Populate from persistent entity
}
else
{
// Prepare form with default values
}
return "channel/admin/channel-edit";
}
#RequestMapping(value = "/{channelId}/admin", method = RequestMethod.PUT)
public String saveChannel(#PathVariable Long channelId, #ModelAttribute("channelForm") #Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
{
try
{
// Has to validate in controller if the name is already used by another channel, since in the validator, we don't know the channelId
Long nameChannelId = channelService.getChannelIdByName(channelEditForm.getName());
if (nameChannelId != null && !nameChannelId.equals(channelId))
result.rejectValue("name", "channel:admin.f1.error.name");
}
catch (EmptyResultDataAccessException e)
{
// That's fine, new valid unique name (not so fine using an exception for this, but you know...)
}
if (result.hasErrors())
{
return "channel/admin/channel-edit";
}
// Copy properties from form to ChannelEditRequest DTO
// ...
// Save
// ...
redirectAttributes.addFlashAttribute("successMessage", new SuccessMessage.Builder("channel:admin.f1.success", "Success!").build());
// POST-REDIRECT-GET
return "redirect:/channels/" + channelId + "/admin";
}
#InitBinder("channelForm")
protected void initBinder(WebDataBinder binder)
{
binder.setValidator(formValidator);
}
}
I think I finally found the solution.
As it turns out Spring binds path variables to form beans, too! I haven't found this documented anywhere, and wouldn't have expected it, but when trying to rename the path variable, like #DavidW suggested (which I would have expected to only have a local effect in my controller method), I realized that some things got broken, because of the before-mentioned.
So, basically, the solution is to have the ID property on the form-backing object, too, BUT not including a hidden input field in the HTML form. This way Spring will use the path variable and populate it on the form. The local #PathVariable parameter in the controller method can even be skipped.
The cleanest way to solve this, I think, is to let the database handle the duplicates: Add a unique constraint to the database column. (or JPA by adding a #UniqueConstraint)
But you still have to catch the database exception and transform it to a user friendly message.
This way you can keep the spring MVC validator simple: only validate fields, without needing to query the database.
Could you not simply disambiguate the 2 (URI template variables vs. parameters) by using a different name for your URI template variable?
#RequestMapping(value = "/{chanId}/admin", method = RequestMethod.PUT)
public String saveChannel(#PathVariable Long chanId, #ModelAttribute("channelForm") #Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
{
[...]
What ever you said is correct the correct way to design rest api is to mention the resource id in path variable if you look at some examples from the swagger now as open api you could find similar examples there
for you the correct solution would be to use a custom validator like this
import javax.validation.Validator;`
import org.apache.commons.lang3.StringUtils;`
import org.springframework.validation.Errors;`
importorg.springframework.validation.beanvalidation.CustomValidatorBean;`
public class MyValidator extends CustomValidatorBean {`
public void myvalidate(Object target,Errors errors,String flag,Profile profile){
super.validate(target,errors);
if(StringUtils.isEmpty(profile.name())){
errors.rejectValue("name", "NotBlank.profilereg.name", new Object[] { "name" }, "Missing Required Fields");
}
}
}
This would make sure all the fields are validated and you dont need to pass the id in the form.

Picketlink: How to get annotation parameters and the name of the function decorated when using #Secures?

I'm trying to define and use a custom security binding type called BasicRolesAllowed, as has been demonstrated in the Picketlink quickstarts here.
The only different between my type the ones in the quickstart, is that my annotation has to accept an array of strings (we want to secure methods using not just one but possibly combinations of roles), and thus my annotation is defined thus:
public #interface BasicRolesAllowed {
String[] value() default {};
}
Following the quickstart, I've tried to define how this decorator authenticates as such:
#Secures
#BasicRolesAllowed
public boolean doAdminCheck(Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) throws Exception {
/*
Sample usage of #BasicRolesAllowed is like:
#BasicRolesAllowed(value = RoleConstants.CREATE_USER)
TODO: need to get these from the #BasicRolesAllowed annotation instance/usage
*/
String[] requiredRoles = {};// get these from annotation
boolean isAuthorized = true;
for (String role : requiredRoles)
isAuthorized = isAuthorized && hasRole(relationshipManager, identity.getAccount(), getRole(identityManager, role));
return isAuthorized;
}
And as can be seen in the snippet, the trick part is:
String[] requiredRoles = {};// get these from annotation
How do I get the string constants passed to the annotation on the decorated method so I can use them in looking up roles?
Some Hints:
There's an answer to a similar question here, but the problem is that in that solution; one needs to know the name of the decorated function or class - which in my case is impossible given that the decorator will be used just about anywhere, and I don't know how to get these via the method shown in the Picketlink quickstart.
Also, the solution only shows how to obtain the value passed to an annotation expecting only 1 string - maybe I could try using values(), but the above limitation still stands in my way.
Thanks in advance to anyone who can help.
Thanks to #pedroigor over at #picketlink (freenode), the solution can be gleaned from an example of such a use-case in the picketlink quickstart here. In that file, a method getAnnotation() is defined, which has the signature:
private <T extends Annotation> T getAnnotation(InvocationContext invocationContext, Class<T> annotationType)
So, using this method, I'm able to introspect and obtain the values passed to my annotation as can be seen in my new implementation of the roles checking method here:
#Secures
#BasicRolesAllowed
public boolean hasBasicRolesCheck(InvocationContext invocationContext, Identity identity, IdentityManager identityManager, RelationshipManager relationshipManager) throws Exception {
BasicRolesAllowed basicRolesAllowed = getAnnotation(invocationContext,BasicRolesAllowed.class);
String[] requiredRoles = basicRolesAllowed.value();// get these from annotation
boolean isAuthorized = true;
for (String role : requiredRoles)
isAuthorized = isAuthorized && hasRole(relationshipManager, identity.getAccount(), getRole(identityManager, role));
return isAuthorized;
}
The essential modifications being:
I had to pass an instance of the invocation context InvocationContext invocationContext by adding this as a parameter to my method definition (CDI magic takes care of all else I hear).
I then obtain the annotation instance by calling:
BasicRolesAllowed basicRolesAllowed = getAnnotation(invocationContext,BasicRolesAllowed.class);
And then get the values/parameters passed to the annotation thus:
String[] requiredRoles = basicRolesAllowed.value();// get these from annotation
This solves my problem :-)

Categories