Search code examples
grailsgrails-domain-classgrails-controller

Why is Grails (6.0.0) trying to cast a String URL parameter to a domain object? (It seems related to a partial match of the domain name.)


I have a controller method that takes a String argument called "search":

def test(String search) {
   log.info("search: ${search}")
   render("testing ${search}")
} 

This worked fine in Grails 3.3.9, but in 6.0.0, I get an error when my URL includes ?search=mystring

Cannot cast object 'mystring' with class 'java.lang.String' to class 'com.whatever.project.SavedSearch'

I do have a domain object called SavedSearch. It appears that Grails is attempting to cast the parameter named "search" into an object of this type.

When I change the name of the parameter to anything else, like "searchString", it works fine.

Here are steps to reproduce using a minimal Grails 6 project:


git clone https://github.com/landydan/so-grails-6-poc-search-query.git

git checkout revised

grails clean

grails run-app

Navigate in the browser to localhost:8080. 
Click on the link shown in the landing page.  
You should see the class cast exception.

Note that this bug seems to depend on the presence of the method below that references the SavedSearch object, in the same controller:

    def something() {
        Long searchId = params.id as Long
        // below is the line that needs to be present
        SavedSearch search = SavedSearch.read(searchId)
        render("placeholder")
    }

Solution

  • I had the same issues after migration. If you have another method where you assign values to variable with the same name as variable in your method, for example:

    def test(String search) {
       log.info("search: ${search}")
       render("testing ${search}")
    }
    
    def another() {
        SavedSearch search = SavedSearch.read(searchId)
    }
    

    Grails 5/6 will try to use any "search" in method declaration as SavedSearch type. This happens for domains and for command objects.

    This is obviously a bug in Grails, but there is simple workaround - just name your variable differently, for example:

    SavedSearch savedSearch = SavedSearch.read(searchId)