I'm looking for a way how to forward POST request which has been made to endpoint in #RestController class and forward it to external URL with body and headers untouched (and return response from this API of course), is it possible to do it by using some spring features? The only solution which I have found is extracting a body from #RequestBody and headers from HttpServletRequest and use RestTemplate to perform a request. Is there any easier way?
#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());
}
}
The above code is taken from this answer.
This is more a matter of the HTTP spec than Spring where the server would be expected to return a 307 redirect status, indicating the client should follow the redirect using the same method and post data.
This is generally avoided in the wild as there's a lot of potential for misuse, and friction if you align with the W3.org spec that states the client should be prompted before re-executing the request at the new location.
One alternative is to have your Spring endpoint act as a proxy instead, making the POST call to the target location instead of issuing any form of redirect.
307 Temporary Redirect (since HTTP/1.1) In this occasion, the request should be repeated with another URI, but future requests can still use the original URI.2 In contrast to 303, the request method should not be changed when reissuing the original request. For instance, a POST request must be repeated using another POST request.
Related
I am using the Spring ClientHttpRequestInterceptor to capture all outgoing HTTP calls from my applications in order to log the data. In addition to the data that I am already collecting in the interceptor, I want to somehow fetch the name of the function from which the HTTP call originated. So, as an example, if a method called getStuffFromUrl is making the HTTP call using the Spring RestTemplate as follows,
public String getStuffFromUrl() {
...
return restTemplate.exchange(url, HttpMethod.GET,entity, String.class).getBody();
}
when I capture this outbound HTTP call in my interceptor, I want to retrieve the name of the method getStuffFromUrl as well. How could I go about doing this?
If you are allowed to modify your HTTP request, one way would be to add a ad-hoc HTTP header for the method name :
public String getStuffFromUrl() {
HttpHeaders headers = new HttpHeaders();
headers.add("JavaMethod", "getStuffFromUrl");
entity = new Entity(headers)
...
return restTemplate.exchange(url, HttpMethod.GET,entity, String.class).getBody();
}
You could then get back the method name and remove the header from within the ClientHttpRequestInterceptor prior the HTTP request is actualy sent out.
ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution)
throws IOException {
String javaMethodName="Unknown";
List<String> javaMethodHeader = request.getHeaders().remove("JavaMethod");
if(javaMethodHeader!=null && javaMethodHeader.size()>0) {
javaMethodName = javaMethodHeader.get(0);
}
log.info("Calling method = "+ javaMethodName);
execution.execute(request, body);
}
(provided code not tested)
I have to call a PUT method using Resttemplate. I am able to hit the service from POST Man. But when i try the same request from Java using Resttemplate its throwing error .What could be mistake i am doing.
405 : [{"category":"ACCESS","code":"METHOD_NOT_SUPPORTED","description":"Request method 'PUT' not
supported","httpStatusCode":"405"}]
#Autowired
#Qualifier("orderMasterUpdateClient")
private RestTemplate orderMasterUpdateClient; // Loading the template with credentials and URL
ResponseEntity<SalesOrderDocument> responseEntity = orderMasterUpdateClient.exchange(
URL,
HttpMethod.PUT,
new HttpEntity<>(headers),
SalesOrderDocument.class, changeRequest);
If you want to send changeRequestobject data in the body of the PUT request, I suggest you to use next RestTemplate exchange method call:
String url = "http://host/service";
ChangeRequest changeRequest = new ChangeRequest();
HttpHeaders httpHeaders = new HttpHeaders();
HttpEntity<ChangeRequest> httpEntity = new HttpEntity<>(changeRequest, httpHeaders);
ResponseEntity<ChangeRequest> response = restTemplate
.exchange(url, HttpMethod.PUT, httpEntity, ChangeRequest.class);
I'm trying to create test unit for GET method which requires JSON payload to get result based on provided data in JSON.
I have tried that:
User user = new User();
user.setUserId(userId);
ResponseEntity<User> getResponse = restTemplate.exchange(getRootUrl() + "/getUser", HttpMethod.GET, user, User.class);
assertNotNull(getResponse);
assertEquals(getResponse.getStatusCode(), HttpStatus.OK);
but it throws an error on exchange for user that object is not suitable.
the method documentation is pretty straightforward
Execute the HTTP method to the given URI template, writing the given request entity to the request, and returns the response as ResponseEntity.
URI Template variables are expanded using the given URI variables, if any.
Specified by:
exchange in interface RestOperations
Parameters:
url - the URL
method - the HTTP method (GET, POST, etc)
requestEntity - the entity (headers and/or body) to write to the request may be null)
responseType - the type of the return value
uriVariables - the variables to expand in the template
you need change user to HttpEntity
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
JSONObject parm = new JSONObject();
parm.put("user", user);
HttpEntity<JSONObject> entity = new HttpEntity<JSONObject>(parm, headers);
I want to use this RestTemplate code to make POST requests.
#Bean(name = "simpleRestTemplate")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(getClientHttpRequestFactory());
restClient.getInterceptors().add(new BasicAuthorizationInterceptor("username", "password"));
HttpEntity<PaymentTransaction> request = new HttpEntity<>(new PaymentTransaction());
ResponseEntity<PaymentTransaction> response = restClient.exchange("http://example.com", HttpMethod.POST,
request, PaymentTransaction.class);
PaymentTransaction foo = response.getBody();
return restClient;
}
How I can add Toke authentication into the HTTP link?
Probably the easiest way is to use exchange("http://example.com" + "/" + token, HttpMethod.POST,
Is there any better way?
Check out UriComponentsBuilder:
URI uri = UriComponentsBuilder.fromUriString("http://example.com")
.pathSegment(token)
.build()
.toUri();
Then you can use exchange() that takes a URI as its first parameter.
restClient.exchange(uri, HttpMethod.POST, request, PaymentTransaction.class);
As #nickb commented, authentication is best done in HTTP headers.
If you really need to inject a token in the URL, you can implement a custom interceptor.
Pseudo code:
final String tokenValue = "something";
restClient.getInterceptors().add(new ClientHttpRequestInterceptor() {
#Override
ClientHttpResponse intercept(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution)
throws java.io.IOException {
URI modifiedUri = UriComponentsBuilder.fromUri(request.getURI())
.query("token={tokenPlaceholder}")
.buildAndExpand(tokenValue)
.toUri();
request.setURI(modifiedUri);
}
});
There are many reasons for not doing that, for instance:
Systems that intercept and logs URL would log the token too, allowing 3rd parties to impersonate your users
You need to parse the token from the URL while dealing the rest of the query in the POST body request
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/client/ClientHttpRequestInterceptor.html
I'm completely new to Java and trying to consume a rest API with Spring Boot in Gradle, so far I've managed to make a very basic get request to display a message like below
#RestController
public class HelloController {
#RequestMapping(value = "/hello", method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
model.addAttribute("message", "Hello");
return "hello";
}
}
Now, how to extend this get request to make HTTP requests consume an endpoint based on RestTemplate, assuming this is my endpoint that i want to consume like below:
RestTemplate restTemplate = new RestTemplate(); ResponseEntity response = restTemplate.getForEntity("http://aws.services.domain.com/country/id", String.class);
Finally, I want to achieve authorized HTTP GET requests by adding a token Bearer in the Authorization header.
Thank you for answers and suggestions in advance
If you want to add a header, you have to use exchange or execute method.
So, in your case:
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Your Bearer Token");
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, entity, String.class, param);