I'm trying to integrate my app with ebay Finding API, after getting familiar with api, I tried making some requests to see if it's working as expected, but one thing got me stuck. The ebay service even though I explicitly set RESPONSE-DATA-FORMAT to JSON, returns response in json format but the content-type is text/plain. I set my restTemplate message converters as follows:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper)
{
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(objectMapper);
messageConverter.setSupportedMediaTypes(
ImmutableList
.of(
new MediaType("application", "json", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET),
new MediaType("text", "plain", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET)
));
return messageConverter;
}
#Bean
RestTemplate restTemplate(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter)
{
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(Collections.singletonList(mappingJackson2HttpMessageConverter));
return new RestTemplate();
}
Even though when I try to deserialize response which looks like this:
json data
And object:
public class Response
{
private String version;
public Response()
{
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
}
My Api call:
String url = uriBuilder.formEndpoint("iphone").toString();
Response response = restTemplate.getForObject(url, Response.class);
Is finishing with exception like this:
Could not extract response: no suitable HttpMessageConverter found for
response type [class com.domain.Response]
and content type [text/plain;charset=UTF-8]
Related
I have a request interceptor config for my feign client that i will like to verify if it is configured properly. It is suppose to make request to the auth url and get a authorization taken.
This seems to work fine. But i think its not putting it to every request sent to to the resource server. Hence i keep getting 403. but when i try this on postman with the auth token generated in my code it works fine.
Bellow is the code
#Component
public class FeignC2aSystemOAuthInterceptor implements RequestInterceptor {
#Value("${c2a.oauth2.clientId}")
private String clientId;
#Value("${c2a_system.authUrl}")
private String authUrl;
#Value("${c2a.oauth2.clientSecret}")
private String clientSecret;
private String jwt;
private LocalDateTime expirationDate = LocalDateTime.now();
private final RestTemplate restTemplate;
public FeignC2aSystemOAuthInterceptor(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#Override
public void apply(RequestTemplate requestTemplate) {
if (LocalDateTime.now().isAfter(expirationDate)) {
requestToken();
System.out.println("JUST AFTER REQUEST" + this.jwt);
}
/* use the token */
System.out.println("USE THE TOKEN" + this.jwt);
requestTemplate.header("Authorization: Bearer " + this.jwt);
}
private void requestToken() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("client_id", clientId);
map.add("client_secret", clientSecret);
map.add("grant_type", "client_credentials");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<C2AAuthResponse> response = restTemplate.postForEntity(authUrl, request, C2AAuthResponse.class);
this.jwt = Objects.requireNonNull(response.getBody()).getAccessToken();
LocalDateTime localDateTime = LocalDateTime.now();
this.expirationDate = localDateTime.plusSeconds(response.getBody().getExpiresIn());
}
config
#Configuration
public class FeignC2aSystemConfig {
#Bean
RestTemplate getRestTemplate() {
return new RestTemplate();
};
#Bean
FeignC2aSystemOAuthInterceptor fen () {
return new FeignC2aSystemOAuthInterceptor(getRestTemplate());
}
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
and client
#FeignClient(name = "c2aSystem", url = "${c2a_system.base_url}", configuration = FeignC2aSystemConfig.class)
public interface C2AApiClient {
#PostMapping(value = C2ASystemIntegrationUrls.SEND, produces = "application/json", consumes = "application/json")
HttpServletResponse sendSms(#RequestBody C2aMessage c2aMessage);
#GetMapping(value = C2ASystemIntegrationUrls.GETLIST, produces = "application/json", consumes = "application/json")
List<MessageData> getMessages();
}
during logging i have noticed that it i call the interceptor and i can see the auth token logged using sout.
Please i would like to know if i have made a mess somewhere along the way that might cause it not to apply the authorization token to the request, thanks
You're using the RequestTemplate API wrong in this line:
requestTemplate.header("Authorization: Bearer " + this.jwt);
the header method accepts 2 parameters. First a key and then the corresponding value, and there's an overload with a String vararg. Your code will complile because of the varag parameter but won't work because it'll be handled as an empty array argument.
The implementation in the RequestTemplate is clear. If the array is empty, it'll consider that header for removal.
The fix is easy, just put the JWT token into the second argument instead of concatenating it with the header key, like this:
requestTemplate.header("Authorization: Bearer ", this.jwt);
How to make get with body using rest template?
Based on question from: POST request via RestTemplate in JSON, I tried make GET with body via HttpEntity (just check if it is possible), but
it failed receiving:
Required request body is missing
For HttpMethod.POST: localhost:8080/test/post body is added correctly, but for
HttpMethod.GET localhost:8080/test/get it is not mapped.
My code is, as below:
#RestController
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
private final RestTemplate restTemplate = new RestTemplate();
#GetMapping("/test/{api}")
public SomeObject test(#PathVariable("api") String api) {
String input = "{\"value\":\"ok\"}";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(input, headers);
HttpMethod method = "get".equals(api) ? HttpMethod.GET : HttpMethod.POST;
String url = "http://localhost:8080/" + api;
return restTemplate.exchange(url, method, entity, SomeObject.class).getBody();
}
#GetMapping("/get")
public SomeObject getTestApi(#RequestBody(required = false) SomeObject someObject) {
return new SomeObject() {{ setValue(someObject != null ? "ok" : "error"); }};
}
#PostMapping("/post")
public SomeObject postTestApi(#RequestBody(required = false) SomeObject someObject) {
return new SomeObject() {{ setValue(someObject != null ? "ok" : "error"); }};
}
#Data
public static class SomeObject {
private String value;
}
}
Here is the repo with full example: https://gitlab.com/bartekwichowski/git-with-body
I wonder, what is wrong with code?
Also accorging to: HTTP GET with request body
GET with body is possible, but just not good practice.
I found this can't remeber where. Not a good practice, but if in your enviroment you have no other chance:
private static final class HttpComponentsClientHttpRequestWithBodyFactory extends HttpComponentsClientHttpRequestFactory {
#Override
protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
if (httpMethod == HttpMethod.GET) {
return new HttpGetRequestWithEntity(uri);
}
return super.createHttpUriRequest(httpMethod, uri);
}
}
private static final class HttpGetRequestWithEntity extends HttpEntityEnclosingRequestBase {
public HttpGetRequestWithEntity(final URI uri) {
super.setURI(uri);
}
#Override
public String getMethod() {
return HttpMethod.GET.name();
}
}
and when you get your restTemplate object
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestWithBodyFactory());
i had the same issue with RestTemplate and GET.
Tried to switch to Unirest but that also did not allow to use body with GET method.
Changing GET to POST is successful.
Making a call from postman after deploying in Liberty works fine and body did get accepted and expected response is generated.
i believe its something with the embedded tomcat server used.
I am trying to get the actual json i recieve as response from rest service.
My problem is that i have multiple message converters configured for my RestTemplate like this.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.setMessageConverters(getMessageConverters());
return restTemplate;
}
private List<HttpMessageConverter<?>> getMessageConverters() {
List<HttpMessageConverter<?>> converters = new LinkedList<>();
StringHttpMessageConverter textConverter = new StringHttpMessageConverter();
converters.add(textConverter);
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
converters.add(jsonConverter);
HttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
converters.add(formHttpMessageConverter);
return converters;
}
Here is the how i try to retrive the string from the response.
ResponseEntity<String> message = null;
try {
message = restTemplate.postForEntity(
url,
new HttpEntity<>(payload, getHeaders()),
String.class);
return message.getBody();
} catch (HttpClientErrorException e) {
log.error(e.getResponseBodyAsString(), e);
}
My guess is that because i have a Mapping2Jackson2HttpMessageConverter configured, each time i recieve message with content type application/json, it tries to convert it using json converter even if response type class is specified as String.
I am using spring 4.1 and my Rest Controller signature looks like:
#RestController
#RequestMapping("/api/device")
public class ApiRestController {
public ResponseEntity<Response> singleResponse() {
System.out.println("SDR GET");
SingleDataResponse res = new SingleDataResponse();
res.setCmd("8028");
res.setData("xyz");
res.setRfu("rfu");
res.setSid("99");
return new ResponseEntity<Response>(res, HttpStatus.OK);
}
#RequestMapping(value = "/single-res-post", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Response> singleResponsePost(
#RequestBody SingleDataRequest req, HttpServletRequest request,
HttpServletResponse response) {
System.out.println("SDR Post");
SingleDataResponse res = new SingleDataResponse();
res.setCmd(req.getCmd());
res.setRfu(req.getRfu());
res.setSid(req.getSid());
res.setData("0");
return new ResponseEntity<Response>(res, HttpStatus.OK);
}
}
I have added the below Bean in my WebMvcConfig
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
adapter.setCacheSecondsForSessionAttributeHandlers(0);
final MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJackson2HttpMessageConverter();
List<HttpMessageConverter<?>> httpMessageConverter = new ArrayList<HttpMessageConverter<?>>();
httpMessageConverter.add(mappingJacksonHttpMessageConverter);
String[] supportedHttpMethods = { "POST", "GET", "HEAD" };
adapter.setMessageConverters(httpMessageConverter);
adapter.setSupportedMethods(supportedHttpMethods);
return adapter;
}
I am using HttpClient for Get and Post to access the above rest api
I able to acess the Get Request but while sending the Post request I am getting the response
<!DOCTYPE html>
<html><head><title>Apache Tomcat/8.0.12 - Error report</title></head>
<body>
<h1>HTTP Status 405 - Request method 'POST' not supported</h1>
<p><b>message</b> <u>Request method 'POST' not supported</u></p>
<p><b>description</b> <u>The specified HTTP method is not allowed for the requested resource.</u></p>
</body></html>
Kindly suggest
The code used to send the post request
public static void verifyIreoWSPost() throws Exception {
String wms = "http://localhost:8080/test-rest/api/device/single-res-post";
Map<String, String> headers = new HashMap<String, String>();
String json = "{\"sid\":\"99\",\"rfu\":\"rfu\",\"cmd\":\"8028\",\"data\":\"xyz\"}";
headers.put(HttpHeaders.CONTENT_TYPE, "application/json");
headers.put("username", "admin");
headers.put("password", "admin");
String response = HttpUtils.sendPost(wms, headers, json);
System.out.println("post response: " + response);
}
When attempting to post to a Spring-Data-Rest web service via RestTemplate, the JSON representation of my domain object is being converted to a full blown JSON object that isn't in HAL representation. My assumption here is that I need to register the Jackson2HalModule as a deserializer though am not sure how to do that considering I register it to the objectMapper. The serialization works correctly when calling GET on the webservice, just not for POST/PUT:
Request outputBuffer field:
{
"id" : 1,
"name" : "Name",
"description" : "",
"childObject" : {
"id" : 1,
"name" : "test"
}
}
Rest Template configuration:
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setDateFormat(new ISO8601DateFormat());
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.registerModule(new Jackson2HalModule());
return objectMapper;
}
public void configureMessageConverters(
List<HttpMessageConverter<?>> messageConverters) {
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(objectMapper());
jsonMessageConverter.setSupportedMediaTypes(MediaType
.parseMediaTypes("application/hal+json,application/json"));
messageConverters.add(jsonMessageConverter);
}
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
configureMessageConverters(messageConverters);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
Request Headers:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Calling method:
ResponseEntity<DomainObject> responseEntity =
restTemplate.exchange(this.getBaseUri() + resolveResource(), HttpMethod.POST, new HttpEntity(domainObject,createHttpHeaders(tenantId)), DomainObject.class);
I think you should not register your own ObjectMapper. Spring creates it for you and also registers all the modules needed. So I would just try to remove your ObjectMapper bean.
If you need to customize the ObjectMapper you could use a Jackson2ObjectMapperBuilder. See the documentation for more details - http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return Jackson2ObjectMapperBuilder //
.json() //
.locale(ENGLISH) //
.timeZone("UTC") //
.indentOutput(true) //
.serializationInclusion(NON_NULL) //
.featuresToDisable(WRITE_DATES_AS_TIMESTAMPS, FAIL_ON_UNKNOWN_PROPERTIES) //
;
}
I would also let spring take care of the message converters:
So let spring inject them when you create the RestTemplate - so something like this:
#Bean
public RestTemplate restTemplate(List<HttpMessageConverter<?>> messageConverters) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}