I have a spring boot application and am testing integration test. My REST service produces JSON and I can confirm it when testing it in postman.
But when I make a getForObject call by restTemplate:
#Test
public void testGetObject() {
Pet pet = restTemplate.getForObject("http://localhost:9000/pets/10000", User.class, Collections.emptyMap());
assertThat(pet.getName(), is("Bobby"));
}
It fails with following error:
Could not extract response: no suitable HttpMessageConverter found for response type [class petstore.entity.User] and content type [text/html;charset=utf-8]
I read lots of posts in stackoverflow, and having that restTempalte itself has MappingJackson2HttpMessageConverter as one of default converters which has JSON as default media type then I should not get this error.
Is there anything I am missing here?
Well, the message is pretty indicative - you're getting text/html as a response type, make your endpoint return application/json. If you're using Spring MVC then you can do it by adding the produces parameter to the annotation:
#RequestMapping(value = "/pets/{id}", method = RequestMethod.GET, produces = "application/json")
Or, if you're using Jersey, add this annotation to your method:
#Produces("application/json")
Related
I'm working on a spring-boot (1.4.0-RELEASE) MVC Groovy app which will present an XML api. By default Spring seems to wire up Jackson which marshalls my response objects to JSON, however I want it to default to responding in XML without requiring any Accept header from clients, hence I configured the default content type as follows:
#Configuration
class SpringWebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_XML);
}
}
This works just fine, however when running our tests I discovered that calling /health now returns a 406 status code and no content (it previously returned a 200 and a JSON response).
Having reverted the above change I thought perhaps I could force each controller to explicitly set the response content type via the use of a ResponseEntity, in doing so I tried the following in my controller method:
#RequestMapping(value = "/blah",
method = RequestMethod.GET)
ResponseEntity<MyResponseObject> getProgrammeRestrictions(#PathVariable String coreNumber) {
// Generate response object (code snipped)...
new ResponseEntity<MyResponseObject>(myResponseObject,
new HttpHeaders(contentType: MediaType.APPLICATION_XML),
HttpStatus.OK)
}
However this doesn't seem to influence the response type, which still defaults to JSON.
In a nutshell it seems that setting a default non-json content type breaks the actuator healthcheck. Is there someway to force the healthcheck bits and bobs to disregard the default setting and always be generated in JSON?
Has anyone else experienced this? Grateful for any pointers as I'm a bit stuck here.
Many thanks,
Edd
You need to add jackson-dataformat-xml dependency:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
Then, make its XmlMapper available:
#Autowired
private MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter;
#Bean
public ObjectMapper objectMapper(){
// this returns an XmlMapper, which is a subclass of ObjectMapper
return mappingJackson2XmlHttpMessageConverter.getObjectMapper();
}
It works when sending a request from the browser (http://localhost:8080/health), the returned result is in XML (chrome sends the header Accept: */*).
When sending the request programmatically, you still have to pass Accept: application/json in your header since the service expects this media type, but the returned result will be XML.
Hi I am trying to write small app with REST Json. I have some method that returns ArrayList of entity objects. And I am doing that:
#RequestMapping(value="/workers/", method = RequestMethod.GET)
public #ResponseBody ArrayList<Workers> showAllEmployes() throws Exception
{
ArrayList<Workers> workers = new ArrayList<Workers>();
workers = (ArrayList<Workers>) spiroService.getAllWorkers();
return workers;
}
And after this I got:
HTTP Status 500. The server encountered an internal error that prevented it from fulfilling this request.
When I try to return primitive data type then all is ok. I have nothing in server logs. And I have necessary imports. Please some tip.
Seems you have issue in produce json format, try this.
#RequestMapping(value = "/workers/", method = RequestMethod.GET,
produces={MediaType.APPLICATION_JSON_VALUE})
I'm using a 3rd party REST API which is returning 'NaN' in it's JSON response :( I can't change the response.
I'm using Spring MVC with RestTemplate and the built in Message Converters to deserialize the JSON to an Object.
I was wondering, if there is a smart way of setting the JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS property to allow for the NaN in the response.
Because I have other message converters which I do not need to configure myself I'm currently doing the following:
List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJacksonHttpMessageConverter) {
ObjectMapper objectMapper = ((MappingJacksonHttpMessageConverter) converter).getObjectMapper();
objectMapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
}
}
This works but I don't like the fact that I'm a) iterating and b) doing the instanceof comparison just to set this property.
What would be a smarter/nicer way to do this?
Here are your options:
Instantiate and configure MappingJacksonHttpMessageConverter then set the message converters of your resttemplate to that instance. See RestTemplate.html#setMessageConverters(java.util.List). Note that this will remove all default message converters that are automatically instantiated when you instantiate a RestTemplate.
Use #JsonDeserialize. See http://dev.sghill.net/2012/04/how-do-i-write-jackson-json-serializer.html
I have a jersey client that I am trying to unmarshall a response entity with. The problem is the remote web service sends back application/octet-stream as the content type so Jersey does not know how to unmarshall it (I have similar errors with text/html coming back for XML and such). I cannot change the web service.
What I want to do is override the content-type and change it to application/json so jersey will know which marshaller to use.
I cannot register application/octet-stream with the json marshaller as for a given content type I actually might be getting back all kinds of oddities.
As laz pointed out, ClientFilter is the way to go:
client.addFilter(new ClientFilter() {
#Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, "application/json");
return getNext().handle(request);
}
});
I'm not well-versed in the Jersey client API, but can you use a ClientFilter to do this? Perhaps you could add a property to the request via ClientRequest.getProperties().put(String, Object) that tells the ClientFilter what Content-Type to override the response with. If the ClientFilter finds the override property, it uses it, otherwise it does not alter the response. I'm not sure if the ClientFilter is invoked prior to any unmarshalling though. Hopefully it is!
Edit (Have you tried something like this):
public class ContentTypeClientFilter implements ClientFilter {
#Override
public ClientResponse handle(ClientRequest request) throws ClientHandlerException {
final ClientResponse response = getNext().handle(request);
// check for overridden ContentType set by other code
final String overriddenContentType = request.getProperties().get("overridden.content.type");
if (overriddenContentType != null) {
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, overriddenContentType);
}
return response;
}
}
Under Java 8 and Jersey 2 you can do it with a lambda:
client.register((ClientResponseFilter) (requestContext, responseContext) ->
responseContext.getHeaders().putSingle("Content-Type", "application/json"));
Would appreciate any code examples of how to call a SpringWS endpoint intrceptor from a Junit test class. Particularly on how to prepare a SOAP message context and endpoint object. The SOAP message in the context will need to have a custom SOAP header included.
Something like....
public class MyInterceptorTest
private static String "... my XML SOAP test message ...";
#Test
public testMyInterceptor() {
myMessageContext = ... Build a MessageContext with the XML message string;
myEndPointObject = ... Build an endpoint object;
boolean result = MyInterceptorClass.handleRequest(myMessageContext, myEndPointObject);
... Check results;
}
Any examples would be appreciated.
The MessageContext can be created by instantiating a DefaultMessageContext object. The request WebServiceMessage can created using the test support class PayloadMessageCreator, but this only appeared in Spring-WS 2.x.
The endpoint object can be anything - it depends what your interceptor does with it. If it doesn't actually use it, then you can just pass in null.
I had the same issue and was able to figure it out in part using #skaffman's suggestion.
Basically, I had a custom EndpointInterceptor that I wanted to test with real data so that I would know I had everything correct.
You will have to upgrade spring-ws-test and other spring-ws dependencies to version 2.0 or higher. I ended up using something different than PayloadMessageCreator.
final Source payload = new StreamSource(new StringReader(soapPayload));
SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
WebServiceMessage requestPayload = new SoapEnvelopeMessageCreator(payload).createMessage(saajSoapMessageFactory);
MessageContext messageContext = new DefaultMessageContext(requestPayload, saajSoapMessageFactory);
soapPayload is the string value of an entire soap envelope.
Something similar to this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
...fill in your custom headers here
</soapenv:Header>
<soapenv:Body><someRequest>...</someRequest></soapenv:Body>
</soapenv:Envelope>
You will obviously need to fill in your request payload, any namespaces, as well as your custom headers.
I set the endpoint object to null as I was not doing anything with it as part of my interceptor.