Calling method A is forbidden for type B in Thymeleaf expressions - spring

I have just upgraded web project to Spring 6, Spring Boot 3 GA, and now I deal with following Thymeleaf incompability. I am generating form select.
<select class="form-select" th:field="*{bankAccountId}" th:classappend="${#fields.hasErrors('bankAccountId')} ? 'is-invalid'">
<option value="" th:text="#{SelectBankAccount}">SELECT_BANK_ACCOUNT</option>
<option th:each="bankAccount : ${bankAccounts}" th:value="${bankAccount.getId()}" th:text="${bankAccount.getName()}" th:selected="${bankAccount.getId().equals(bankAccountId)}">BANK_ACCOUNT_NAME</option>
</select>
Expression in th:selected is failing with following exception.
org.springframework.expression.EvaluationException: Calling method 'equals' is forbidden for type 'class java.util.UUID' in Thymeleaf expressions. Blocked classes are: [com.sun.*, jakarta.*, java.*, javax.*, jdk.*, org.ietf.jgss.*, org.omg.*, org.w3c.dom.*, org.xml.sax.*, sun.*]. Allowed classes are: [java.lang.Boolean, java.lang.Byte, java.lang.Character, java.lang.Double, java.lang.Enum, java.lang.Float, java.lang.Integer, java.lang.Iterable, java.lang.Long, java.lang.Math, java.lang.Number, java.lang.Short, java.lang.String, java.math.BigDecimal, java.math.BigInteger, java.math.RoundingMode, java.time.*, java.util.ArrayList, java.util.Calendar, java.util.Calendar, java.util.Collection, java.util.Date, java.util.Enumeration, java.util.HashMap, java.util.HashSet, java.util.Iterator, java.util.LinkedHashMap, java.util.LinkedHashSet, java.util.LinkedList, java.util.List, java.util.Locale, java.util.Map, java.util.Map$Entry, java.util.Optional, java.util.Properties, java.util.Set, java.util.stream.Stream].
at org.thymeleaf.spring6.expression.ThymeleafEvaluationContext$ThymeleafEvaluationContextACLMethodResolver.resolve(ThymeleafEvaluationContext.java:282) ~[thymeleaf-spring6-3.1.0.RELEASE.jar:3.1.0.RELEASE]
Both bankAccount.getId() and $bankAccountId are java.util.UUID.
Cast to String is not possible in Thymeleaf expression. All methods calls on java.util package objects are forbidden.
Not sure what is the most correct workaround. Either I can cast the UUIDs to Strings in backAccount object, add comparator method into backAccount, call a comparator util statically or via #bean notation, or even define a custom method for Thymeleaf and use as #xxx() expression.

Had the same issue, however it was a bug with Spring. It was resolved in the version 3.0.1.
For more information, look here: https://github.com/thymeleaf/thymeleaf-spring/issues/200#issuecomment-545922604

Related

Spring boot and thymeleaf: Can not evaluate size() of an ArrayList

I have an ArrayList in my mvc controller class:
List<Market> markets = loadMarkets();
I add this list to Model object:
model.addAttribute("markets", markets);
In my template, when I want to get size() of this array:
<span>Top <strong th:text="${markets.size}"></strong>assets.</span>
I got this error:
Cannot evaluate because of compilation error(s): The method size() is undefined for the type Object.
Snapshot of the VSCode debugging tools:
I am using Spring Boot version 2.2.6.RELEASE with Thymeleaf template engine.
When you use property notation th:text="${markets.size}", then Thymeleaf will search for a method getSize() on the markets attribute in the model. But there is no getSize() method, only size() method.
So use the method itself instead of using the property notation:
th:text="${markets.size()}"

How can I prevent a null RequestParam raising an exception?

