I have a simple handler in my controller which returns a message
#RequestMapping(value = "/message")
#ResponseBody
public Message get() {
return new Message(penguinCounter.incrementAndGet() + " penguin!");
}
At the same time I can use something like this
#RequestMapping(value = "/message")
ResponseEntity<Message> get() {
Message message = new Message(penguinCounter.incrementAndGet() + " penguin!");
return new ResponseEntity<Message>(message, HttpStatus.OK);
}
What is the difference betweet this two approaches? Let's not take into account HttpStatus :)
ResponseEntity will give you some added flexibility in defining arbitrary HTTP response headers. See the 4th constructor here:
http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/http/ResponseEntity.html
ResponseEntity(T body, MultiValueMap<String,String> headers, HttpStatus statusCode)
A List of possible HTTP response headers is available here:
http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Responses
Some commonly-used ones are Status, Content-Type and Cache-Control.
If you don't need that, using #ResponseBody will be a tiny bit more concise.
HttpEntity represents an HTTP request or response consists of headers and body.
// Only talks about body & headers, but doesn't talk about status code
public HttpEntity(T body, MultiValueMap<String,String> headers)
ResponseEntity extends HttpEntity but also adds a Http status code.
// i.e ResponseEntity = HttpEntity + StatusCode
public ResponseEntity(T body, MultiValueMap<String,String> headers, HttpStatus statusCode)
Hence used to fully configure the HTTP response.
For Ex:
#ControllerAdvice
public class JavaWebExeptionHandler {
#Autowired
ExceptionErrorCodeMap exceptionErrorCodeMap;
#ExceptionHandler(RuntimeException.class)
public final ResponseEntity<ExceptionResponseBody> handleAllExceptions(Exception ex) {
Integer expCode = exceptionErrorCodeMap.getExpCode(ex.getClass());
// We have not added headers to response here, If you want you can add by using respective constructor
return new ResponseEntity<ExceptionResponseBody>(new ExceptionResponseBody(expCode, ex.getMessage()),
HttpStatus.valueOf(expCode));
}
}
#ResponseBody indicates that return value of method on which it is used is bound to the response body
(Mean the return value of method is treated as Http response body)
ResponseEntity<> is a generic class with a type parameter, you can specify what type of object to be serialized into the response body.
#ResponseBody is an annotation, indicates that the return value of a method will be serialized into the body of the HTTP response.
you can set headers using ResponseEntity<>
#ResponseEntity represents a response which includes headers, body and status code.
#ResponseBody only returns the body of the response.
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)
In my spring boot Application i have a scheduler which calls an API to generate token which expires in 15 min. Time of scheduler is also 15 min. please find below sample:
public class TokenGeneration {
private static String token = null;
#Scheduled(15 minutes)
public String fetchToken() {
// api call which return token
HttpEntity<model> response = restTemplate.exchange(uri, POST, entity, model.class);
token = response.getBody().getAccessToken();
}
}
I stored token value in static variable from a non static method so that i can use this token variable wherever i want to use token value. is this right approach ? if not plz let me know how i can achieve this.
Do i need to make TokenGeneration class singleton so that only one instance of this class is made throught application?
Also i want to create an interceptor or filter in which i can set Authorization headers and token value so that each request will populate authorization header automatically, i don't want to set authorization header in each request like this :
HttpHeaders headers = new HttpHeaders();
headers.set(CpsConstant.AUTHORIZATION, CpsConstant.BEARER + token);
So i tried with this custom interceptor :
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
String token = TokenGeneration.token;
request.getHeaders().add("Authorization", "Bearer " + token);
return execution.execute(request, body);
}
will add this interceptor in restTemplate in config file.
So is this right approach for both token generation as well as setting headers for each request or any improvements need to be done in this approach ?
Me thinking of calling token generation method in interceptor in case of token is null like :
if(token == null){
//call token generation fetchToken method
}
It is the right approach
Spring default scope is always singleton if not specified
It is ok to use interceptor, but what if you want to call a API without a token?
Best approach to use two separate methods to send request with token and without token using a separate class
#Component
public class RestClient {
#Autowired
RestTemplate restTemplate;
public HttpHeaders getRequestHeaderBearer() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add(HeaderParameters.AUTHORIZATION, HeaderParameters.BEARER +
TokenGeneration.token);
return headers;
}
public HttpHeaders getRequestHeader() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
public <T> ResponseEntity<T> restExchangeBearer(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeaderBearer()), classObj);
}
public <T> ResponseEntity<T> restExchange(String url, HttpMethod httpMethod,
Class<T> classObj) {
return restTemplate.exchange(url, httpMethod,
new HttpEntity<>("parameters", this.getRequestHeader()), classObj);
}
}
I am working on part of an API, which requires making a call to another external API to retrieve data for one of its functions. The call was returning an HTTP 500 error, with description "Content type 'application/octet-stream' not supported." The call is expected to return a type of 'application/json."
I found that this is because the response received doesn't explicitly specify a content type in its header, even though its content is formatted as JSON, so my API defaulted to assuming it was an octet stream.
The problem is, I'm not sure how to adjust for this. How would I get my API to treat the data it receives from the other API as an application/json even if the other API doesn't specify a content type? Changing the other API to include a contenttype attribute in its response is infeasible.
Code:
The API class:
#RestController
#RequestMapping(path={Constants.API_DISPATCH_PROFILE_CONTEXT_PATH},produces = {MediaType.APPLICATION_JSON_VALUE})
public class GetProfileApi {
#Autowired
private GetProfile GetProfile;
#GetMapping(path = {"/{id}"})
public Mono<GetProfileResponse> getProfile(#Valid #PathVariable String id){
return GetProfile.getDispatchProfile(id);
}
The service calling the external API:
#Autowired
private RestClient restClient;
#Value("${dispatch.api.get_profile}")
private String getDispatchProfileUrl;
#Override
public Mono<GetProfileResponse> getDispatchProfile(String id) {
return Mono.just(id)
.flatMap(aLong -> {
MultiValueMap<String, String> headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
return restClient.get(getDispatchProfileUrl, headers);
}).flatMap(clientResponse -> {
HttpStatus status = clientResponse.statusCode();
log.info("HTTP Status : {}", status.value());
return clientResponse.bodyToMono(GetProfileClientResponse.class);
// the code does not get past the above line before returning the error
}).map(GetProfileClientResponse -> {
log.debug("Response : {}",GetProfileClientResponse);
String id = GetProfileClientResponse.getId();
log.info("SubscriberResponse Code : {}",id);
return GetProfileResponse.builder()
// builder call to be completed later
.build();
});
}
The GET method for the RestClient:
public <T> Mono<ClientResponse> get(String baseURL, MultiValueMap<String,String> headers){
log.info("Executing REST GET method for URL : {}",baseURL);
WebClient client = WebClient.builder()
.baseUrl(baseURL)
.defaultHeaders(httpHeaders -> httpHeaders.addAll(headers))
.build();
return client.get()
.exchange();
}
One solution I had attempted was setting produces= {MediaType.APPLICATION_JSON_VALUE} in the #RequestMapping of the API to produces= {MediaType.APPLICATION_OCTET_STREAM_VALUE}, but this caused a different error, HTTP 406 Not Acceptable. I found that the server could not give the client the data in a representation that was requested, but I could not figure out how to correct it.
How would I be able to treat the response as JSON successfully even though it does not come with a content type?
Hopefully I have framed my question well enough, I've kinda been thrust into this and I'm still trying to figure out what's going on.
Are u using jackson library or jaxb library for marshalling/unmarshalling?
Try annotating Mono entity class with #XmlRootElement and see what happens.
I have a Spring Boot application using jax-rs with resteasy (3.0.24). I'm trying to get the HttpHeaders for a request as such:
#DELETE
#Path("/myendpoint")
public Response myMethod(#Context HttpHeaders headers, #Context HttpServletRequest request) {
// headers is always null
}
The headers param is always null even though I'm making the request with multiple headers. As an alternative, I'm extracting them via the HttpServletRequest.getHeaderNames(), but I'd really like know why headers is not populated.
Found the (embarrassing, although I deflect the blame to the author:)) error. #Context HttpHeaders headers was using Spring's implementation and not that from jax-rs.
You gotta get the headers using the #Context then check if the one one you want is there.
#Path("/users")
public class UserService {
#GET
#Path("/get")
public Response addUser(#Context HttpHeaders headers) {
String userAgent = headers.getRequestHeader("user-agent").get(0);
return Response.status(200)
.entity("addUser is called, userAgent : " + userAgent)
.build();
}
}
private ArrayList<NameValuePair> mParams;
HttpClient client = new DefaultHttpClient();
mParams = new ArrayList<NameValuePair>();
mParams.add(new BasicNameValuePair("testKey", "John"));
mParams.add(new BasicNameValuePair("testSerial", "003-100"));
HttpPost request = new HttpPost("http://localhost:8080/test/getRequiredEnv");
request.setEntity(new UrlEncodedFormEntity(mParams, HTTP.UTF_8));
HttpResponse response = client.execute(request);
// TestController.java
#RestController
public class TestController {
private static final Logger logger = Logger.getLogger(TestController.class);
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
#RequestParam("testKey") String testKey,
#RequestParam("testValue") String testValue,
#RequestHeader HttpHeaders headers) {
logger.info("Test Key [" + testKey + "]");
logger.info("Test Value [" + testValue + "]");
return new TestResponseInfo("0001", "ABC");
}
Can someone please tell me is this the correct way to get data from 'Request.setEntity' in SpringMVC rest controller or I am missing something?
Secondly, in postman "httpPost" request I pass the parameters (testKey & testValue) as headers or as body?
Thirdly, without knowing the parameters in httpPost request can I able to parse the incoming request and extract the parameters from it in Spring controller?
First of all it would be good to know the content-type of the request that is sent.
So I guess you want to get the body of the request. To get all request parameters if you don't know the parameter names beforehand you can use #RequestParam with type Map<String, String> to get all params:
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
#RequestParam Map<String, String> allParams,
#RequestHeader HttpHeaders headers)
But I am not sure if this works because it also depends on the content-type. E.g. for form data (application/x-www-form-urlencoded) have a look at the Spring documentation about #RequestBody which states about one of the default message converters FormHttpMessageConverter:
FormHttpMessageConverter converts form data to/from a MultiValueMap.
So try:
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
#RequestBody MultiValueMap<String, String>,
#RequestHeader HttpHeaders headers)
Alternatively there is also HttpServletRequest.getParameterMap() which gets you a Map. You can get the request by just including HttpServletRequest request as a method argument.
If you know the paramters beforehand, annotating your POJO that resembles the form data with #ModelAttribute should also work like so:
#RequestMapping(value = "/getRequiredEnv", method = RequestMethod.POST)
public #ResponseBody ResponseInfo getRequiredEnv(
##ModelAttribute Test myTestPojo,
#RequestHeader HttpHeaders headers)
Or you could also send data as application/json and when including jackson as a dependency, #Requestbody will map your data to a POJO. Have a look at e.g. Spring JSON request body not mapped to Java POJO.
In regard to your second question httpPost will pass the parameters as body since it is a POST request.