How to download pdf file using Feign Client? - spring-cloud

In our project we are using feign client to make a call to third party service. For content type application/json it’s working fine. But we have a requirement where a third party service URL return pdf file and that time we are getting exception.
Due to security reason I can not paste the logs and code but if any one share me the code to download a pdf file from feign client that would be very helpful to me.
Thanks in advance!!

You could use byte[] as return type.
#FeignClient(url = "url", name = "name")
public interface SomeFeignClient {
#GetMapping("/give-me-a-pdf")
byte[] getPDF();
}
Your service would simply call
public byte[] getPDF() {
return SomeFeignClient.getPDF();
}
Now with the bytes array you could perform any operation you want, for example saving the file using
FileUtils.writeByteArrayToFile(new File("pathname"), resource);
or provide an endpoint to download the file (Spring boot can return pretty much anything without use of any external library)
#GetMapping("/pdf")
ResponseEntity getPDF() {
byte[] resource = SomeFeignClient.getPDF();
return ResponseEntity.ok()
.contentLength(resource.length)
.contentType(MediaType.APPLICATION_PDF)
.body(resource);
}

Related

Spring Cloud Gateway Routing Based On Content of the Request Body

I need to create a reverse proxy that takes incoming request and based on the content of the request body, route the request to specific URI.
This is for a routing micro service that acts like a reverse proxy and does routing based on some information from each request body. This means for each request I need to parse the request body and get the "username" field and then make a JDBC connection to fetch additional information from the database. Based on that information in database, it would finally redirect the request to the correct URI.
From what I have now, I have 2 blocking methods. The first one is the parsing for the request body, the other one is the JDBC connection to the database. I understand that I should not put any blocking calls inside the gateway filter. I just don't know what I should do in this case. I could have both operations running async but in the end I still need the information from database to do routing.
#Bean
public RouteLocator apiLocator(RouteLocatorBuilder builder, XmlMapper xmlMapper) {
return builder.routes()
.route(r -> r
.path("/test")
.and()
.readBody(String.class, s -> true) // Read the request body, data will be cached as cachedRequestBodyObject
.filters(f -> f.filter(new GatewayFilter() {
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
// The following method is blocking and should not be put here
xmlMapper.readValue((String) exchange.getAttribute("cachedRequestBodyObject"), Map.class);
} catch (Exception e) {
//TODO
}
return chain.filter(exchange);
}
}))
.uri("http://localhost:8080"))
.build();
}
The above example only includes the blocking parsing as my request body is XML based. My IDE is warning me of having a blocking call there which I really appreciate.
Any help is greatly appreciated. Thank you everyone!
After some research, Mono.fromCallable seems to be a good fit. I then asked the same question directly under the github repo, it turns out that using a servlet app may be better. For anyone who is interested to see what I came up with, please take a look here https://github.com/spring-cloud/spring-cloud-gateway/issues/1229

How to get the file content type from the REST service?

