I have a class annoted with Service Annotation on server 1 .
#Service
public class MainHandler implements AbstractHandler {
#Autowired
private ServiceLocal defaultService;
#Override
public boolean execute(HttpServletRequest request, HttpServletResponse response) throws MsisdnServiceException {
System.out.println("The default Request" + request);
}
}
I want to call this method from other remote server after passing the request and get the response from this , what is the way to do in spring .
Invoking methods remotely would be using a technology called RMI, which you can google easily.
However, since you want to use HttpServletRequest and HttpServletResponse, you probably should write an Http Controller using Spring MVC. For that you can also google and very easily find excellent tutorials and guides.
You can use spring's RestTemplate to make communication with the servers.
First you need to create a controller on server 1 backend to get data from server 2:
#RestController
public class MyController {
#RequestMapping(value = "/endpoint", method = RequestMethod.POST)
String execute(#RequestBody MyClass object) {
System.out.println("Your data" + object);
}
}
On server 2 backend create a method that make a REST call to server 1's endpoint with RestTemplate:
void request() {
String url = "http://localhost:8080/endpoint";
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set("Content-Type", "application/json");
JSONObject json = new JSONObject();
json.put("name", "yourName");
json.put("email", "name#gmail.com");
HttpEntity < String > httpEntity = new HttpEntity < String > (json.toString(), httpHeaders);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject(url, httpEntity, String.class);
}
Related
I have been searching all morning and i think i'm missing something .
i have a Spring boot controller with a method to save a client.
this is the method :
// ajouter un client
#RequestMapping(value="/AjoutClient/{clientData}", method=RequestMethod.POST)
public String AjoutClient(#PathVariable String clientData) {
Client c = new Client();
c.setNomClient(clientData.split(";")[0]);
c.setPrenomClient(clientData.split(";")[1]);
c.setAdresseClient(clientData.split(";")[2]);
c.setTelClient(clientData.split(";")[3]);
c.setEmailClient(clientData.split(";")[4]);
c.setCinClient(clientData.split(";")[5]);
client.save(c);
return "test";
}
i want to consume this method from another application with this method :
#RequestMapping(value="/ajoutClient", method=RequestMethod.POST)
public void ajout(#RequestParam("nom") String nom,#RequestParam("prenom") String prenom,#RequestParam("adr") String adr,#RequestParam("tel") String tel,#RequestParam("mail") String mail,#RequestParam("cin") String cin) {
String ClientData=nom+";"+prenom+";"+adr+";"+tel+";"+mail+";"+cin;
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> request = new HttpEntity<>(new String(ClientData));
ResponseEntity<String> response = restTemplate
.exchange("http://localhost:9093/AjoutClient/"+ClientData, HttpMethod.POST, request, String.class);
assertThat(response.getStatusCode(), is(HttpStatus.CREATED));
}
** explication : i get the values from a form and construct a string with those values, then try to send that string to my clientController.
PS: i can't send client object, i have to send the values one by one then create the client object in the clientController.
i'm feeling pretty lost because i can see that something is wrong but i don't know what is it.
First of all I would suggest you avoid using #PathVariable for passing the data like this.
You're already sending everything in the request body, so first step is to change:
public String AjoutClient(#PathVariable String clientData) {
to
public String AjoutClient(#RequestBody String clientData) {
and
restTemplate.exchange("http://localhost:9093/AjoutClient/" + ClientData, HttpMethod.POST, request, String.class);
to just
restTemplate.exchange("http://localhost:9093/AjoutClient", HttpMethod.POST, request, String.class);
Then if you're expecting 201 status then you have to return it:
public ResponseEntity<String> AjoutClient(#RequestBody String clientData) {
...
return ResponseEntity.created(null).body("test");
}
PS: Please pay attention to what #JB Nizet mentioned, cause he has a point here. Just research that keywords (google them) or read some tutorials e.g https://www.baeldung.com/java-url-encoding-decoding or https://www.baeldung.com/rest-template and you'll easily find out more about standard practices.
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/
I have the following code:
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
public ResponseEntity<?> api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
String response="{SOME_JSON}";
URI callbackURL = new URI("http://otherAPIEnv/api2");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(callbackURL);
return new ResponseEntity<String>(response,httpHeaders, HttpStatus.OK);
}
I tried the above code, but when I hit the api1 through my curl I get the response on the same machine, but I want the response to be redirected to api2 at otherAPIEnv machine.
Could someone please suggest how to achieve this kind of request and response?
When you send a request to a URL it should respond to the same otherwise client will be in waiting for it until it times out.
So, the approach should be different in this scenario.
First, in your main rest API you have to send a response code to release the client.
Then, in the API method you have to call another method asynchronously which calls api2 and performs the desired operation.
Here is a simple example.
#Autowired
API2Caller api2Caller;
#RequestMapping(
consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE},
path = "api/api1",
method = RequestMethod.POST,
produces = MediaType.ALL_VALUE
)
#ResponseStatus(HttpStatus.ACCEPTED)
public void api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException, GeneralSecurityException, URISyntaxException {
api2Caller.callApi2(requestBody);
}
and the APICaller should look like following
#Component
public class API2Caller {
#Async
public SomeResultPojo callApi2() {
// use RestTemplate to call the api2
return restTemplate.postForObject("http://otherAPIEnv/api2", request, SomeResultPojo.class);
}
}
But you can choose your most comfortable way to perform asynchronous operation.
Look like a job for redirect.
String redirectMe() {
return "redirect:http://otherAPIEnv/api2"
}
As for the curl. You have POST mapping of the method so be sure to try it with curl -X POST... or change it to GET.
This the more modular and more generic way to do such kind of things:
public #ResponseBody ClientResponse updateDocStatus(MyRequest myRequest) {
ClientResponse clientResponse = new ClientResponse(CTConstants.FAILURE);
try {
HttpHeaders headers = prepareHeaders();
ClientRequest request = prepareRequestData(myRequest);
logger.info("cpa request is " + new Gson().toJson(request));
HttpEntity<ClientRequest> entity = new HttpEntity<ClientRequest>(request, headers);
String uri = cpaBaseUrl + updateDocUrl ;
ClientResponse serviceResponse = Utilities.sendHTTPRequest(uri, entity);
clientResponse = serviceResponse;
if (serviceResponse != null) {
if (CTConstants.SUCCESS.equalsIgnoreCase(serviceResponse.getStatus())) {
clientResponse.setStatus(CTConstants.SUCCESS);
clientResponse.setMessage(" update success.");
}
}
} catch (Exception e) {
logger.error("exception occurred ", e);
clientResponse.setStatus(CTConstants.ERROR);
clientResponse.setMessage(e.getMessage());
}
return clientResponse;
}
public static ClientResponse sendHTTPRequest(String uri, HttpEntity<ClientRequest> entity) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());
SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setReadTimeout(CTConstants.SERVICE_TIMEOUT);
rf.setConnectTimeout(CTConstants.SERVICE_TIMEOUT);
ParameterizedTypeReference<ClientResponse> ptr = new ParameterizedTypeReference<ClientResponse>() {
};
ResponseEntity<ClientResponse> postForObject = restTemplate.exchange(uri, HttpMethod.POST, entity, ptr);
return postForObject.getBody();
}
You need to use redirect and modify the return type of your method
public String api1CallBack(#RequestBody String requestBody, HttpServletRequest request) throws IOException {
return "redirect:http://otherAPIEnv/api2";
}
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);
}
I have 2 Spring Web applications: Application1 and Application2. In Application1, I have an endpoint at "http://application1/getbigcsv" that uses streaming in order to serve a gigantic 150MB CSV file back to the user if they hit that URL.
I dont want users to hit Application1 directly, but hit Application2 instead.
If I have the following method in my controller in Application2
#RequestMapping(value = "/large.csv", method = GET, produces = "text/csv")
#ResponseStatus(value = HttpStatus.OK)
public String streamLargeCSV() {
// Make an HTTP Request to http://application1/getbigcsv
// Return its response
}
My worry is the above is not doing "streaming" whereas Application1 is doing streaming. Is there some way I can make sure that the application2 will be serving back the same data from application1's rest endpoint in a streaming fashion? Or is the method above actually returning things in a "Streaming" method already because Application1 is serving its endpoint as streaming?
First of all: you can but not with that method signature.
Unfortunately, you have not shown how you produce that CSV file in app1, whether this is truly streaming. Let's assume it is.
You signature will look like this:
#RequestMapping(value = "/large.csv", method = GET, produces = "text/csv")
#ResponseStatus(value = HttpStatus.OK)
public void streamLargeCSV(OutputStream out) {
// Make an HTTP Request to http://application1/getbigcsv
// Return its response
}
Now we have to grab the input stream from app1 first. Use Apache HttpClient to get your HttpEntity. This entity has a writeTo(OutputStream) method which will receive your out parameter. It will block until all bytes are consumed/streamed. When you are done, free all HttpClient resources.
Complete code:
#RequestMapping(value = "/large.csv", method = GET, produces = "text/csv")
#ResponseStatus(value = HttpStatus.OK)
public void streamLargeCSV(OutputStream out) {
// Make an HTTP Request to http://application1/getbigcsv
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://application1/getbigcsv");
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
HttpEntity entity = response.getEntity();
// Return its response
entity.writeTo(out);
} finally {
response.close();
}
}
Here is my real world example. Start reading from "Interesting to say what I have achieved in particular with this:"
In java.ws.rs.core package you have classes: StreamingOutput and ResponseBuilder.
Not sure if it will help you, but you may try.
Example:
#Produces("application/octet-stream")
public Response doThings () {
...
StreamingOutput so;
try {
so = new StreamingOutput() {
public void write(OutputStream output) {
…
}
};
} catch (Exception e) {
...
}
ResponseBuilder response = Response.ok(so);
response.header("Content-Type", ... + ";charset=utf-8");
return response.build();
}
Change your methods return type to ResponseEntity<?> and return as following:
#GetMapping("/download")
public ResponseEntity<?> fetchActivities(
#RequestParam("filename") String filename) {
String string = "some large text"
InputStream is = new ByteArrayInputStream(string.getBytest());
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=large.txt");
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
return ResponseEntity.ok().headers(headers).body(new InputStreamResource(is));
}