I'm working in a Spring Boot environnement using Kotlin. I made a controller with a method annotated with #GetMapping. This method have some parameters of type #RequestParam declared as Double type. If I try to call my method without providing these parameters, my code raises the following exception:
java.lang.IllegalStateException: Optional double parameter 'latitude' is present but cannot be translated into a null value due to being declared as a primitive type.
I assume that the parameters have default value (probably 0.0), but Kotlin need an object which can be null, so the exception is raised.
All works fine if I provide the parameters, but I want my code working if no parameters are provided.
How can I avoid this exception?
Here's how my controller looks like:
#RestController
#RequestMapping("/api/stations")
class StationController {
#GetMapping
fun findAll(#RequestParam(value = "latitude", required = false) currentLatitude: Double,
#RequestParam(value = "longitude", required = false) currentLongitude: Double): ResponseEntity<List<Entity>> {
//Method body
}
Maybe the following part of the documentation regarding basic types will help you:
On the Java platform, numbers are physically stored as JVM primitive types, unless we need a nullable number reference (e.g. Int?) or generics are involved. In the latter cases numbers are boxed.
Your guess might be correct then. Try using Double? and it should be ok.

Easy way to submit an array field with Spring MVC forms?

I have a form with a list of nested fieldsets corresponding to a collection of objects, backed by a form object server-side. Fieldsets can be added or removed client-side. I want to submit a form without caring about object indices or sparse lists in command object.
Here's my Controller method code:
#PostMapping("/foobar")
public String doPost(FoobarForm form) {
//...
}
In PHP, Rails etc it's very easy:
<input name="prop[]">
, and it will automatically populate $_POST["prop"] with all the values.
Working with Spring MVC, I tried these things:
<input name="prop[]"> - doesn't work saying Invalid property 'prop[]' of bean class [...]: Invalid index in property path 'prop[]'; nested exception is java.lang.NumberFormatException: For input string: ""
<input name="prop"> - will not bind to a list-typed bean property, even when multiple fields present.
<input name="prop[${i}]"> - implies all that hassle with sparse list and index handling, both client-side and server-side. Certainly not the right way to do things when working with a powerful web framework.
I'm wondering why can't I just use [] in property name and let Spring create a list automatically? It was asked three times on Spring JIRA without any reasonable responses.
Spring form binding makes this more easy. You need to add List object in your bean and bind that in jsp using spring form.
class FoobarForm {
List<String> prop;
}
In jsp, if you need to show/edit value all at once then <form:input path="prop" />
.if you want to show one by one then use indexing<form:input path="prop[0]" />. Use proper CommandName in form. It will work.
I found the answer here.
You can use the MultiValueMap<String, String>
#RequestMapping("foo")
public String bar(#RequestParam MultiValueMap<String, String> parameters){ ... }
With these two inputs:
<input name="prop" value="One">
<input name="prop" value="Two">
the result will be {prop=["One","Two"]}
The code below will work too.
public String bar(#RequestParam("prop") List<String> props){ ... }

Grails messages.properties typeMismatch error messages

I have a User domain class that has an enum field in it called Gender. It has values for male and female in it. When I represent it on the GSP page it appears as a drop down. To test the security of the web app, I used firebug to alter the value of male which was 0 to abc to see if the validation catches it. I does but the message it showed wasn't human readable. I used the following in my messages.properties.
typeMismatch.User.gender=The value for {0} is {1} and it is not a valid value
But the problem is, when my GSP renders the Errors, It doesn't replace {1} with the rejected value of the drop down list. I tried {2} and {3} and etc, it only prints them as String and don't replace them. Is there a way to get the rejectedValue in the message ?
Updated: I have toString method in my enum Gender too. Here is some code from controller and gsp page.
Controller:
if (bioInstance.hasErrors() || currentUser.hasErrors()) {
render(view: 'editProfile', model: [user: currentUser, bioInstance: bioInstance])
return
}
GSP:
<g:hasErrors beans="[bioInstance:Bio,user:User]">
<ul class="alert alert-danger">
<g:renderErrors beans="[bioInstance:Bio,user:User]" as="list"/>
</ul>
</g:hasErrors>
It happens because grails renders error messages via message(error: someError) method. someError is an instance of org.springframework.validation.FieldError, which was created during validation phase, and it has only one argument to its message - the name of domain class itself. So, it's a binding error, not a validation error, which by default has 3 arguments: property name, domain class name and rejected value.
If you're using grails 2.3 and above - you can write a custom binder as described at http://grails.org/doc/latest/guide/theWebLayer.html#dataBinding. Or use an integer field inside the domain class, write some constraints for it, and expose it to the outside via getter as enum.

Spring form tags. Allow nulls in form:select (enum)

I'm using Spring form tags for filling form with values.
I have form backing object:
public class FormInfo {
public enum Status {ON, OFF}
private Satus status;
//getter setter
...
}
And in JSP Status enum presented like this:
<form:form commandObject="formInfo " ...>
<form:select path="status">
<form:option value="null" label="Please select"/>
<form:options/>
</form:select>
</form:form>
All works fine, i.e. default message and enum values are presented in <select>.
But the status field is not required, so I want allow user to leave Status field unselected. But if form submitted without selecting status field, then I get error:
error in object 'formInfo' on field 'status': rejected value [null];
How I can set enum to null when no values is selected?
Please note I'm using JSR 303 validation. And error described above is not happens automatically, I get this error message manually from following method BindingResult#getFieldErrors().
This is my controller code:
public void myMethod(#Valid #ModelAttribute("formInfo") FormInfo sourcingDetail, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
log.error("Error during validation is occurred." + bindingResult.getFieldErrors().toString()); // <-- this is error message
}
...
}
Also please note, I didn't set any JSR-303 annotation (like #NotNull) on status field.
UPDATE:
Almost full error message which I get from calling this method BindingResult#getFieldErrors() (explained above):
Error during validation is occurred.[Field error in object 'formInfo'
on field 'status': rejected value [null];
...
[Failed to convert property value of type 'java.lang.String' to
required type 'com.my.project.model.Status' for property 'status';
nested exception is java.lang.IllegalStateException: Cannot convert
value of type [java.lang.String] to required type
[com.my.project.model.Status] for property 'status': no matching
editors or conversion strategy found],
Looks like you have the same problem as i!.
There is a method in the controller that serves as a hook where you could specify how to transform the String values, that came from the HTTP request, to a concrete object!.
The method is called initBinder, and there you attach the right behavior to do the conversion properly. I am still researching, but so far, looks good.
Take a look at this :
Form Values to be Null instead of "" in Spring
http://kaushalyas.blogspot.com.ar/2011/02/data-binding-in-spring-mvc.html
Hope it helps to found the solution!
Greetings
Victor.

Resources