I have a REST service to upload a file, is there a way to know what content type I might be getting? The user using my REST service might upload a PDF, Word or Excel document.
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("/details/documents")
public Response uploadDocument(#FormDataParam("file") File documentContent,
#FormDataParam("documentName") String documentName)
throws Exception {
//more implementation code here to upload the file.
}
Instead of File as a method parameter, you can use FormDataBodyPart, and then call getMediaType(). If you want to get the part's body as a File, just use getValueAs(File.class) or if you want the input stream just use InputStream.class.
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("/details/documents")
public Response uploadDocument(#FormDataParam("file") FormDataBodyPart part) {
MediaType mediaType = part.getMediaType();
File file = part.getValueAs(File.class);
}

How to close InputStream which fed into Response(jax.rs)

#GET
#Path("/{id}/content")
#Produces({ "application/octet-stream" })
public Response getDocumentContentById(#PathParam("id") String docId) {
InputStream is = getDocumentStream(); // some method which gives stream
ResponseBuilder responseBuilder = Response.ok(is);
responseBuilder.header("Content-Disposition", "attachment; filename=" + fileName);
return responseBuilder.build();
}
Here how can I close the InputStream is ? If something(jax.rs) closes automatically. Please give me some information. Thank you.
When you're wanting to stream a custom response, the most reliable way I've found is to return an object that contains the InputStream (or which can obtain the stream in some other way at some point), and to define a MessageBodyWriter provider that will do the actual streaming at the right time.
For example, this code is part of Apache Taverna, and it streams back the zipped contents of a directory. All that the main code needs to do to use it is to return a ZipStream as the response (which can be packaged in a Response or not) and to ensure that it is dealing with returning the application/zip content type. The final point to note is that since this is dealing with CXF, you need to manually register the provider; unlike with Glassfish, they are not automatically picked up. This is a good thing in sophisticated scenarios, but it does mean that you need to do the registration.

gwt getModuleBaseURL on same server creates an java.lang.UnsatisfiedLinkError

I have a single GWT web-application integrated with Spring MVC. I have a working Controller which works perfectly and is unit tested to accept POSTed JSON data and returns JSON data.
From within the same application, to avoid any SOP cross-site domain issues, I am making a call with a RequestBuilder to POST the same json data, and I expect JSON data back.
I created a basic java class that should make a call, but I have a few issues. This running web-app is running in hosted mode in Jetty in Eclipse. I have done a ton of research on how GWT should make a call to an existing web-service with a simple HTP request.
The first issue from my unit test is that:
String baseUrl = GWT.getModuleBaseURL();
is not working and I get:
java.lang.UnsatisfiedLinkError: com.google.gwt.core.client.impl.Impl.getModuleBaseURL()Ljava/lang/String;
I think I know what the correct URL should be, so when I hard-code the url correctly, and execute this code:
String url = getRootUrl() + "rest/pendingInvoices/searchAndCount";
System.out.println("PendingInvoiceDataSource: getData: url=" + url);
// Send request to server and catch any errors.
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
builder.setHeader("Accept", "application/json");
builder.setHeader("Content-type", "application/json");
// builder.setRequestData(requestData);
try
{
System.out.println("PendingInvoiceDataSource: SEND REQUEST: getData: requestData=" + requestData);
Request request = builder.sendRequest(requestData, new RequestCallback()
{
public void onError(Request request, Throwable exception)
{
System.out.println("Couldn't retrieve JSON");
}
#Override
public void onResponseReceived(Request request, Response response)
{
if (200 == response.getStatusCode())
{
// updateTable(JsonUtils.safeEval(response.getText()));
System.out.println("data=" + response.getText());
}
else
{
System.out.println("Couldn't retrieve JSON (" + response.getStatusText() + ")");
}
}
});
}
catch (RequestException e)
{
System.out.println("Couldn't retrieve JSON");
}
I get this error on he sendRequest:
java.lang.UnsatisfiedLinkError: com.google.gwt.xhr.client.XMLHttpRequest.create()Lcom/google/gwt/xhr/client/XMLHttpRequest;
at com.google.gwt.xhr.client.XMLHttpRequest.create(Native Method)
at com.google.gwt.http.client.RequestBuilder.doSend(RequestBuilder.java:383)
at com.google.gwt.http.client.RequestBuilder.sendRequest(RequestBuilder.java:261)
I think this might be a quick fix, or maybe something small I have forgotten, so I'll try some more testing and see what I can find.
Everything client in GWT is only meant to run on the client-side: compiled to JS or in DevMode.
Only shared, server and vm classes can be used on the server-side.
If you want to get your server URL, use the appropriate methods from the HttpServletRequest (or whatever it is in Spring MVC as it seems from how you tagged the question that's what you're using).
If you want to make HTTP requests from your server, use an HttpURLConnection, or OkHttp, Apache Http Components or similar libraries, or even Spring's own HTTP client API.
Actually, it was only the Unit Test that was having a problem. Once I actually tried to run the deployed code, it all worked. I'll still try h GWTTestCase as suggested in order to get the unit test working.
But everything worked correctly when I ran the deployed code.
I also changed: String baseUrl = GWT.getModuleBaseURL();
which gave me:
http://localhost:8888/MyProject
To: String baseUrl = GWT.getHostPageBaseURL();
which gave me:
http://localhost:8888/
and that all worked.

GWT request to download file

I know it's not possible to send an ajax request or use GWT's RequestBuidler to send a request for a file download; needing a form to do it but how do I get a reference to the response when it returns with lets say an error.
The request i send is for a file download but if the file download is too big the Java servlet responds with an error, how do i get a reference to this error to handle it appropriately on the GWT side.
You can add a FormPanel.SubmitCompleteHandler to the form and parse the results in onSubmitComplete().
With extGWT you can parse the HTML response to know the HTML error code
For instance
com.extjs.gxt.ui.client.widget.Component.addListener(Events.Submit, new Listener<FormEvent>()
{
public void handleEvent(final FormEvent event)
{
String htmlResponse = event.getResultHtml();
(...)
}
});