Skip to content

Request param value with curly braces throws IllegalArgumentException #335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dreambrother opened this issue Apr 21, 2015 · 9 comments
Open

Comments

@dreambrother
Copy link

Example:

@ResponseBody
@RequestMapping(value = "", method = RequestMethod.GET)
public Resources<FileResource> listFilesHal(
        @RequestParam(value = "q") String query) {
    Link self = linkTo(methodOn(FileRestController.class).listFilesHal(query)).withSelfRel();
    return new Resources<>(listFilesImpl(query), self);
}

If q contains curly braces (and it does because it's JSON) then linkTo throws
GET http://localhost:8080/files?q={"foo": "bar"}

java.lang.IllegalArgumentException: Map has no value for '"foo"'
    at org.springframework.web.util.UriComponents$MapTemplateVariables.getValue(UriComponents.java:276)
    at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:221)
    at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:326)
    at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:46)
    at org.springframework.web.util.UriComponents.expand(UriComponents.java:152)
    at org.springframework.web.util.UriComponentsBuilder.buildAndExpand(UriComponentsBuilder.java:302)
    at org.springframework.hateoas.mvc.ControllerLinkBuilderFactory.linkTo(ControllerLinkBuilderFactory.java:145)
    at org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo(ControllerLinkBuilder.java:135)
@vivin
Copy link

vivin commented Apr 24, 2015

According to RFC 1738, curly braces are illegal in URLs, so what you have is actually not a valid URL. if your query parameter has a JSON value, you should URL encode it. " and space are illegal as well, whereas : is reserved. In this case, you would end up with GET http://localhost:8080/files?q=%7B%22foo%22%3A%20%22bar%22%7D.

@dreambrother
Copy link
Author

@vivin, seems spring-hateoas should do this automatically

@vivin
Copy link

vivin commented Apr 26, 2015

Oh, my mistake. I thought that was a request coming in. This is a URL that Spring HATEOAS generates?

@dreambrother
Copy link
Author

Parameter came as URL encoded value, then spring-mvc automatically decodes it and if I pass it to the linkTo method, it throws strange exception.

@vivin
Copy link

vivin commented Apr 26, 2015

Yeah, I'd imagine that it should urlencode it on its own.

@odrotbohm odrotbohm self-assigned this May 21, 2015
@odrotbohm odrotbohm added this to the 0.18 milestone May 21, 2015
@odrotbohm odrotbohm modified the milestones: 0.18, 0.19 Jun 1, 2015
@odrotbohm odrotbohm removed this from the 0.19 milestone Aug 31, 2015
@SingleShot
Copy link

There appears no way around this. If one encodes the query parameter, it ends up double-encoded.

An example of how we are encountering this error:

linkTo(methodOn(SomeController.class).someMethod("id:{<123>, <456>}"))

Note this is not JSON - our domain has text with curly braces in it.

It appears Spring HATEOAS is trying to do template variable expansion on a value passed into the linkTo/methodOn controller signature.

@SingleShot
Copy link

We are just gonna do a workaround for this, which is to call methodOn with a null value:

linkTo(methodOn(SomeController.class).someMethod(null))

Then we call a method that knows about that particular parameters (in our case, it is called "query") and adds it into the URL manually:

Link addQueryToLink(String query, Link link) {

  final UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(link.getHref());

  if (query == null || query.isEmpty()) {
    builder.replaceQueryParam("query");
  } else {
    builder.replaceQueryParam("query", query);
  }

  return new Link(builder.build().encode().toUriString(), link.getRel());
}

Obviously this can be improved upon, made more generic, etc. but ideally we should not have to do it.

@wendelicious
Copy link

So, i hit this once per quarter or so, and it seems to always be a user error: I'll annotate the request method arguments as @PathParam. smh Should be @PathVariable. @PathVariable for the win.

fwiw

@gregturn
Copy link
Contributor

gregturn commented Jul 31, 2020

I just crafted a test case that has a controller like this:

@RestController
static class RequestParamBasedController {

	@GetMapping
	CollectionModel<FileSystemResource> listFiles(@RequestParam("q") String query) {

		Link self = linkTo(methodOn(RequestParamBasedController.class).listFiles(query)).withSelfRel();
		return CollectionModel.of(Collections.emptyList(), self);
	}
}

And the tests look like this:

@Test
void requestParamBasedLink() {

	Link link = linkTo(methodOn(RequestParamBasedController.class).listFiles("my search")).withSelfRel();
	assertThat(link.getHref()).isEqualTo("http://localhost?q=my%20search");
	assertThat(link.isTemplated()).isFalse();

	link = linkTo(methodOn(RequestParamBasedController.class).listFiles("{\"foo\": \"bar\"}")).withSelfRel();
	assertThat(link.getHref()).isEqualTo("http://localhost?q=%7B%22foo%22:%20%22bar%22%7D");
	assertThat(link.isTemplated()).isFalse();

	link = linkTo(methodOn(RequestParamBasedController.class).listFiles(null)).withSelfRel();
	assertThat(link.getHref()).isEqualTo("http://localhost?q={q}");
	assertThat(link.isTemplated()).isTrue();
}

Does this not create things properly now since #593.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants