spring-projects / spring-projects/spring-framework

Using the MvcUriComponentsBuilder to get a URL to a controller method adds empty parameter when the value is not provided [SPR-14890]

Open
#19,456 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

in: web type: enhancement
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

Cèsar Ordiñana opened SPR-14890 and commented

I'm developing a Spring boot with Spring MVC web app using Thymeleaf views. I'm using the Building URIs to Controllers and methods from views feature through the Thymeleaf #mvc.url utility.

I have the following sample controller:

@Controller
@RequestMapping(value = "/categories/{category}/products",
    name = "CategoriesItemProductsThymeleafController",
    produces = MediaType.TEXT_HTML_VALUE)
public class CategoriesItemProductsThymeleafController {
  public CategoryService categoryService;

  @Autowired
  public CategoriesItemProductsThymeleafController(CategoryService categoryService) {
    this.categoryService = categoryService;
  }

  @PostMapping(name = "addToProducts")
  @ResponseBody
  public ResponseEntity<?> addToProducts(@ModelAttribute Category category,
      @RequestParam("products") List<Long> products) {
    categoryService.addToProducts(category, products);
    return ResponseEntity.ok().build();
  }
}

In the Thymeleaf view I get the link to that controller method with something like:

<div id="create-url" data-data-create-url="${(#mvc.url('CategoriesItemProductsThymeleafController#addToProducts')).buildAndExpand('CATEGORY_ID')}"></div>

It uses internally the MvcUriComponentsBuilder.fromMappingName(String) method which returns a MethodArgumentBuilder. It expects a list of product ids as a request parameter, but I can't use the MethodArgumentBuilder.arg() method because the values I want to send are to be selected in javascript.

The problem with this method is that the Url return is the following one:

/categories/CATEGORY_ID/products?products

I use that Url as a parameter to call jQuery.ajax() using my own products values. If I use a GET method, the url ends up being:

/categories/CATEGORY_ID/products?products&products=1&products=2

Then the controller method receives the following list of values: [null, 1, 2]. As a workaround I can remove those null values from the list, but I think the Url is not constructed as it should.

After some debugging I've found the code which add those empty parameters is the HierarchicalUriComponents.getQuery() method. It has the following code:

for (Object value : values) {
    if (queryBuilder.length() != 0) {
        queryBuilder.append('&');
    }
    queryBuilder.append(name);

    if (value != null) {
        queryBuilder.append('=');
        queryBuilder.append(value.toString());
    }
  }

The problem could be solved by changing the code to add the parameter only if value!=null.


Affects: 4.3.3

Issue Links:

  • #18113 UriComponentsBuilder interprets empty request parameters as null

1 votes, 3 watchers

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with MvcUriComponentsBuilder.fromMappingName(String) and the mentioned HierarchicalUriComponents.getQuery() implementation. Reproduce the controller and URL-building example from the issue, then verify that an omitted request parameter does not produce a bare “?products” entry while explicitly added values still appear correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.