Error 406 while using an email address as a path variable in Spring Boot
I came across an issue while developing a new endpoint on a Spring boot app. Basically, a REST endpoint with a GET mapping defined in a Spring controller was “occasionally” not doing what it was meant to do. And trust me no developer likes debugging intermittent software issues. Here’s what the path looked like:
/users/{email}
It was meant to return a regular JSON as response. When this endpoint was invoked with any email address, the portion of the email after . was being truncated. So foo@bar.com would become foo@bar . Weird, why would Spring do this! After giving it more thought it made sense. The reason they did this was for requesting resources with path extensionslike /documents/foo.json ,/documents/foo.xml etc.
After some investigation I found that the reason for the problem. It was happening because Spring considers the portion of the parameter after . as an extension and treats it differently. Note that this only happens when the path ends with the parameter. This means that if we had something like \users\{email}\activity with email foo@bar.com, we wouldn’t have encountered this issue since the path doesn’t end with .com which is where the problem lies.
The fix for this was pretty straight forward. (this was one of the multiple ways to fix this) All we had to do is change the path to :
\users\{email:.+}
What we’re saying here is - hey Spring consider everything after \users\ to be part of the email parameter. And we’ve used a regex to denote that. Code tested with a few email addresses, reviewed and deployed to production, happy days!
Or not. Another intermittent issue, this time on production. Some requests to the same endpoint throw an error with status 406 Not Acceptable . This particularly happened to emails ending with .au in our case. This time it was driving us crazy. We’ve added a handle for the truncation part, what else could possibly go wrong?! Turns out that this time the controller was being invoked and processed fine, however Spring did something different before returning the response. Because au also happens to be an audio file format, Spring performed a URL based Content Negotiation . In other words, Spring tried to return content of type au which it obviously didn’t find. There are other file extensions which would cause the same issue.
The fix:
Added the following method in the Spring boot configuration file (annotated with @Configuration )
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}Here we’re telling Spring to not favour path extensions (as the code suggests). There are more configs that can be added here, like adding a default content type to use. Refer to the docs for more details.
Lesson learnt — DO NOT use email addresses on path variables (We had to use it because of the way data was structured in the project).
While Spring offers a lot of features for enterprise software development, it comes with some complexity. Sigh. An interesting afternoon. Happy coding!
