RareTyler
RareTyler

Reputation: 131

SpringBoot Consume REST source, HttpMessageConverter Error

I'm a beginner to web programming. I am interested because there are a lot of videogame API's out there that can be consumed as URLs.

I followed this guide and got it to work with springboot and maven, (https://spring.io/guides/gs/consuming-rest/)

Now I am trying to swap out their URL and add one from a videogame (http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item=21787)

The following code generates the following errors:

@SpringBootApplication
public class Application {

private static final Logger log = LoggerFactory.getLogger(Application.class);

public static void main(String args[]) {
    SpringApplication.run(Application.class);
}

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
    builder.additionalMessageConverters(new FakeConverter());
    return builder.build();
}

@Bean
public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
    return args -> {
        Quote quote = restTemplate.getForObject(
                "http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item=21787", Quote.class);
                //"http://gturnquist-quoters.cfapps.io/api/random", Quote.class);
        log.info(quote.toString());
    };
}



    java.lang.IllegalStateException: Failed to execute CommandLineRunner
   at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:801) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:782) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:769) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1185) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1174) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
at hello.Application.main(Application.java:25) [classes/:na]
 Caused by: org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class hello.Quote] and content type [text/html;charset=ISO-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:622) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at hello.Application.lambda$0(Application.java:37) [classes/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) [spring-boot-1.4.0.RELEASE.jar:1.4.0.RELEASE]
... 6 common frames omitted

It sounds like the format being returned by the URL link "[text/html;charset=ISO-8859-1]" isn't supported by the Spring RestTemplate. This strikes me as weird because that charset is a default in the Java Charset class, and going to the URL, the text looks like normal text.

I tried manually adding the StringHttpMessageConverter but that didn't work.

I thought maybe the code doesn't recognize the part where it says "text/html", so I made a 'FakeConverter' that wraps StringHttpMessageConverter as detailed below:

    public class FakeConverter implements HttpMessageConverter{

private StringHttpMessageConverter converter;
ArrayList<MediaType> list = new ArrayList();
public FakeConverter() {
    converter = new StringHttpMessageConverter(Charset.forName("ISO-8859-1"));
}

@Override
public boolean canRead(Class arg0, MediaType arg1) {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean canWrite(Class arg0, MediaType arg1) {
    // TODO Auto-generated method stub
    return true;
}

@Override
public List getSupportedMediaTypes() {
    // TODO Auto-generated method stub
    list.add(new MediaType("*", "text/html", Charset.forName("ISO-8859-1")));
    //list.add(new MediaType("text/html", "*", Charset.forName("ISO-8859-1")));
    //list.add(new MediaType("*", "*", Charset.forName("ISO-8859-1")));

    return list;
}

@Override
public Object read(Class arg0, HttpInputMessage arg1) throws IOException, HttpMessageNotReadableException {
    // TODO Auto-generated method stub
    return converter.read(arg0, arg1);
}

@Override
public void write(Object arg0, MediaType arg1, HttpOutputMessage arg2)
        throws IOException, HttpMessageNotWritableException {
    // TODO Auto-generated method stub
    converter.write((String)arg0, arg1, arg2);

}

}

I'm making canRead/canWrite always true to try and brute force it to read the URL, I tried all those different MediaTypes to see if I could get the program to support my URL. I read on a different post here that "*" functions as a wildcard in the MediaType class, so I was surprised to see the double wildcard attempt fail.

Now I am lost, and I thought it would be way easier to read a link.

Please offer any insight you have, and ask any clarifying questions you need to supply a better answer.

Thanks

Upvotes: 2

Views: 1185

Answers (1)

Seeker
Seeker

Reputation: 957

From a Spring point of view, none of the HttpMessageConverter instances registered with the RestTemplate can convert text/html content to a Quote object. The method of interest is HttpMessageConverter#canRead(Class, MediaType). The implementation for all of the above returns false, including GsonHttpMessageConverter,MappingJackson2MessageConverter etc.

Since no HttpMessageConverter can read your HTTP response, processing fails with an exception.

If you can modify the server response, modify it to set the Content-type to application/json

If you can't modify the server response, you'll need to write and register your own HttpMessageConverter (which can extend the Spring classes, see AbstractGenericHttpMessageConverterand its sub classes) that can read and convert text/html.

alternatively you can try adding support for additional media types to GsonHttpMessageConverter,

public class GsonHttpMessageConverterDemo extends GsonHttpMessageConverter {
    public GsonHttpMessageConverterDemo () {
        List<MediaType> types = Arrays.asList(
                new MediaType("text", "html", DEFAULT_CHARSET),
                new MediaType("application", "json", DEFAULT_CHARSET),
                new MediaType("application", "*+json", DEFAULT_CHARSET)
        );
        super.setSupportedMediaTypes(types);
    }
}

Upvotes: 1

Related Questions