Final Edit
So after researching this because the answer's didn't quite make since with what I was seeing, I found out that Spring does some weird stuff with extension matching. If I submit a request like this byNameOrAtlName/myStringHere.1
or this byNameOrAtlName/myStringHere.12
everything is fine, but byNameOrAtlName/myStringHere.123
causes it to break as does byNameOrAtlName/myStringHere.com
, but byNameOrAtlName/myStringHere.co
is ok, but byNameOrAtlName/myStringHere.c
is not.
In summary, I have no clue what logic spring is using to determine extension stuff, but for certain extensions the {varName:.+}
works as a sort of work around, but it looks like you need to completely disable dot file suffix to really get around it.
Using Spring 4.1.6
Spring is throwing the following exception org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
I have the following code, and everything works unless the string (name) has a period in it.
@RestController
@RequestMapping(value = "/foo/")
public class Testing{
@RequestMapping(value = "byNameOrAltName/{name:.+}", method = RequestMethod.GET)
@Transactional(readOnly = true)
public Collection<MyDTO> getByNameOrAltNAme(@PathVariable("name") String name) {
return myRepo.getMyDTOsByNameOrAtlName(name);
}
}
So this works. http://localhost:8080/data/foo/byNameOrAtlName/myStringHere
, but if I do this it fails http://localhost:8080/data/foo/byNameOrAtlName/myStringHere.fluffy
I read the other answers around this exception, but none of them seemed to apply to my case. And at first I thought it was the problem where spring doesn't like period to be in the path, and I fixed that before with the regex .+
but I tried it both with and without the regex and I get the same error.
Any ideas as to why Spring would throw this?
EDIT
Here is my debug log:
21:32:09,118 DEBUG work.orm.jpa.support.OpenEntityManagerInViewFilter: 161 - Opening JPA EntityManager in OpenEntityManagerInViewFilter
21:32:09,118 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/r2d2/**'
21:32:09,118 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 1 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
21:32:09,118 DEBUG y.web.context.HttpSessionSecurityContextRepository: 192 - Obtained a valid SecurityContext from SPRING_SECURITY_CONTEXT: 'org.springframework.security.core.context.SecurityContextImpl@3eda7134: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@3eda7134: REDACTED
21:32:09,122 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 2 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
21:32:09,122 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
21:32:09,122 DEBUG ework.security.web.header.writers.HstsHeaderWriter: 128 - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@518864fd
21:32:09,122 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
21:32:09,122 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/logout'
21:32:09,122 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 5 of 11 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
21:32:09,122 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 131 - Request 'GET /data/restaurant/supplier/bynameoraltname/quill.com' doesn't match 'POST /login
21:32:09,122 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 6 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
21:32:09,122 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 7 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
21:32:09,122 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 8 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
21:32:09,122 DEBUG y.web.authentication.AnonymousAuthenticationFilter: 106 - SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken@3eda7134: REDACTED
21:32:09,123 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 9 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
21:32:09,123 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 10 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
21:32:09,123 DEBUG org.springframework.security.web.FilterChainProxy: 324 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/login.html'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/index.html'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/*/css/**'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/*/fonts/**'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/*/img/**'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/*/lib/**'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/*/vendor/**'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/sales/**'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/registration/termsofservice.html'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/registration/privacypolicy.html'
21:32:09,123 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/restaurantui/useractivation.html**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/restaurantui/index.html'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/basicuseractivation/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/orderconfirmation/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/supplieruseractivation/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/restaurantuseractivation/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/useractivation/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/user/passwordresetrequest'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/user/changepasswordfortoken/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/user/checkpasswordtokenvalidity/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/signup/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/downloads/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/unsubscribe.html'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/restaurant/emailsubscriptions/unsubscribe/**'
21:32:09,124 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/adminui/**'
21:32:09,125 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/admin*'
21:32:09,125 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/confirm/**'
21:32:09,125 DEBUG rk.security.web.util.matcher.AntPathRequestMatcher: 151 - Checking match of request : '/data/restaurant/supplier/bynameoraltname/quill.com'; against '/data/**'
21:32:09,125 DEBUG ity.web.access.intercept.FilterSecurityInterceptor: 218 - Secure object: FilterInvocation: URL: /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116; Attributes: [isAuthenticated()]
21:32:09,125 DEBUG ity.web.access.intercept.FilterSecurityInterceptor: 347 - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@3eda7134: REDACTED
21:32:09,126 DEBUG ingframework.security.access.vote.AffirmativeBased: 65 - Voter: org.springframework.security.web.access.expression.WebExpressionVoter@3b97d645, returned: 1
21:32:09,126 DEBUG ity.web.access.intercept.FilterSecurityInterceptor: 242 - Authorization successful
21:32:09,126 DEBUG ity.web.access.intercept.FilterSecurityInterceptor: 255 - RunAsManager did not change Authentication object
21:32:09,126 DEBUG org.springframework.security.web.FilterChainProxy: 309 - /data/restaurant/supplier/byNameOrAltName/quill.com?_=1434072729116 reached end of additional filter chain; proceeding with original chain
21:32:09,126 DEBUG org.springframework.web.servlet.DispatcherServlet: 861 - DispatcherServlet with name 'Spring MVC Servlet' processing GET request for [/data/restaurant/supplier/byNameOrAltName/quill.com]
21:32:09,126 DEBUG mvc.method.annotation.RequestMappingHandlerMapping: 294 - Looking up handler method for path /restaurant/supplier/byNameOrAltName/quill.com
21:32:09,127 DEBUG mvc.method.annotation.RequestMappingHandlerMapping: 299 - Returning handler method [public java.util.Collection<com.siftit.webservices.personae.restaurant.dtos.SupplierDTO> com.siftit.webservices.personae.restaurant.RestaurantSupplierWebService.getByNameOrAltNAme(java.lang.String)]
21:32:09,128 DEBUG k.beans.factory.support.DefaultListableBeanFactory: 248 - Returning cached instance of singleton bean 'restaurantSupplierWebService'
21:32:09,128 DEBUG org.springframework.web.servlet.DispatcherServlet: 947 - Last-Modified value for [/data/restaurant/supplier/byNameOrAltName/quill.com] is: -1
21:32:09,128 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,128 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 367 - Creating new transaction with name [com.siftit.webservices.security.WebServiceInterceptor.preHandle]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
21:32:09,128 DEBUG rg.springframework.jdbc.datasource.DataSourceUtils: 153 - Setting JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@624e4a40] read-only
21:32:09,128 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 403 - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@6d32e04]
21:32:09,128 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,129 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 472 - Participating in existing transaction
21:32:09,129 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,129 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 472 - Participating in existing transaction
21:32:09,140 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 755 - Initiating transaction commit
21:32:09,140 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 512 - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356]
21:32:09,141 DEBUG rg.springframework.jdbc.datasource.DataSourceUtils: 222 - Resetting read-only flag of JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@624e4a40]
21:32:09,141 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 605 - Not closing pre-bound JPA EntityManager after transaction
21:32:09,142 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,142 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 367 - Creating new transaction with name [com.siftit.webservices.personae.restaurant.RestaurantSupplierWebService.getByNameOrAltNAme]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
21:32:09,142 DEBUG rg.springframework.jdbc.datasource.DataSourceUtils: 153 - Setting JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@624e4a40] read-only
21:32:09,142 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 403 - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@32689afc]
21:32:09,143 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,143 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 472 - Participating in existing transaction
21:32:09,143 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,143 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 472 - Participating in existing transaction
21:32:09,144 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,144 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 472 - Participating in existing transaction
21:32:09,144 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 334 - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356] for JPA transaction
21:32:09,144 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 472 - Participating in existing transaction
21:32:09,146 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 755 - Initiating transaction commit
21:32:09,146 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 512 - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@1d65b356]
21:32:09,146 DEBUG rg.springframework.jdbc.datasource.DataSourceUtils: 222 - Resetting read-only flag of JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@624e4a40]
21:32:09,147 DEBUG org.springframework.orm.jpa.JpaTransactionManager: 605 - Not closing pre-bound JPA EntityManager after transaction
21:32:09,147 DEBUG ethod.annotation.ExceptionHandlerExceptionResolver: 134 - Resolving exception from handler [public java.util.Collection<com.siftit.webservices.personae.restaurant.dtos.SupplierDTO> com.siftit.webservices.personae.restaurant.RestaurantSupplierWebService.getByNameOrAltNAme(java.lang.String)]: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
21:32:09,147 DEBUG k.beans.factory.support.DefaultListableBeanFactory: 248 - Returning cached instance of singleton bean 'globalWebServiceExceptionHandler'
21:32:09,147 DEBUG ethod.annotation.ExceptionHandlerExceptionResolver: 360 - Invoking @ExceptionHandler method: public java.lang.Object com.siftit.webservices.GlobalWebServiceExceptionHandler.defaultErrorHandler(javax.servlet.http.HttpServletRequest,java.lang.Exception) throws java.lang.Exception
21:32:09,155 DEBUG thod.annotation.RequestResponseBodyMethodProcessor: 163 - Written [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation] as "application/x-msdownload" using [org.springframework.http.converter.StringHttpMessageConverter@214c8f9d]
21:32:09,155 DEBUG org.springframework.web.servlet.DispatcherServlet:1034 - Null ModelAndView returned to DispatcherServlet with name 'Spring MVC Servlet': assuming HandlerAdapter completed request handling
21:32:09,155 DEBUG org.springframework.web.servlet.DispatcherServlet: 996 - Successfully completed request
21:32:09,155 DEBUG ork.security.web.access.ExceptionTranslationFilter: 116 - Chain processed normally
21:32:09,155 DEBUG urity.web.context.SecurityContextPersistenceFilter: 105 - SecurityContextHolder now cleared, as request processing completed
21:32:09,155 DEBUG work.orm.jpa.support.OpenEntityManagerInViewFilter: 186 - Closing JPA EntityManager in OpenEntityManagerInViewFilter
21:32:09,156 DEBUG .springframework.orm.jpa.EntityManagerFactoryUtils: 432 - Closing JPA EntityManager
Here are my request headers:
Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Cookie:JSESSIONID=BLABLABLABLA; subscriptionType=PREMIUM; userType=ROLE_ADMIN
DNT:1
Host:localhost:8080
Pragma:no-cache
Referer:http://localhost:8080/restaurantui/
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36
Spring is treating the part after the dot as a file suffix to try to determine what response type you actually want. Here is a decent writeup on content negotiation in Spring.
So what happens is that Spring is trying to present the result to in a content type it can't find a converter to.
To solve this you need to tell spring to turn off suffix-based content negotiation:
@Configuration
public class ContentNegotiationConfig extends WebMvcConfigurerAdapter {
@Override
void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
// Turn off suffix-based content negotiation
configurer.favorPathExtension(false);
}
}
Update:
I've dug a bit deeper and I think I can explain what's happening.
The default configuration ignores unknown path suffixes, so to explain this we need to know how Spring determines that a path suffix is unknown and that boils down to this piece of code in PathExtensionContentNegotiationStrategy
:
@Override
protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
throws HttpMediaTypeNotAcceptableException {
if (this.useJaf) {
MediaType jafMediaType = JafMediaTypeFactory.getMediaType("file." + extension);
if (jafMediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(jafMediaType)) {
return jafMediaType;
}
}
if (!this.ignoreUnknownExtensions) {
throw new HttpMediaTypeNotAcceptableException(getAllMediaTypes());
}
return null;
}
So what's happening is likely that the Java Activation Framework is recognising some of your suffixes and returning a media type for them - the .c extension probably returns text/x-c since that's causing an exception.