Spring Web: Download a File from a service via a Spring Service - java

I want to be able to download a file from a legacy service through a middle-layer Spring Web service. The problem currently is that I am returning the contents of the file and not the file itself.
I've used FileSystemResource before, but I do not want to do this, since I want Spring to only redirect and not create any files on the server itself.
Here is the method:
#Override
public byte[] downloadReport(String type, String code) throws Exception {
final String usernamePassword = jasperReportsServerUsername + ":" + jasperReportsServerPassword;
final String credentialsEncrypted = Base64.getEncoder().encodeToString((usernamePassword).getBytes("UTF-8"));
final HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
httpHeaders.add("Authorization", "Basic " + credentialsEncrypted);
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
final HttpEntity httpEntity = new HttpEntity(httpHeaders);
final String fullUrl = downloadUrl + type + "?code=" + code;
return restTemplate.exchange(fullUrl, HttpMethod.GET, httpEntity, byte[].class, "1").getBody();
}

Turns out I was missing this annotation parameter in my *Controller class:
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
The whole method of the controller should look like this:
#RequestMapping(value = "/download/{type}/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<?> downloadReport(#PathVariable String type, #PathVariable String id) throws Exception {
return new ResponseEntity<>(reportService.downloadReport(type, id), HttpStatus.OK);
}

Related

What's the best way to encode and decode parameter in springboot?

I use #RequestParam to get the parameter value,but I find the if I pass the value like 'name=abc&def&id=123',I will get the name value 'abc' instead of 'abc&def'. I find the encode and decode the parameter value can solve my problem.But I have to write the encode and decode mehtod in every controller method,Do spring have the global mehtod that decode every #RequestParam value?When using #RequestParam, is it necessary to encode and decode every value?
Here is my code:
#PostMapping("/getStudent")
public Student getStudent(
#RequestParam String name,
#RequestParam String id) {
name= URLDecoder.decode(name, "UTF-8");
//searchStudent
return Student;
}
#PostMapping("/getTeacher")
public teacher getTeacher(
#RequestParam String name,
#RequestParam String teacherNo) {
name= URLDecoder.decode(name, "UTF-8");
//searchTeacher
return teacher;
}
Somebody say the the Spring will have already done this,but I have try,the result is not right.Only use curl cmd is ok,but java code is not ok.
#PostMapping(value = "/example")
public String handleUrlDecode1(#RequestParam String param) {
//print ello%26test
System.out.println("/example?param received: " + param);
return "success";
}
#GetMapping(value = "/request")
public String request() {
String url = "http://127.0.0.1:8080/example?param=ello%26test";
System.out.println(url);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForObject(url, null, String.class);
}
You must create an HTTP entity and send the headers and parameter in body.
#GetMapping(value = "/request")
public String request() {
String url = "http://127.0.0.1:8080/example";
System.out.println(url);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("param","ello&test");
map.add("id","ab&c=def");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
return restTemplate.postForObject(url, request, String.class);
}
As you can read here, the escape character for & is %26.
So you should use the following
name=abc%26def&id=123
If you don't use an escape character according to URL standards, Spring will try to use what follows & and try to match it as a new query parameter.
No need to manually use URLDecoder, SpringBoot controllers will handle it for you.
#RestController
public class UrlDecodeController {
#GetMapping(value = "/example")
public String handleUrlDecode(#RequestParam String param) {
System.out.println("/example?param received: " + param);
return "success";
}
#PostMapping(value = "/example2")
public String handleUrlDecodeInPostRequest(#RequestParam String param1, ExamplePayload payload) {
System.out.println("/example2?param1 received: " + param1);
System.out.println("request body - value1: " + payload.getValue1());
return "success";
}
#GetMapping(value = "/request")
public String request() {
String url = "http://localhost:8080/example2?param1=test1&test2";
System.out.println(url);
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("value1","test1&test2");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
return restTemplate.postForObject(url, request, String.class);
}
class ExamplePayload{
private String value1;
private String value2;
//getters and setters
public ExamplePayload() {
}
}
}
Call with GET /example?param=hello%26test and the System.out.println outputs:
/example?param received: hello&test
Call the POST using curl as an example:
curl -X POST "http://localhost:8080/example2?param1=test1%26test2" -d "value1=test3%26test4"
Prints:
/example2?param1 received: test1&test2
request body - value1: test3&test4
Added GET /request to show using RestTemplate with the application/x-www-form-urlencoded Content-Type. Note that RestTemplate will automatically url encode any values passed as request parameters or in the request body. If you pass a String value of "%26" it will pass it as is, this is what you are seeing in your example. If you pass "&" it will url encode it to "%26" for you, and the Controller decodes it automatically on the other side.

