springframework get all request headers - java

I know that spring3 has #RequestHeader to get a single request header in a controller. I'm wondering if there is an easy way to get ALL the request headers? I'm hoping for something like this:
#RequestMapping(value="/some/url",RequestMethod.GET)
public void endpoint(RequestParams params, BindingResult result, #RequestHeader MultiValueMap<String,String> headers, HttpServletRequest request, ModelMap model) {
}
Currently I'm doing something like this:
MultiValueMap<String,String> headers = new HttpHeaders();
for (Enumeration names = request.getHeaderNames(); names.hasMoreElements();) {
String name = (String)names.nextElement();
for (Enumeration values = request.getHeaders(name); values.hasMoreElements();) {
String value = (String)values.nextElement();
headers.add(name,value);
}
}

From the Javadocs:
#RequestHeader can be used on a Map, MultiValueMap, or HttpHeaders method parameter to gain access to all request headers.
More info is available online here and there.

if you don't want to read doc:
mappingMethodName(#RequestHeader Map<String, String> headers) {
headers.forEach((key, value) -> {
System.out.printf("Header '%s' = %s%n", key, value);
});
}

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.

Send multiple key in Feign Client

I have a nxt request POST with form url encoded using Feign Client
#FeignClient(
url = "${url}", configuration = NxtApi.Configuration.class)
public interface NxtApi {
#PostMapping(value = "nxt", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
String new(
#RequestParam String requestType, #RequestBody Map<String, ?> payload);
class Configuration {
#Bean
Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> converters) {
return new SpringFormEncoder(new SpringEncoder(converters));
}
#Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
}
I want to send the same key with two values
Map<String, Object> param = new HashMap<>();
param.put("filter", valueOne);
param.put("filter", valueTwo);
api.new("asset",param);
I need something like that
filter=valueOne&filter=valueTwo
But it's being sent like this (Request response in the log)
filter=[valueOne,valueTwo]
Thanks for any help.
You will have to use a List of String values instead of a Map.
#PostMapping(value = "nxt", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
String new(#RequestParam String requestType, #RequestParam("filter") List<String> filter, #RequestBody Map<String, ?> payload);
as I found it here: Spring Cloud Feign Client #RequestParam with List parameter creates a wrong request

Spring #RestController how to get header at class level [duplicate]

I am new to web programming in general, especially in Java, so I just learned what a header and body is.
I'm writing RESTful services using Spring MVC. I am able to create simple services with the #RequestMapping in my controllers. I need help understanding how to get HTTP header information from a request that comes to my method in my REST service controller. I would like to parse out the header and get some attributes from it.
Could you explain how I go about getting that information?
When you annotate a parameter with #RequestHeader, the parameter retrieves the header information. So you can just do something like this:
#RequestHeader("Accept")
to get the Accept header.
So from the documentation:
#RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(#RequestHeader("Accept-Encoding") String encoding,
#RequestHeader("Keep-Alive") long keepAlive) {
}
The Accept-Encoding and Keep-Alive header values are provided in the encoding and keepAlive parameters respectively.
And no worries. We are all noobs with something.
You can use the #RequestHeader annotation with HttpHeaders method parameter to gain access to all request headers:
#RequestMapping(value = "/restURL")
public String serveRest(#RequestBody String body, #RequestHeader HttpHeaders headers) {
// Use headers to get the information about all the request headers
long contentLength = headers.getContentLength();
// ...
StreamSource source = new StreamSource(new StringReader(body));
YourObject obj = (YourObject) jaxb2Mashaller.unmarshal(source);
// ...
}
My solution in Header parameters with example is user="test" is:
#RequestMapping(value = "/restURL")
public String serveRest(#RequestBody String body, #RequestHeader HttpHeaders headers){
System.out.println(headers.get("user"));
}
You can use HttpEntity to read both Body and Headers.
#RequestMapping(value = "/restURL")
public String serveRest(HttpEntity<String> httpEntity){
MultiValueMap<String, String> headers =
httpEntity.getHeaders();
Iterator<Map.Entry<String, List<String>>> s =
headers.entrySet().iterator();
while(s.hasNext()) {
Map.Entry<String, List<String>> obj = s.next();
String key = obj.getKey();
List<String> value = obj.getValue();
}
String body = httpEntity.getBody();
}

OAuth2RestTemplate adds charset into content type header

When trying to get an access token using OAuth2RequestTemplate the call sends the below header in a request
"Content-Type", "application/x-www-form-urlencoded;charset=UTF-8”
I’d like to drop the charset to not be included there.
I tried to set header value manually through AccessTokenRequest object and a CustomTokenProvider, but it did not work.
Any idea why it is actually including it there and how to get rid of it.
UPDATE : Including the code sample
OPTION 1 :
String oauthServerUri = "..../access_token";
ClientCredentialsResourceDetails ccDetails = new ClientCredentialsResourceDetails();
ccDetails.setClientId("clientId");
ccDetails.setClientSecret("clientSecret");
ccDetails.setGrantType("client_credentials");
ccDetails.setAccessTokenUri(oauthServerUri);
AccessTokenRequest tokenRequest = new DefaultAccessTokenRequest();
Map<String, List<String>> headers = new HashMap<>();
headers.put("Content-Type", Arrays.asList("Some Proper Value"));
tokenRequest.setHeaders(headers);
OAuth2ClientContext context = new DefaultOAuth2ClientContext(tokenRequest);
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(ccDetails, context);
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
OPTION 2 :
As an alternative I have tried the approach described here :
How to set HTTP Header for OAuth2RestTemplate
Which is implementing AccessTokenProvider and setting headers in obtainAccessToken. But this did not help either.
When you make the constructor new OAuth2RestTemplate(ccDetails, context); behind it makes a super() which makes a RestTemplate because it extends from it.
public OAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
super();
if (resource == null) {
throw new IllegalArgumentException("An OAuth2 resource must be supplied.");
}
this.resource = resource;
this.context = context;
setErrorHandler(new OAuth2ErrorHandler(resource));
}
The RestTemplate constructor puts messageConverters by default.
public RestTemplate() {
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
......
Maybe a solution could be that once created the object OAuth2RestTemplate you make a restTemplate.setMessageConverters(messageConverters) with the MediaType that interests you, from this method (inside RestTemplate class) deletes the previous ones:
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required");
// Take getMessageConverters() List as-is when passed in here
if (this.messageConverters != messageConverters) {
this.messageConverters.clear();
this.messageConverters.addAll(messageConverters);
}
}
EDIT:
If you see the image above, you can see when you do:
restTemplate.getAccessToken();
call to
getRequestCallback(resource, form, headers), extractor, form.toSingleValueMap());
protected RequestCallback getRequestCallback(OAuth2ProtectedResourceDetails resource,
MultiValueMap<String, String> form, HttpHeaders headers) {
return new OAuth2AuthTokenCallback(form, headers);
}
and look what his constructor does:
/**
* Request callback implementation that writes the given object to the request stream.
*/
private class OAuth2AuthTokenCallback implements RequestCallback {
private final MultiValueMap<String, String> form;
private final HttpHeaders headers;
private OAuth2AuthTokenCallback(MultiValueMap<String, String> form, HttpHeaders headers) {
this.form = form;
this.headers = headers;
}
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getHeaders().putAll(this.headers);
request.getHeaders().setAccept(
Arrays.asList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_FORM_URLENCODED));
FORM_MESSAGE_CONVERTER.write(this.form, MediaType.APPLICATION_FORM_URLENCODED, request);
}
}
Maybe through inheritance and overwriting methods, you can pass on to that builder the headers that interest you.
Also in OAuth2AccessTokenSupport the method retrieveToken has interesting comments:
// Prepare headers and form before going into rest template call in case the URI is affected by the result
authenticationHandler.authenticateTokenRequest(resource, form, headers);
// Opportunity to customize form and headers
tokenRequestEnhancer.enhance(request, resource, form, headers);
I hope I helped you.
You could use a interceptor to wrap your request, see ClientHttpRequestInterceptor#intercept:
intercept
ClientHttpResponse intercept(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution)
throws IOException
Intercept the given request, and return a response. The given ClientHttpRequestExecution allows the interceptor to pass on the request and response to the next entity in the chain.
A typical implementation of this method would follow the following pattern:
Examine the request and body
Optionally wrap the request to filter HTTP attributes.
Optionally modify the body of the request.
Either
execute the request using ClientHttpRequestExecution.execute(org.springframework.http.HttpRequest, byte[]),
or
do not execute the request to block the execution altogether.
Optionally wrap the response to filter HTTP attributes.
Your modified code:
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(ccDetails, context);
restTemplate.setInterceptors(Arrays.asList(new new RestTemplateHeaderModifierInterceptor()));
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
with
public class RestTemplateHeaderModifierInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpRequest requestWrapper = new CustomHttpRequestWrapper(request);
return execution.execute(requestWrapper, body);
}
}
public class CustomHttpRequestWrapper extends HttpRequestWrapper {
public CustomHttpRequestWrapper(HttpRequest request) {
super(request)
}
#Override
public HttpHeaders getHeaders() {
// return all headers, but change the charset
}
}

Optional Request Header in Spring Rest Service

I'm using Spring Restful web service & having request body with request header as shown below:
#RequestMapping(value = "/mykey", method = RequestMethod.POST, consumes="applicaton/json")
public ResponseEntity<String> getData(#RequestBody String body, #RequestHeader("Auth") String authorization) {
try {
....
} catch (Exception e) {
....
}
}
I want to pass one more optional request header called "X-MyHeader". How do I specify this optional request header in Spring rest service?
Also, how do I pass this same value in response header??
Thanks!
UPDATE: I just found that I can set required=false in request header, so one issue is resolved. Now, the only issue remaining is how do I set the header in the response??
Use required=false in your #RequestHeader:
#PostMapping("/mykey")
public ResponseEntity<String> getData(
#RequestBody String body,
#RequestHeader(value = "Auth", required = false) String authorization) {}
This question is answered here:
In Spring MVC, how can I set the mime type header when using #ResponseBody
Here is a code sample from: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-httpentity
#RequestMapping("/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) throws UnsupportedEncodingException {
String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader");
byte[] requestBody = requestEntity.getBody();
// do something with request header and body
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
}

Categories

Resources