I tried to solve the problem by coding a TC8 Valve to intercept the request, rewrite the URL without a trailing slash, and then have TC8 restart the request process. And all without losing any request content sent with, for example, a PUT request. I was able to intercept and rewrite the URL, but I never successfully got TC8 to restart the request. I was very close, but time ran out for further exploration. (I was also unsuccessful with a Filter implementation.)
Our services are fronted by haproxy and it does allow for URL rewriting. The following placed in a frontend or a backend definition will remove any trailing slashes in the URL's path:
acl has-trailing-slash path_end / reqrep ^(HEAD\ )(.*?)(\/+)(\?.*?)?(\ HTTP\/1.[01]) \1\2\4\5 if has-trailing-slash reqrep ^(GET\ )(.*?)(\/+)(\?.*?)?(\ HTTP\/1.[01]) \1\2\4\5 if has-trailing-slash reqrep ^(PUT\ )(.*?)(\/+)(\?.*?)?(\ HTTP\/1.[01]) \1\2\4\5 if has-trailing-slash reqrep ^(POST\ )(.*?)(\/+)(\?.*?)?(\ HTTP\/1.[01]) \1\2\4\5 if has-trailing-slash
One could replace the 4 rewrite rules with just one that handles all the HTTP methods but I chose not to: The leading 2 characters are enough to quickly select the right rule.