How do I pass a variable from one controller to another in Spring

I was able to get my access tokens from my local server but need to use the access token retrieved from the signup get controller in the signup post controller. How do I get the "token" variable into the signup post controller to use in HTTP calls?
// Initial mapping for visiting the form
#RequestMapping(value = "/signup", method= RequestMethod.GET)
public String signupForm(#RequestParam("code") String code, #ModelAttribute("signup") emailInput form)
throws JsonProcessingException, IOException {
ResponseEntity<String> response = null;
System.out.println("Authorization Code: " + code);
RestTemplate restTemplate = new RestTemplate();
// Client id and secret replaced
String credentials = "clientId:clientSecret";
String encodedCredentials = new String(Base64.encodeBase64(credentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + encodedCredentials);
HttpEntity<String> request = new HttpEntity<String>(headers);
String access_token_url = "https://idfed.constantcontact.com/as/token.oauth2";
access_token_url += "?code=" + code;
access_token_url += "&redirect_uri=http://localhost:8080/signup";
access_token_url += "&grant_type=authorization_code";
response = restTemplate.exchange(access_token_url, HttpMethod.POST, request, String.class);
System.out.println("Full Access Token Response: " + response.getBody());
// Get the Access Token From the recieved JSON response
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(response.getBody());
String token = node.path("access_token").asText();
System.out.println("Here is my token: " + token);
return "/signup";
}
// What happens after the user presses the submit button
Second Controller where I need the variable to use in calls. I need it here so that I can use form variables collected from the HTML page to make calls after the user posts the form data.
#RequestMapping(value = "/signup", method= RequestMethod.POST)
public String signupSubmit(#ModelAttribute("signup") emailInput form, RedirectAttributes redirectAttributes)
throws IOException {
// Use access token here to make calls to endpoints
redirectAttributes.addFlashAttribute("email", form.getEmail());
return "redirect:/result";
}

Loading JSON data from a URL passing authentication parameters

I have to consume a JSON from a URL, I'm using Springboot with jackson, when I post I send some authentication information in the header, the API expects SECRETKEY + ACCESSKEY + date I have all this information to send
public void sendListPayload(int count, List object, String controller) throws NoSuchAlgorithmException, IOException {
Control type = Control.valueOf(controller);
String endereco = getAdress(type);
String payloadSecure = "";
RestTemplate restTemplate = new RestTemplate();
String url = "http://adress/site.php";
HttpHeaders headers;
String payload = convertListToJson(object);
headers = getHeaders(count, payload);
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(payloadSecure, headers);
String answer = restTemplate.postForObject(url, entity, String.class);
log.info(answer);
}
GetHeader
public HttpHeaders getHeaders(int sizeRecords, String payloadSecure) throws NoSuchAlgorithmException {
HttpHeaders headers = new HttpHeaders();
String signature = "";
signature = payloadSecure + SECRETKEY + ACCESSKEY + getISODate();
String fullSignature = FIRSTPAYLOAD + getISODate() + ":" + Useful.toSha(signature);
headers.add("HEADER", fullSignature);
return headers;
The minified JSON I'm going to read will come like this
[{"relatorioID":"1852","professorID":"7","alunoID":"37","turmaID":"44","bimestre":"0","data":"2014-06-05 07:51:49","situacao":"1"},
{"relatorioID":"1854","professorID":"7","alunoID":"37","turmaID":"44","bimestre":"0","data":"2014-06-05 07:51:55","situacao":"1"}]
I already have an object with the same fields to instantiate with the JSON data
I'm very new to java and springboot, how do I get past secretkey and accesskey? Is it in the header of get mehod?
Then I have to transform JSON received into a list of objects using jackson ... to insert them into the local database.
So, the point is you need to be clear about the API specification that in which format your API expect the request. Anyways answering your question below code can help you to send your authentication parameters in the header and handle the response.
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.http.ResponseEntity;
//some more class import may be you need to add
try {
UriComponents uriComponents =
UriComponentsBuilder.newInstance().scheme("https").host(host).path(url).
queryParam("url_param1", value).queryParam("another_param",
value).build().encode();
HttpHeaders headers = new HttpHeaders();
headers.add("SECRETKEY", value);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
ResponseEntity<List<MyResponseObject>> response = restTemplate.exchange(uriComponents.toUri(), HttpMethod.GET, entity, new ParameterizedTypeReference<List<MyResponseObject>>());
List<MyResponseObject > responses= response.getBody();
} catch (Exception e) {
logger.error(e.getMessage());
}
Create MyResponseObject class to bind your response json property
public class MyResponseObject {
#JsonProperty("relatorioID")
private String relatorioID;
#JsonProperty("professorID")
private Integer professorID;
...
//getter //setter
}
I hope this will help you to bind your params on the header and sent by encoding your query param in URL. Once you get the response in as a list of MyResponseObject object.

Spring forward rest request to another rest service [duplicate]

I have build a web application using spring mvc framework to publish REST services.
For example:
#Controller
#RequestMapping("/movie")
public class MovieController {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody Movie getMovie(#PathVariable String id, #RequestBody user) {
return dataProvider.getMovieById(user,id);
}
Now I need to deploy my application but I have the following problem:
The clients do not have direct access to the computer on which the application resides (There is a firewall). Therefore I need a redirection layer on a proxy machine (accessible by the clients) which calls the actual rest service.
I tried making a new call using RestTemplate:
For Example:
#Controller
#RequestMapping("/movieProxy")
public class MovieProxyController {
private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody Movie getMovie(#PathVariable String id,#RequestBody user,final HttpServletResponse response,final HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), new HttpEntity<T>(user, headers), Movie.class);
}
This is ok but I need to rewrite each method in the controller to use the resttemplate. Also, this causes redundant serialization/deserialization on the proxy machine.
I tried writing a generic function using restemplate, but it did not work out:
#Controller
#RequestMapping("/movieProxy")
public class MovieProxyController {
private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";
#RequestMapping(value = "/**")
public ? redirect(final HttpServletResponse response,final HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
RestTemplate restTemplate = new RestTemplate();
return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), ? , ?);
}
I could not find a method of resttemplate which works with request and response objects.
I also tried spring redirect and forward. But redirect does not change the request's client ip address so i think it is useless in this case. I could not forward to another URL either.
Is there a more appropriate way to achieve this?
You can mirror/proxy all requests with this:
private String server = "localhost";
private int port = 8080;
#RequestMapping("/**")
#ResponseBody
public String mirrorRest(#RequestBody String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
URI uri = new URI("http", null, server, port, request.getRequestURI(), request.getQueryString(), null);
ResponseEntity<String> responseEntity =
restTemplate.exchange(uri, method, new HttpEntity<String>(body), String.class);
return responseEntity.getBody();
}
This will not mirror any headers.
Here's my modified version of the original answer, which differs in four points:
It does not make the request body mandatory, and as such does not let GET requests fail.
It copies all headers present in the original request. If you are using another proxy/web server, this can cause issues due to content length/gzip compression. Limit the headers to the ones you really need.
It does not reencode the query params or the path. We expect them to be encoded anyway. Note that other parts of your URL might also be encoded. If that is the case for you, leverage the full potential of UriComponentsBuilder.
It does return error codes from the server properly.
#RequestMapping("/**")
public ResponseEntity mirrorRest(#RequestBody(required = false) String body,
HttpMethod method, HttpServletRequest request, HttpServletResponse response)
throws URISyntaxException {
String requestUrl = request.getRequestURI();
URI uri = new URI("http", null, server, port, null, null, null);
uri = UriComponentsBuilder.fromUri(uri)
.path(requestUrl)
.query(request.getQueryString())
.build(true).toUri();
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.set(headerName, request.getHeader(headerName));
}
HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
try {
return restTemplate.exchange(uri, method, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
}
You can use Netflix Zuul to route requests coming to a spring application to another spring application.
Let's say you have two application: 1.songs-app, 2.api-gateway
In the api-gateway application, first add the zuul dependecy, then you can simply define your routing rule in application.yml as follows:
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>LATEST</version>
</dependency>
application.yml
server:
port: 8080
zuul:
routes:
foos:
path: /api/songs/**
url: http://localhost:8081/songs/
and lastly run the api-gateway application like:
#EnableZuulProxy
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now, the gateway will route all the /api/songs/ requests to http://localhost:8081/songs/.
A working example is here: https://github.com/muatik/spring-playground/tree/master/spring-api-gateway
Another resource: http://www.baeldung.com/spring-rest-with-zuul-proxy
#derkoe has posted a great answer that helped me a lot!
Trying this in 2021, I was able to improve on it a little:
You don't need #ResponseBody if your class is a #RestController
#RequestBody(required = false) allows for requests without a body (e.g. GET)
https and port 443 for those ssl encrypted endpoints (if your server serves https on port 443)
If you return the entire responseEntity instead of only the body, you also get the headers and response code.
Example of added (optional) headers, e.g. headers.put("Authorization", Arrays.asList(String[] { "Bearer 234asdf234"})
Exception handling (catches and forwards HttpStatuses like 404 instead of throwing a 500 Server Error)
private String server = "localhost";
private int port = 443;
#Autowired
MultiValueMap<String, String> headers;
#Autowired
RestTemplate restTemplate;
#RequestMapping("/**")
public ResponseEntity<String> mirrorRest(#RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
URI uri = new URI("https", null, server, port, request.getRequestURI(), request.getQueryString(), null);
HttpEntity<String> entity = new HttpEntity<>(body, headers);
try {
ResponseEntity<String> responseEntity =
restTemplate.exchange(uri, method, entity, String.class);
return responseEntity;
} catch (HttpClientErrorException ex) {
return ResponseEntity
.status(ex.getStatusCode())
.headers(ex.getResponseHeaders())
.body(ex.getResponseBodyAsString());
}
return responseEntity;
}
proxy controller with oauth2
#RequestMapping("v9")
#RestController
#EnableConfigurationProperties
public class ProxyRestController {
Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;
#Autowired
private ClientCredentialsResourceDetails clientCredentialsResourceDetails;
#Autowired
OAuth2RestTemplate oAuth2RestTemplate;
#Value("${gateway.url:http://gateway/}")
String gatewayUrl;
#RequestMapping(value = "/proxy/**")
public String proxy(#RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response,
#RequestHeader HttpHeaders headers) throws ServletException, IOException, URISyntaxException {
body = body == null ? "" : body;
String path = request.getRequestURI();
String query = request.getQueryString();
path = path.replaceAll(".*/v9/proxy", "");
StringBuffer urlBuilder = new StringBuffer(gatewayUrl);
if (path != null) {
urlBuilder.append(path);
}
if (query != null) {
urlBuilder.append('?');
urlBuilder.append(query);
}
URI url = new URI(urlBuilder.toString());
if (logger.isInfoEnabled()) {
logger.info("url: {} ", url);
logger.info("method: {} ", method);
logger.info("body: {} ", body);
logger.info("headers: {} ", headers);
}
ResponseEntity<String> responseEntity
= oAuth2RestTemplate.exchange(url, method, new HttpEntity<String>(body, headers), String.class);
return responseEntity.getBody();
}
#Bean
#ConfigurationProperties("security.oauth2.client")
#ConditionalOnMissingBean(ClientCredentialsResourceDetails.class)
public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
return new ClientCredentialsResourceDetails();
}
#Bean
#ConditionalOnMissingBean
public OAuth2RestTemplate oAuth2RestTemplate() {
return new OAuth2RestTemplate(clientCredentialsResourceDetails);
}
If you can get away with using a lower-level solution like mod_proxy that would be the simpler way to go, but if you need more control (e.g. security, translation, business logic) you may want to take a look at Apache Camel: http://camel.apache.org/how-to-use-camel-as-a-http-proxy-between-a-client-and-server.html
I got inspired by Veluria's solution, but I had issues with gzip compression sent from the target resource.
The goal was to omit Accept-Encoding header:
#RequestMapping("/**")
public ResponseEntity mirrorRest(#RequestBody(required = false) String body,
HttpMethod method, HttpServletRequest request, HttpServletResponse response)
throws URISyntaxException {
String requestUrl = request.getRequestURI();
URI uri = new URI("http", null, server, port, null, null, null);
uri = UriComponentsBuilder.fromUri(uri)
.path(requestUrl)
.query(request.getQueryString())
.build(true).toUri();
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (!headerName.equals("Accept-Encoding")) {
headers.set(headerName, request.getHeader(headerName));
}
}
HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
try {
return restTemplate.exchange(uri, method, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
}
You need something like jetty transparent proxy, which actually will redirect your call, and you get a chance to overwrite the request if you needed. You may get its detail at http://reanimatter.com/2016/01/25/embedded-jetty-as-http-proxy/

How to send JSON as a Input parameter from one Microservice to another using RestTemplate in Spring Boot

I want to send JSON as an input from Microservice M1 to a Microservice M2.
M1 and M2 both are on different machines.
I am new to Spring Boot,
I found some code but I am unable to get it.
Please help.
make a class on both microservices or make a jar of that class and add to both microservices so that they both can access the same data.
Lets say the class is
class TestData{
private String name;
private String id;
// getters and setters
}
Now you can send data from M1 to M2 as following
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
TestData data = new TestData();
HttpEntity<?> entity = new HttpEntity<Object>(data,headers);
ResponseEntity<Object> responseEntity = restTemplate.exchange("url", HttpMethod.POST, entity, Object.class);
In Microservice M2 you can write a controller to get the data and process it as follows
#RequestMapping(value="/url",method=RequestMethod.POST)
public Object do(#RequestBody TestData data){
// do something
return //something
}
Let's Say Your Have MicroService1 which needs to send JSONObject => JsonObject to another MicroService2 which is on different Machine but on same network .
Sender Side:
RestTemplate restTemplate = new RestTemplate();
String jsonString = restTemplate.postForObject("http://10.177.7.128:8080/user/insertJsonObject",jsonObject,String.class);
Syntax for restTemplate.postForObject is:
ResponseType var1 = restTemplate.postForObject("network ip Address:portnumber/path",JSONObject,ResponseType)
To Know the URI go to System Preferences > Network
To Receive the object at the receiver Side
#RequestMapping(value="/user/insertJsonObject", method=RequestMethod.POST)
public String updateProductSold(#RequestBody JSONObject jsonObject) {
...Body
...
...
return responseStatus;
Here is the sample code
public class Test {
public static void main(String[] args) {
String jsonString = "{\"id\" : \"123\",\"name\" : \"Tom\",\"class\" : {\"subject\" : \"Math\",\"teacher\" : \"Jack\"}}";
RestTemplate restTemplate = new RestTemplate();
String url = "http://192.1168.1.190:8080/test" // url for second service
System.out.println(responserEntityValue(jsonString,restTemplate,url,HttpMethod.POST,String.class));
}
public ResponseEntity<String> responserEntityValue(final String body, final RestTemplate restTemplate,
final String uRL, final HttpMethod requestMethod, final Class<String> stringClass) {
HttpHeaders headers = new HttpHeaders();
// Set all headers
headers.add(DatabaseConstants.CONTENT_TYPE, "application/json");
HttpEntity<String> request = new HttpEntity<>(body, headers);
return restTemplate.exchange(uRL, requestMethod, request, stringClass);
}

Categories

Resources