Using parameter 1 without using parameter 0 in Spring Boot messages - spring

In messages.properties file:
validation.title=At least {1} characters
Also LocalValidatorFactoryBean is defined:
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource);
return bean;
}
When I run the code I get the result:
At least {1} characters
But if I change in properties:
validation.title=At least {1} characters {0}
I get the result:
At least 20 characters title
But I do not need the 0 parameters.
Is there a way to fix this?

You're trying to access array parameters and with this approach in order to get that done and have your string interpolated you need to use the one at the index 0 and I have no idea why. There's another way though and with common annotations (haven't tried this with custom annotations) for validating forms this can be solved as follows:
// the form
public class SomeForm {
#Length(min = 4, max = 16, message="{username.length.error}")
private String username;
// other stuff
}
In the messages.properties
username.length.error = Username cannot be blank and its length must be between {min} and {max} characters.
So in the message.properties (in this case {min} and {max}) the placeholders must match the names of the params you have on the annotation.

Related

Spring: #PathVariable from multiple path elements like with jax-rs?

I'm migrating some routes from a jax-rs based application to SpringBoot. In jax-rs I can use #Path to define a regex that contains multiple URL path elements:
#Path("{id:[^/]+/y[\d]{4}/m[\d]{1,2}/d[\d]{1,2}/h[\d]{1,2}}/")
The id variable in the method body will then be the matching segment of the URL and I can go about my day.
With #RequestMapping in Spring this doesn't work. As soon as you put a forward slash into the regex you get a PatternParseException.
PathContainer pathContainingSlash = PathContainer.parsePath("/api/test/y1978/m07/d15");
PathPatternParser parser = new PathPatternParser();
assertThrows(PatternParseException.class, () ->
parser.parse("/api/test/{ticketId:y[\\d]{4}/m[\\d]{1,2}/d[\\d]{1,2}}"));
This same problem appears to happen with AntPathMatcher.
AntPathMatcher antPathMatcher = new AntPathMatcher();
assertThrows(IllegalStateException.class, () ->
antPathMatcher.extractUriTemplateVariables(
"/api/test/{ticketId:y[\\d]{4}/m[\\d]{1,2}/d[\\d]{1,2}}",
"/api/test/y1978/m07/d15"));
This is a problem because I have about 78 of these URL patterns. I'm going to have to define each pattern individually with each path element being a separate variable. Then I'm going to have to use String concatenation to combine them back together in the format of a path.
#GetMapping("/{year:y[\\d]{4}}/{month:m[\\d]1,2}/{day:d[\\d]{1,2}")
public ResponseEntity<Void> foo(#PathVariable String year,
#PathVariable String month,
#PathVariable String day) {
String date = year + "/" + month + "/" + day;
}
Other than using Jax-rs in my SpringBoot app, is there accomplish this? It's possible to write them all like this but it seems sub-optimal.
For clarity, I really want a way to extract multiple path elements from a URL into a #PathVariable. I would like something like this:
#GetMapping("/api/test/{date:y[\\d]{4}/m[\\d]{1,2}/d[\\d]{1,2}}")
public ResponseEntity<Void> foo(#PathVariable String date) {}
So that date is now equal to y1978/m07/d15
Also, this is just one example pattern. There are 78 unique patterns, that have a varying number of a path elements and contents of the elements. In Jax-RS using #Path I can OR these regexes together and create one route and the path variable is accessible inside the method.
how about adding spring-boot-starter-validation for validation
requires addition of following jar
org.springframework.boot:spring-boot-starter-validation
add #org.springframework.validation.annotation.Validated on top of controller class
add #javax.validation.constraints.Pattern with regex attribute to the #PathVariable method params
#GetMapping("{year}/{month}/{day}")
public ResponseEntity<Void> foo(
#PathVariable #Pattern(regexp = "[\\d]{4}", message = "year must be ..") String year,
#PathVariable #Pattern(regexp = "[\\d]{1,2}", message = "month must ..") String month,
#PathVariable #Pattern(regexp = "[\\d]{1,2}", message= "day must be ..") String day) {
String date = year + "/" + month + "/" + day;
to return http 400 status, add a method to handle the ConstraintViolationException
#ExceptionHandler(value = { ConstraintViolationException.class })
protected ResponseEntity<List<String>> handleConstraintViolations(ConstraintViolationException ex, WebRequest request) {
List<String> errorMessages = ex.getConstraintViolations().stream()
.map(violation -> violation.getMessage()).collect(Collectors.toList());
return new ResponseEntity<List<String>>(errorMessages, HttpStatus.BAD_REQUEST);
}
more validation examples here:
https://reflectoring.io/bean-validation-with-spring-boot/
more exception handling options here:
https://www.baeldung.com/exception-handling-for-rest-with-spring
a possible option using path rewrite from this thread
Spring MVC Getting PathVariables containing dots and slashes
add
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>urlrewritefilter</artifactId>
<version>4.0.3</version>
</dependency>
add rewrite rules to src/main/webapp/WEB-INF/urlrewrite.xml
<urlrewrite>
<rule>
<from>^/api/test/(y[\d]{4})/(m[\d]{2})/(d[\d]{2})$</from>
<to>/api/test?date=$1/$2/$3</to>
</rule>
</urlrewrite>
create controller methods matching the to path of the rewrite rule with query params
#GetMapping("/api/test")
public ResponseEntity<Void> foo(#RequestParam String date) {
System.out.println(date);
return new ResponseEntity<Void>(HttpStatus.OK);
}
add configuration class to register the rewrite filter with urlPatterns to filter
#Configuration
public class FiltersConfig {
#Bean
public FilterRegistrationBean<Filter> someFilterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<Filter>();
registration.setFilter(rewriteFilter());
// add paths to filter
registration.addUrlPatterns("/api/*");
registration.setName("urlRewriteFilter");
registration.setOrder(1);
return registration;
}
public Filter rewriteFilter() {
return new UrlRewriteFilter();
}
}

Assign a value to a parameter in application properties before use

I have a property in my application.properties file in a SpringBoot project -
app.module.service.url=http://localhost:8090/{DOCID}
I have injected it into my Java file as below -
#Value("${app.module.service.url}")
private String url;
Now I need to replace DOCID with some value in my function(i.e-dynamic). How do I get to do this or am I completely missing it?
I am aware that we can do this in case of message properties in Spring. But here I have nothing to do with Locales.
Edit:
There is some value which I need to put in place of {DOCID} when using it within my Java implementation.
...
public class Sample{
#Value("${app.module.service.url}")
private String url;
public void sampleFunc(){
String str = "random Value" //some dynamic value goes in here
...
Now I need to replace {DOCID} with str in url
Two way binding is not possible in Spring Properties.It can only read run time and environmental variables.
Please refer following link for more information.
https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding
You can use spring UriTemplate for this purpose.
In your implementation class:
...
public class Sample{
#Value("${app.module.service.url}")
private String url;
public void sampleFunc(){
String dockId = someValue; // customize according to your need
UriTemplate uriTemplate = new UriTemplate(url);
URI uri = uriTemplate.expand(docId);
new RestTemplate().postForEntity(uri, requestObhect, Responseclass.class);
}
...
For more information, you can refer :
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/UriTemplate.html
Try like this ...
app.module.service.url=http://localhost:8090/{{docId}}
And set run configuration as below
-DdocId = 133323 // any number which you want

JSR303 Validation - Bean's property as Message Parameter

In JSR303 validation,is it possible to pass the bean's property as parameter for validation message ?
Ex.
class BeanA {
private String userName;
#Min(value=15, message="Age of {userName} should be greater than {value}")
private int age;
public BeanA(String userName,int age){
//initialization
}
}
For an object BeanA("Ahamed", 12) , I should get error "Age of Ahamed should be greater than 15"
Unfortunatelly you can pass to annotation parameter only constant values. Since you need to evaluate value of message, it cannot be set as annotation parameter.
As a matter of fact, you don't really need to do this, default error message tells more or less what is going on, although it is not as informative as you would like.

spring mvc annotation validation integer

I have an object.
public class MyObject
{
....
#Column(name = "a_number") #NotNull #NumberFormat(style = Style.NUMBER) #Min(1)
private Integer aNumber;
...
//getters and setters
}
In my controller I have #Valid annotation on my object being posted. I do have validation working on all my other fields in the class (their all Strings) except this number. If I enter a number from my form it works fine and if I violate the #Min(1) it also gives me the correct validation error. My problem however is that if you enter a string instead of a number it throw a NumberFormatException.
I've seen many examples of Integer and validation but no one accounts for if you enter a string into the form being posted. Do I need to do the validation else where? Javascript? I would like a solution that falls in line with the rest of spring validation so I could use this in other classes. I would just like an error stating it must be numeric. Also I tried using the #Pattern annotation but apparently thats just for strings.
Suggestions?
You can add the following to your file which controls your error messages (these are the generic ones it looks for in the case of a type mismatch:
typeMismatch.commandObjectName.aNumber=You have entered an invalid number for ...
typeMismatch.aNumber=You have entered an invalid number for ...
typeMismatch.java.lang.Integer=You have input a non-numeric value into a field expecting a number...
typeMismatch=You have entered incorrect data on this page. Please fix (Catches all not found)
For those who did not get the idea right here is what to do in spring 4.2.0.
Create a file name messages.properties in WEB-INF > classes folder. And put the above type mismatch messages in that file.
In spring configuration or servlet.xml file create the following bean.
<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basename" value="messages"></beans:property>
</beans:bean>
And for your model attribute like private Integer aNumber; in the question along with other validation rules this rule is also applied for type mismatch conversion. You will get your desired message in this.
<form:errors path="aNumber"></form:errors>
Hope it helps others.
Still relevant, so I'll add the programmatical approach of message source bean definition:
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}

Using a custom ResourceBundle with Hibernate Validator

I'm trying to set up a custom message source for Hibernate Validator 4.1 through Spring 3.0. I've set up the necessary configuration:
<!-- JSR-303 -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource"/>
</bean>
The translations are served from my message source, but it seems that the replacement tokens in the messages themselves are looked up in the message source, i.e. for a:
my.message=the property {prop} is invalid
there are calls to look up 'prop' in the messageSource. Going into ResourceBundleMessageInterpolator.interpolateMessage I note that the javadoc states:
Runs the message interpolation according to algorithm specified in JSR 303.
Note:
Look-ups in user bundles is recursive whereas look-ups in default bundle are not!
This looks to me like the recursion will always take place for a user-specified bundle, so in effect I can not translate standard messages like the one for Size.
How can I plug-in my own message source and be able to have parameters be replaced in the message?
This looks to me like the recursion
will always take place for a
user-specified bundle, so in effect I
can not translate standard messages
like the one for Size.
Hibernate Validator's ResourceBundleMessageInterpolator create two instances of ResourceBundleLocator (i.e. PlatformResourceBundleLocator) one for UserDefined validation messages - userResourceBundleLocator and the other for JSR-303 Standard validation messages - defaultResourceBundleLocator.
Any text that appears within two curly braces e.g. {someText} in the message is treated as replacementToken. ResourceBundleMessageInterpolator tries to find the matching value which can replace the replacementToken in ResourceBundleLocators.
first in UserDefinedValidationMessages (which is recursive),
then in DefaultValidationMessages (which is NOT recursive).
So, if you put a Standard JSR-303 message in custom ResourceBundle say, validation_erros.properties, it will be replaced by your custom message. See in this EXAMPLE Standard NotNull validation message 'may not be null' has been replaced by custom 'MyNotNullMessage' message.
How can I plug-in my own message
source and be able to have parameters
be replaced in the message?
my.message=the property {prop} is
invalid
After going through both ResourceBundleLocators, ResourceBundleMessageInterpolator finds for more replaceTokens in the resolvedMessage (resolved by both bundles). These replacementToken are nothing but the names of Annotation's attributes, if such replaceTokens are found in the resolvedMessage, they are replaced by the values of matching Annotation attributes.
ResourceBundleMessageInterpolator.java [Line 168, 4.1.0.Final]
resolvedMessage = replaceAnnotationAttributes( resolvedMessage, annotationParameters );
Providing an example to replace {prop} with custom value, I hope it will help you....
MyNotNull.java
#Constraint(validatedBy = {MyNotNullValidator.class})
public #interface MyNotNull {
String propertyName(); //Annotation Attribute Name
String message() default "{myNotNull}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
}
MyNotNullValidator.java
public class MyNotNullValidator implements ConstraintValidator<MyNotNull, Object> {
public void initialize(MyNotNull parameters) {
}
public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
return object != null;
}
}
User.java
class User {
private String userName;
/* whatever name you provide as propertyName will replace {propertyName} in resource bundle */
// Annotation Attribute Value
#MyNotNull(propertyName="userName")
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
validation_errors.properties
notNull={propertyName} cannot be null
Test
public void test() {
LocalValidatorFactoryBean factory = applicationContext.getBean("validator", LocalValidatorFactoryBean.class);
Validator validator = factory.getValidator();
User user = new User("James", "Bond");
user.setUserName(null);
Set<ConstraintViolation<User>> violations = validator.validate(user);
for(ConstraintViolation<User> violation : violations) {
System.out.println("Custom Message:- " + violation.getMessage());
}
}
Output
Custom Message:- userName cannot be null

Resources