Search code examples
spring-mvcspring-bootspring-cloudspring-cloud-config

spring cloud config-server .properties content-negotiation failing


For me the content-negotiation mechanism of the e.g. for .properties is not working in Spring Cloud Config Server after switching to Spring Cloud Brixton.RC1 or Brixton.RC2 from Angel.SR5 and Angel.SR6.

The problem occurs when im starting the service using gradlew bootRun or java -jar .... It's working in my integration tests though (see Working Integration-Test below).

Usage Scenario:

I want to access the configuration in profile testing of application my-service, so I'm calling http://localhost:8888/my-service/testing.properties.

Expected result:

some.property=1234
some.other.property=hello there

Actual result:

Without Accept-Header:

<!DOCTYPE html>
<html>
    <head>
        <title>Error 406</title>
    </head>
    <body>
        <h1>Error 406: Not Acceptable</h1>
        <br/>
        Could not find acceptable representation
    </body>
</html>

With Accept-Header application/json:

{
    "timestamp": 1461140158009,
    "status": 406,
    "error": "Not Acceptable",
    "exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
    "message": "Could not find acceptable representation",
    "path": "/config/my-service/default.properties"
}

As you tell from the example, the content-negotiation mechanism seems to be working for error-handling but for the configuration access it is not.

Working Integration-Test:

I wrote the following Spock-Test

@WebIntegrationTest({"server.port=0", "management.port=0"})
@ActiveProfiles("testing")
@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = ConfigurationServiceApplication.class)
class ConfigurationAccessTest extends Specification {

    @Autowired
    TestserverInfo testserverInfo

    def "testing profile returns testing properties"() {
        given:
        RestTemplate rest = new TestRestTemplate(null, null)
        Properties properties = new Properties()

        when:
        String result = rest.getForObject( testserverInfo.getBasePath() + "/my-service-testing.properties", String.class );
        properties.load( new StringReader(result) )

        then:
        properties['test'] == 'Testing Profile World'
        properties['my.long.testing.property'] == 'Testing Profile Property'
    }

Things I already did so far:

  1. Wrote a the above Spock-Test for this scenario that is working in all noted versions of Spring Cloud Config Server
  2. Check ConfigServerMvcConfiguration for any obvious configuration errors as far as my knowledge of content-negotiation in Spring MVC went
  3. Provide a WebMvcConfigurer myself and initializing content-negotiation as in the configuration class referenced above:

    @EnableWebMvc
    @Configuration
    public class ConfigMvcConfiguration extends WebMvcConfigurerAdapter {
        private final Logger logger =  LoggerFactory.getLogger(ConfigMvcConfiguration.class);
    
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            configurer.mediaType("properties", MediaType.valueOf("text/plain"));
            configurer.mediaType("yml", MediaType.valueOf("text/yaml"));
            configurer.mediaType("yaml", MediaType.valueOf("text/yaml"));
            logger.info("media-types added");
        }
    }
    

Can anyone reproduce this issue or provide me any guidance on how to resolve it?


Solution

  • After all the ruckus with writing the question I realized: I was hunting a ghost. While checking the integration-test I wrote myself I found out, that I was trying to access the properties using a wrong url-schema.

    TLDR

    I was trying to access the properties using the URL

    http://localhost:8888/config/my-service/testing.properties   <-- wrong
    rather than
    http://localhost:8888/config/my-service-testing.properties   <-- correct
    

    Details

    One can access the properties in JSON format using the following url schema (when not using labels):

    http://<host>:<port>/<contextPath>/<application>/<profile>
    

    If one wants to access the properties in a format other than json, she has to use another url-schema (again example without labels)

    http://<host>:<port>/<contextPath>/<application>-<profile>.<format>
    

    with format beeing properties/yml/yaml as currently supported formats.

    Once again a small mistake that can complicate things alot.