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.
Related
Suffered a lot in finding how to mock http response . Mocking http request for the same library was easy . Thought to create a thread here , to save your time should you need it .
Requirement ->
Wanted to mock a HttpResponse that is returned when any HttpRequest object is executed . (Note - this is specifically for google client api library)
//creating mockContent for httpRequest
MockHttpContent mockHttpContent = new MockHttpContent();
String content = new String("requestBody");
mockHttpContent.setContent(str.getBytes());
//mocking httpResponse and linking to httpRequest's execution
HttpTransport transport =
new MockHttpTransport() {
#Override
public LowLevelHttpRequest buildRequest(String method, String url) throws IOException {
return new MockLowLevelHttpRequest() {
#Override
public LowLevelHttpResponse execute() throws IOException {
MockLowLevelHttpResponse result = new MockLowLevelHttpResponse();
result.setContent("responseBody");
result.setContentEncoding("UTF-8");//this is very important
result.setHeaderNames(List.of("header1","header2"));
result.setHeaderValues(List.of("header1","header2"));
return result;
}
};
}
};
HttpRequest httpRequest = transport.createRequestFactory().buildPostRequest(HttpTesting.SIMPLE_GENERIC_URL,mockHttpContent);
//getting httpResponse from httpRequest
httpResponse = httpRequest.execute();
//condition to verify the content (body) of the response
assertEquals("responseBody",IOUtils.toString(httpResponse.getContent()));
I want to write a rest client for old code, which as I understand it accepts multipart.
My client is written in quarkus and uses resteasy-multipart-provider
I have old code which I want to call with:
#POST
#Path("/upload")
#Produces(MediaType.APPLICATION_JSON)
public Response addFiles(#Context HttpServletRequest request, #Context ServletContext context)
{
try
{
File repository = (File) context.getAttribute("javax.servlet.context.tempdir");
DiskFileItemFactory factory = Utils.getDiskFileItemFactory(context, repository);
factory.setRepository(repository);
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
List<FileItem> items = upload.parseRequest(request);
for (FileItem item: items)
{
if (!item.isFormField())
{
....
}
}
.....
}
And my client:
#Path("/upload")
#RegisterRestClient(configKey = "scannedimage")
#ClientHeaderParam(name = "Authorization", value = "{lookupAuth}")
public interface UploadClient extends BearerAuthorizedHeader {
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.MULTIPART_FORM_DATA)
Response upload(#MultipartForm UploadBody data);
}
public class UploadBody {
#FormParam("objectId")
#PartType(MediaType.TEXT_PLAIN)
public Long long1;
#FormParam("entityId")
#PartType(MediaType.TEXT_PLAIN)
public Long long2;
#FormParam("doctype")
#PartType(MediaType.TEXT_PLAIN)
public Long documentType;
#FormParam("file")
#PartFilename("{file.getName}")
#PartType(MediaType.APPLICATION_OCTET_STREAM)
public File file;
}
Response always return emty list
{
"lon1": 1,
"long2": 2,
"list": [],
"error": ""
}
what am I doing wrong
I have a valid request example creating using org.apache.httpcomponents:httpmime
HttpEntity entity = MultipartEntityBuilder.create().addTextBody("long1", "1").addTextBody("long2", "2499").addTextBody("doctype", "3306").addBinaryBody("file", file, ContentType.create("application/octet-stream"), "test.pdf").build();
HttpPost httpPost = new HttpPost("http://serviece/upload");
httpPost.setEntity(entity);
httpPost.setHeader("Authorization", "Bearer token");
HttpResponse response = httpClient.execute(httpPost);
HttpEntity result = response.getEntity();
System.out.println(EntityUtils.toString(result));
But I would like to implement it with rest-client, if it possible
As a result. I used org.apache.httpcomponents:httpmime:4.5.3 and writed method:
public UploadResponse upload(String long1, String long2, String documentType, String fileName, InputStream file) {
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpEntity entity = MultipartEntityBuilder
.create()
.addTextBody("long1", long1)
.addTextBody("long2", long2)
.addTextBody("documentType", documentType)
.addBinaryBody("file", file, ContentType.create(MediaType.APPLICATION_OCTET_STREAM), fileName)
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
.setCharset(StandardCharsets.UTF_8)
.build();
HttpPost httpPost = new HttpPost(url + "/upload");
httpPost.setEntity(entity);
httpPost.setHeader("Authorization", "Bearer " + token());
HttpResponse response = httpClient.execute(httpPost);
return objectMapper.readValue(response.getEntity().getContent(), UploadResponse.class);
} catch (IOException e) {
log.error("Error create httpClient", e);
}
return new UploadResponse();
}
Because i can't finded like through resteasy-multipart-provider add file name to file FormParam :(.
Maybe it's fix next version quarkus or restEasy
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.
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
From my Android app I want to request a URL with GET parameters and read the response.
In the request I must add a x-zip header.
The URL is something like
http://example.com/getmethod.aspx?id=111&method=Test
Can some one provide me code for that?
Two things are important: that it is a GET request and contains the x-zip header .
EDIT:
try {
HttpClient client = new DefaultHttpClient();
String getURL = "http://example.com/getmethod.aspx?id=111&method=Test";
HttpGet get = new HttpGet(getURL);
get.setHeader("Content-Type", "application/x-zip");
HttpResponse responseGet = client.execute(get);
HttpEntity resEntityGet = responseGet.getEntity();
if (resEntityGet != null) {
//do something with the response
Log.i("GET ",EntityUtils.toString(resEntityGet));
}
} catch (Exception e) {
e.printStackTrace();
}
I try with this code but I get code with .net error: Object reference not set to an instance of an object...
I think but I'm not sure this if for x-zip header, is header in my code ok?
Here's a code excerpt we're using in our app to set request headers. You'll note we set the CONTENT_TYPE header only on a POST or PUT, but the general method of adding headers (via a request interceptor) is used for GET as well.
/**
* HTTP request types
*/
public static final int POST_TYPE = 1;
public static final int GET_TYPE = 2;
public static final int PUT_TYPE = 3;
public static final int DELETE_TYPE = 4;
/**
* HTTP request header constants
*/
public static final String CONTENT_TYPE = "Content-Type";
public static final String ACCEPT_ENCODING = "Accept-Encoding";
public static final String CONTENT_ENCODING = "Content-Encoding";
public static final String ENCODING_GZIP = "gzip";
public static final String MIME_FORM_ENCODED = "application/x-www-form-urlencoded";
public static final String MIME_TEXT_PLAIN = "text/plain";
private InputStream performRequest(final String contentType, final String url, final String user, final String pass,
final Map<String, String> headers, final Map<String, String> params, final int requestType)
throws IOException {
DefaultHttpClient client = HTTPClientFactory.newClient();
client.getParams().setParameter(HttpProtocolParams.USER_AGENT, mUserAgent);
// add user and pass to client credentials if present
if ((user != null) && (pass != null)) {
client.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user, pass));
}
// process headers using request interceptor
final Map<String, String> sendHeaders = new HashMap<String, String>();
if ((headers != null) && (headers.size() > 0)) {
sendHeaders.putAll(headers);
}
if (requestType == HTTPRequestHelper.POST_TYPE || requestType == HTTPRequestHelper.PUT_TYPE ) {
sendHeaders.put(HTTPRequestHelper.CONTENT_TYPE, contentType);
}
// request gzip encoding for response
sendHeaders.put(HTTPRequestHelper.ACCEPT_ENCODING, HTTPRequestHelper.ENCODING_GZIP);
if (sendHeaders.size() > 0) {
client.addRequestInterceptor(new HttpRequestInterceptor() {
public void process(final HttpRequest request, final HttpContext context) throws HttpException,
IOException {
for (String key : sendHeaders.keySet()) {
if (!request.containsHeader(key)) {
request.addHeader(key, sendHeaders.get(key));
}
}
}
});
}
//.... code omitted ....//
}
You do it exactly as you showed with this line:
get.setHeader("Content-Type", "application/x-zip");
So your header is fine and the problem is some other input to the web service. You'll want to debug that on the server side.