ResponseHandler<RedditRequestResponse> rh = new ResponseHandler<RedditRequestResponse>() {
#Override
public RedditRequestResponse handleResponse(
final HttpResponse response) throws IOException, RedditException {
int statusCode = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new ClientProtocolException("Response contains no content");
}
String responseBody = EntityUtils.toString(entity);
if (statusCode != HttpStatus.SC_OK) {
throw new RedditException(generateErrorString(statusCode, input, responseBody));
}
return new RedditRequestResponse(statusCode, responseBody);
}
};
I'd like to throw my own RedditException from handleResponse() that includes the response body. However, when I try that (like in the above code) I get a RedditException not compatible with throws clause error. I suspect this has something to do with the #Override. What exactly is being overridden? The class that contains this method does not inherit from any other class and does not implement any interfaces that I can tell.
If you make your RedditException extend an IOException then you will be able to get around it by keeping the same method signature
Related
I am trying to return the content of a Json file. But I want to modify before sending it to the front end. I want to add "[" and "]" at the beginning and end of the file. I am doing that because the json file has multiple json root elements.
Like for example extract the result as illustrated in
result = restTemplate.executeRequest(HttpMethod.GET, String.class);
//change Body and put it back in result
Question
Is it possible to change the body of the response and put it back in ResponseEntity?
Source Code
public ResponseEntity<String> getScalityObject(String chainCode, String dataCenter, String path, String byteRange) {
Map<String, Object> queryParams = new HashMap<>();
if (dataCenter != null && !dataCenter.isEmpty()) {
queryParams.put("dataCenter", dataCenter);
}
if (byteRange != null && !byteRange.isEmpty()) {
queryParams.put("byteRange", byteRange);
}
String decodedStr = URLDecoder.decode(path);
queryParams.put("path", decodedStr);
reservationService.setContext(
RESA_INTERNAL_SERVICE_NAME,
queryParams,
"/chains/{chainCode}/objects/file",
chainCode);
restTemplate.setServiceDefinition(reservationService);
ResponseEntity<String> result;
try {
result = restTemplate.executeRequest(HttpMethod.GET, String.class);
//Change responseBody here
return result;
} catch (IOException e) {
e.printStackTrace();
result = new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return result;
}
public <T> ResponseEntity<T> executeRequest(HttpMethod method, Class<T> responseType) throws IOException {
if (this.serviceDefinition == null) {
throw new IllegalArgumentException("You haven't provided any service definition for this call. " +
"Are you sure you called the right method before using this Amadeus Rest Template?");
}
// Resolve the URI
URI url = this.serviceDefinition.getUriComponents().toUri();
// Add the extra headers if necessary
HttpHeaders headers = new HttpHeaders();
if (this.serviceDefinition.getHeaders() != null) {
for(Map.Entry<String,String> headerSet : this.serviceDefinition.getHeaders().entrySet()) {
headers.put(headerSet.getKey(), Arrays.asList(headerSet.getValue()));
}
}
HttpEntity entity = new HttpEntity(headers);
ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
RequestCallback requestCallback = httpEntityCallback(entity, responseType);
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
throw ex;
}
finally {
if (response != null) {
response.close();
}
}
}
One of the way which I can think of is :
ResponseEntity<String> result = restTemplate.executeRequest(HttpMethod.GET, String.class);
StringBuilder builder = new StringBuilder(result.getBody());
... //do your transformation to stringbuilder reference.
result = ResponseEntity.status(result.getStatusCode()).body(builder.toString());
Another way if you want to avoid this is to return String response from your executeRequest & modify that response before creating ResponseEntity.
Try this:
Create your own HttpMessageConverter, implementing:
public interface HttpMessageConverter<T> {
// Indicates whether the given class can be read by this converter.
boolean canRead(Class<?> clazz, MediaType mediaType);
// Indicates whether the given class can be written by this converter.
boolean canWrite(Class<?> clazz, MediaType mediaType);
// Return the list of {#link MediaType} objects supported by this converter.
List<MediaType> getSupportedMediaTypes();
// Read an object of the given type form the given input message, and returns it.
T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
// Write an given object to the given output message.
void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
Register the custom converter into your restTemplate object:
String url = "url";
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the String message converter
restTemplate.getMessageConverters().add(new YourConverter());
// Make the HTTP GET request, marshaling the response to a String
String result = restTemplate.getForObject(url, String.class);
I am trying to create a method with generics to unmarshal JSON lists into lists containing POJOs. The snippet below compiles and runs, but at runtime I am getting my List<CustomPojo> filled with HashMap instances, as the type T is not passed along to the TypeReference constructor which then falls back to HashMap I guess.
public static <T> List<T> getList(String endpoint) throws IOException {
HttpGet request = new HttpGet(SERVER_ADDRESS + endpoint);
CloseableHttpResponse response = httpClient.execute(request);
try {
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() == 200) {
return mapper.readValue(response.getEntity().getContent(), new TypeReference<List<T>>() { });
}
} finally {
response.close();
}
return null;
}
Am I on the right track or is this something not achievable using generics?
Try to use something like:
mapper.readValue(response.getEntity().getContent(),
mapper.getTypeFactory().contructCollectionType(List.class, cls);
where cls is Class<T> it should works.
Before I start: I know that the child node inherits the namespace from the parent node and that's why my problem occurs. Unfortunately, the webservice I am sending my XML doesn't accept the child node without the namespace and, as it is a government entity, a change in their part is rather unlikely.
That being said, I am using Spring-WS to make the communication between my application and the webservice, so in one way or the other the framework uses a transformer to parse my payload Source to the framework's payload Result:
transformer.transform(Source, Result);
Before that transformation take place, my XML has these two nodes like it follows here:
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
After the transformation, the second namespace is removed(as I said before, I know the reason):
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<NFe>
I am also aware that I can use marshallers to achieve the same result and writing the parse code myself. Using that approach is also ok and would be acceptable, but I don't know any other way to achieve the same thing (transforming the javax.xml.transform.Source into javax.xml.transform.Result) using other approach besides the one listed above.
I have two questions then:
1 - Can I avoid the behaviour I am having with the default approach(without using marshallers)?
2 - Is there any other tool that would make the same transformation?
I've been through the same trouble. Unfortunately (or not) WebServiceTemplate with implementation of SOAPMessageFactory (such as SaajSoapMessageFactory) will do everything possible to assure you are sending a well-formed XML as a request by tying you to the Transformers from Source to Result, including never let you repeat 'xmlns' in children when you already did in parent. You have a couple of elegant options to try - what doesn't mean they're the simplest ones. You can work at XML level by using javax.xml.ws.Service and Dispatch interface, which is quite easy if you don't need SSL authentication. Check these links out (first one is written in Pt-BR):
http://www.guj.com.br/t/nfe-v2-00-veja-como-consumir-o-ws/297304
https://alesaudate.wordpress.com/2010/08/09/how-to-dynamically-select-a-certificate-alias-when-invoking-web-services/
Also you can try another message factory, such as DomPoxMessageFactory. This link might be useful:
http://forum.spring.io/forum/spring-projects/web-services/128221-webservicetemplate-get-it-to-stop-adding-soap-envelope
However, if changing the structure of your project isn't an option (which was my case), I have a workaround for you. Yes, a workaround, but once the target webservice IS EXPECTING a malformed XML, I absolve myself :D
I just created abstractions of HttpComponentsMessageSender and HttpComponentsConnection classes, the second one is instantiated through the first one's method createConnection(URI uri). So I can create my WebServiceTemplate like this:
WebServiceTemplate wst = new WebServiceTemplate(new SaajSoapMessageFactory());
wst.setMessageSender(new CustomHttpComponentsMessageSender());
Sadly you'll need to reply the createConnecion method to the new abstraction just to instantiate the custom connection. As I said, it's a workaround!
#Override
public WebServiceConnection createConnection(URI uri) throws IOException {
HttpPost httpPost = new HttpPost(uri);
if (isAcceptGzipEncoding()) {
httpPost.addHeader(HttpTransportConstants.HEADER_ACCEPT_ENCODING,
HttpTransportConstants.CONTENT_ENCODING_GZIP);
}
HttpContext httpContext = createContext(uri);
return new CustomHttpComponentsConnection(getHttpClient(), httpPost, httpContext);
}
The message is effectively sent inside the method onSendAfterWrite(WebServiceMessage message) of the HttpComponentsConnection class I'm abstracting from. Surprisingly, the 'message' parameter isn't used inside the method. It's there only for inheritance rules. And the good news: It's a protected method. The downside, again, is that I need to copy almost the entire class in order to change only this method, once the fields has no public visibility, and framework will need them in response handling. So, I'll post my entire class down:
public class CustomHttpComponentsConnection extends HttpComponentsConnection {
private final HttpClient httpClient;
private final HttpPost httpPost;
private final HttpContext httpContext;
private HttpResponse httpResponse;
private ByteArrayOutputStream requestBuffer;
protected CustomHttpComponentsConnection(HttpClient httpClient, HttpPost httpPost, HttpContext httpContext) {
super(httpClient, httpPost, httpContext);
Assert.notNull(httpClient, "httpClient must not be null");
Assert.notNull(httpPost, "httpPost must not be null");
this.httpClient = httpClient;
this.httpPost = httpPost;
this.httpContext = httpContext;
}
public HttpResponse getHttpResponse() {
return httpResponse;
}
public HttpPost getHttpPost() {
return httpPost;
}
#Override
protected OutputStream getRequestOutputStream() throws IOException {
return requestBuffer;
}
#Override
protected void onSendBeforeWrite(WebServiceMessage message) throws IOException {
requestBuffer = new ByteArrayOutputStream();
}
#Override
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
OutputStream out = getRequestOutputStream();
String str = out.toString();
str = str.replaceAll("<NFe>", "<NFe xmlns=\"http://www.portalfiscal.inf.br/nfe\">");
ByteArrayOutputStream bs = new ByteArrayOutputStream();
bs.write(str.getBytes());
getHttpPost().setEntity(new ByteArrayEntity(bs.toByteArray()));
requestBuffer = null;
if (httpContext != null) {
httpResponse = httpClient.execute(httpPost, httpContext);
}
else {
httpResponse = httpClient.execute(httpPost);
}
}
#Override
protected int getResponseCode() throws IOException {
return httpResponse.getStatusLine().getStatusCode();
}
#Override
protected String getResponseMessage() throws IOException {
return httpResponse.getStatusLine().getReasonPhrase();
}
#Override
protected long getResponseContentLength() throws IOException {
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
return entity.getContentLength();
}
return 0;
}
#Override
protected InputStream getRawResponseInputStream() throws IOException {
HttpEntity entity = httpResponse.getEntity();
if (entity != null) {
return entity.getContent();
}
throw new IllegalStateException("Response has no enclosing response entity, cannot create input stream");
}
#Override
public Iterator<String> getResponseHeaderNames() throws IOException {
Header[] headers = httpResponse.getAllHeaders();
String[] names = new String[headers.length];
for (int i = 0; i < headers.length; i++) {
names[i] = headers[i].getName();
}
return Arrays.asList(names).iterator();
}
#Override
public Iterator<String> getResponseHeaders(String name) throws IOException {
Header[] headers = httpResponse.getHeaders(name);
String[] values = new String[headers.length];
for (int i = 0; i < headers.length; i++) {
values[i] = headers[i].getValue();
}
return Arrays.asList(values).iterator();
}
Again, this is the easiest way I found when changing project structure is not an option. Hope this helps.
I don't think then there is any other approach for your transformation. As you know marshallers are the best practice to use in these scenarios. Better to use JAXB.
With Apache HttpClient, it's possible to manipulate the retrieved content by adding a HttpResponseIntercepter. With this it is quite easy to add header attributes. But how to manipulate the content of the retrieved HttpEntitys?
As example I like to convert all Text to Uppercase.
#Test
public void shoudConvertEverythingToUpperCase() throws ClientProtocolException, IOException
{
final DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
defaultHttpClient.addResponseInterceptor(new HttpResponseInterceptor() {
#Override
public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException
{
final HttpEntity entity = response.getEntity();
final HttpEntity upperCaseEntity = makeAllUppercase(entity);
response.setEntity(upperCaseEntity);
}
private HttpEntity makeAllUppercase(final HttpEntity entity)
{
// how to uppercase everything and return the cloned HttpEntity
return null;
}
});
final HttpResponse httpResponse = defaultHttpClient.execute(new HttpGet("http://stackoverflow.com"));
assertTrue(StringUtils.isAllUpperCase(EntityUtils.toString(httpResponse.getEntity())));
}
private HttpEntity makeAllUppercase(final HttpEntity entity)
{
Header h = entity.getContentType();
ContentType contentType = h != null ? ContentType.parse(h.getValue()) : ContentType.DEFAULT_TEXT;
String content = EntityUtils.toString(entity, contentType.getCharset());
return new StringEntity(content.toUpperCase(Locale.US), contentType);
}
This is not the most efficient due to intermediate buffering of content in memory but the most concise implementation.
Okay, I have a class NamedSystems, that has as its only field a Set of NamedSystem.
I have a method to find NamedSystems by certain criteria. That's not really important. When it gets results, everything works fine. However, when it can't find anything, and thus returns a null (or empty -- I've tried both ways) set, I get problems. Let me explain.
I'm using the Spring RestTemplate class and I'm making a call like this in a unit test:
ResponseEntity<?> responseEntity = template.exchange(BASE_SERVICE_URL + "?
alias={aliasValue}&aliasAuthority={aliasAssigningAuthority}",
HttpMethod.GET, makeHttpEntity("xml"), NamedSystems.class,
alias1.getAlias(), alias1.getAuthority());
Now, since this would normally return a 200, but I want to return a 204, I have an interceptor in my service that determines if a ModelAndView is a NamedSystem and if its set is null. If so, I then the set the status code to NO_CONTENT (204).
When I run my junit test, I get this error:
org.springframework.web.client.RestClientException: Cannot extract response: no Content-Type found
Setting the status to NO_CONTENT seems to wipe the content-type field (which does make sense when I think about it). So why is it even looking at it?
Spring's HttpMessageConverterExtractor extractData method:
public T extractData(ClientHttpResponse response) throws IOException {
MediaType contentType = response.getHeaders().getContentType();
if (contentType == null) {
throw new RestClientException("Cannot extract response: no Content-Type found");
}
for (HttpMessageConverter messageConverter : messageConverters) {
if (messageConverter.canRead(responseType, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + responseType.getName() + "] as \"" + contentType
+"\" using [" + messageConverter + "]");
}
return (T) messageConverter.read(this.responseType, response);
}
}
throw new RestClientException(
"Could not extract response: no suitable HttpMessageConverter found for response type [" +
this.responseType.getName() + "] and content type [" + contentType + "]");
}
Going up the chain a bit to find out where that Extractor is set, I come to RestTemplate's exchange() method that I used in the test:
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException {
HttpEntityRequestCallback requestCallback = new HttpEntityRequestCallback(requestEntity, responseType);
ResponseEntityResponseExtractor<T> responseExtractor = new ResponseEntityResponseExtractor<T>(responseType);
return execute(url, method, requestCallback, responseExtractor, uriVariables);
}
So, it's trying to convert what amounts to nothing because of the supplied response type from the exchange call. If I change the responseType from NamedSystems.class to null, it works as expected. It doesn't try to convert anything. If I had tried to set the status code to 404, it also executes fine.
Am I misguided, or does this seem like a flaw in RestTemplate? Sure, I'm using a junit right now so I know what's going to happen, but if someone is using RestTemplate to call this and doesn't know the outcome of the service call, they would naturally have NamedSystems as a response type. However, if they tried a criteria search that came up with no elements, they'd have this nasty error.
Is there a way around this without overriding any RestTemplate stuff? Am I viewing this situation incorrectly? Please help as I'm a bit baffled.
One more way to solve this would be to make response entity as null as shown below.
ResponseEntity<?> response = restTemplate.exchange("http://localhost:8080/myapp/user/{userID}",
HttpMethod.DELETE,
requestEntity,
null,
userID);
If you still need response headers, try implementing the ResponseErrorHandler.
I believe you should probably look at the ResponseExtractor interface & call execute on the RestTemplate providing your implementation of the extractor. To me it looks like a common requirement to do this so have logged this:
https://jira.springsource.org/browse/SPR-8016
Here's one I prepared earlier:
private class MyResponseExtractor extends HttpMessageConverterExtractor<MyEntity> {
public MyResponseExtractor (Class<MyEntity> responseType,
List<HttpMessageConverter<?>> messageConverters) {
super(responseType, messageConverters);
}
#Override
public MyEntity extractData(ClientHttpResponse response) throws IOException {
MyEntity result;
if (response.getStatusCode() == HttpStatus.OK) {
result = super.extractData(response);
} else {
result = null;
}
return result;
}
}
I've tested this & it seems to do what I want.
To create the instance of the ResponseExtractor I call the constructor & pass the converters from a RestTemplate instance that's been injected;
E.g.
ResponseExtractor<MyEntity> responseExtractor =
new MyResponseExtractor(MyEntity.class, restTemplate.getMessageConverters());
Then the call is:
MyEntity responseAsEntity =
restTemplate.execute(urlToCall, HttpMethod.GET, null, responseExtractor);
Your mileage may vary. ;-)
Here's a simple solution where you can set the default Content-Type for use if it is missing in the response. The Content-Type is added to the response header before it is handed back off to the preconfigured ResponseExtractor for extraction.
public class CustomRestTemplate extends RestTemplate {
private MediaType defaultResponseContentType;
public CustomRestTemplate() {
super();
}
public CustomRestTemplate(ClientHttpRequestFactory requestFactory) {
super(requestFactory);
}
public void setDefaultResponseContentType(String defaultResponseContentType) {
this.defaultResponseContentType = MediaType.parseMediaType(defaultResponseContentType);
}
#Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException {
return super.doExecute(url, method, requestCallback, new ResponseExtractor<T>() {
public T extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().getContentType() == null && defaultResponseContentType != null) {
response.getHeaders().setContentType(defaultResponseContentType);
}
return responseExtractor.extractData(response);
}
});
}
}
This should now be fixed in Spring 3.1 RC1.
https://jira.spring.io/browse/SPR-7911
Or you could extend RestTemplate and override doExecute(..) and check the response body.
For example here is what I implemented and works for us:
#Override
protected <T> T doExecute(final URI url, final HttpMethod method, final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException
{
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try
{
final ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null)
{
requestCallback.doWithRequest(request);
}
response = request.execute();
if (!getErrorHandler().hasError(response))
{
logResponseStatus(method, url, response);
}
else
{
handleResponseError(method, url, response);
}
if ((response.getBody() == null) || (responseExtractor == null))
{
return null;
}
return responseExtractor.extractData(response);
}
catch (final IOException ex)
{
throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex);
}
finally
{
if (response != null)
{
response.close();
}
}
}
I think you are right.
I'm having a similar problem.
I think we should be getting a ResponseEntity with a HttpStatus of NO_CONTENT and a null body.
I came along a workaround (not sure if it meets your case):
First define a custom interceptor class which implements ClientHttpRequestInterceptor. and check if response.getStatusCode() meets your case (my case is != HttpStatus.NOT_FOUND and response.getBody() length is 0), define a custom class (e.x. DefaultResponseForEmptyRestTemplateBody) which has a static method of type MockClientHttpResponse:
public class RequestResponseInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
if(response.getStatusCode()!=HttpStatus.NOT_FOUND && response.getBody().readAllBytes().length==0){
response = DefaultResponseForEmptyRestTemplateBody.getResponse(response.getStatusCode());
}
return response;
}
}
public static class DefaultResponseForEmptyRestTemplateBody {
MockClientHttpResponse response;
private static byte[] content = new byte[0];
public static MockClientHttpResponse getResponse(HttpStatus statusCode){
content = "response body is empty".getBytes();
return new MockClientHttpResponse(content, statusCode);
}
}
finally add this interceptor to your restTemplate object as below:
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
and call your restTemplate.postForEntity:
ResponseEntity<String> response = this.restTemplate.postForEntity(baseUrl, requestParams,String.class);