Getting original request in a REST method( springframework ) - java

Is there a way to get the http request (header content) in a REST method? I'm using spring framework.
I want to build a new request from the current request to a different server in the REST method. This is more like a proxy/forwarding service - so I want to preserve the stuff I want in the original request.
I do not have much options -otherwise I would not have used REST for such stuff.
Does spring framework provide such interface?
For example - if I want to get hold of the request headers in the greeting method in the code below ( example code from spring.io )
#Controller
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public #ResponseBody Greeting greeting(
#RequestParam(value="name", required=false, defaultValue="World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}

You can simply give your handler method a parameter of type HttpServletRequest and Spring will provide it for you.
#RequestMapping("/greeting")
public #ResponseBody Greeting greeting(
#RequestParam(value="name", required=false, defaultValue="World") String name,
HttpServletRequest request) {
HttpServletRequest provides a number of methods to retrieve HTTP headers.
Alternatively, Spring also provides the #RequestHeader annotation that can be used like #RequestParam to retrieve a header from the HTTP request.

Related

How to get information about HTTP request down the stack of the Spring framework handler method

Is there a way to get information about HTTP request from the method down the callstack of the Spring request handler method?
In other words given I have a handler method like:
#GetMapping("/hello")
public String hello(#RequestParam(value = "name", defaultValue = "World") String name) {
MyInternalClass.doSomeAction();
return String.format("Hello %s!", name);
}
I am looking for means to get the information about HTTP request (such as URL, headers, etc.) within the code of the doSomeAction() static method in the MyInternalClass class.
The constraint is that I cannot modify the original method (hello()).
You can add a Request parameter of type HttpServletRequest
#GetMapping("/hello")
public String hello(
#RequestParam(value = "name", defaultValue = "World") String name,
HttpServletRequest originalRequest) {
// HERE: call another method here
return String.format("Hello %s!", name);
}
Have a look at the Spring Reference Documentation, Chapter "Method Arguments"
Part 2
However, I was looking for a method that does not force developers to change their code. I will try to add an example to my question, so it will be more verbose.
You can use the RequestContextHolder to get the request attributes.
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes())
.getRequest();
RequestContextHolder.getRequestAttributes() is a static method, that can be invoked from every where (even for a class that is no Spring Bean). But it is required that it is invoked from a thread that was triggert by a HTTP request.

How to get headers of MultipartFile?

Using spring MVC I receive multipart files in the controller this way
#RestController
public class FilesController {
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(
#RequestPart(value = "description") FileDescription fileDesc,
#RequestPart(value = "attachments", required = false) List<MultipartFile> attachments) {
Some parts of the multipart request may contain headers like "Content-ID", "Content-Location" and so on. But spring interface MultipartFile doesn't provide a method to get any header I want, only getContentType as I see. How I can get all provided headers?
Important point is that in request I could have multipart/mixed as a part of multipart/form-data. So every part of the message has its own map of headers. If I use #RequestHeader, I can see main headers of the request, but there are no headers of a specific part of multipart.
There might be another way, but the one I know of is to ask for a MultipartHttpServletRequest in your method signature.
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(MultipartHttpServletRequest multipartRequest)
You can ask for other arguments if need be.
This object allows you to access details of the multipart in a finer-grained way. For example, you can access each part's header using getMultipartHeaders(String paramOrFileName). You also have methods to access the files content this way, so you would not typically need to keep you #RequestPart inside the method signature.
We can also use javax.servlet.http.Part instead of MultipartFile. Interface Part has getHeader method.
#RequestPart(value = "attachments", required = false) List<Part> attachments
You can get all request headers by using this
#RequestHeader Map<String,String> headers After that, you can search for the header you are looking for.
You can use #RequestHeader annotation to retrieve all the headers from the request, like this:
#RestController
public class FilesController {
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(
#RequestHeader Map<String, String> headersMap
) {
// Use headersMap here
}
or if you want a single header's value, then you can specify the name of the header in #RequestHeader annotation, like this:
#RestController
public class FilesController {
#PostMapping(path = ("/files"), consumes = {"multipart/form-data", "multipart/mixed"})
public Reference createFile(
#RequestHeader("Content-ID") String contentId,
#RequestHeader("Content-Location") String contentLocation,
) {
// Use contentId, contentLocation here
}

Unable to send data to POST Service in Spring 4

I'm trying to write a simple HTTP REST service using Spring 4.
I'm having troubles sending data to a POST endpoint
#RequestMapping(value = "/onlyPost", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestParam("value1")
String param1, #RequestParam("value2") String param2) {
....
}
While trying to send data with Postman, I receive a 400 message (obviously the values are setted in the request's body)
"message": "Required String parameter 'value1' is not present",
What I have noticed is that the issue is somewhat related to the headers, because when I remove the postman's header (Content-Type: application/json) everything works fine.
I tried for more than one hour fixing this by myself with no results. Any hints?
#RequestParam is used to read a URL query parameter.
http://localhost:8080/springmvc/onlyPost?value1=foo&value2=bar
For instance, in the URL above, value1 and value2 are query parameters that you can read using that annotation.
But if you want to read a JSON request instead, you need to change the method to:
#RequestMapping(value = "/onlyPost", method = RequestMethod.POST,
consumes = "application/json")
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
....
}
where User is a POJO holding the two fields:
public class User {
private String value1;
private String value2;
// getters and setters...
}
HTTP 400 is returned when your request is badly formatted, i.e. missing required request parameters
#RequestParam is for URL Params, if you want to pass them like that, you call
<api_url>/onlyPost?value1=<value1>&value2=<value2>
but... if you want to create user you should rather use #RequestBody and put your user data there. Something like that:
#RequestMapping(value = "/users", produces = "application/json", method
= RequestMethod.POST)
public #ResponseBody ResponseEntity<?> createUser(#RequestBody User user) {
[...]
}
if you are creating REST api you should use concrete endpoints, here is a pretty cool reading with some tips: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

Spring REST Interceptor with dynamic values

Question in short: How can I pass a dynamic value to Spring REST interceptor after the RestTemplate is Autowired?
Now a detailed explanation:
I have a Spring REST interceptor as below:
public class HeaderRequestInterceptor implements ClientHttpRequestInterceptor {
private final String headerName;
private final String headerValue;
public HeaderRequestInterceptor(final String headerName, final String headerValue) {
this.headerName = headerName;
this.headerValue = headerValue;
}
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
HttpRequest wrapper = new HttpRequestWrapper(request);
wrapper.getHeaders().set(headerName, headerValue);
return execution.execute(wrapper, body);
}
}
And then I configure my RestTemplate as below with the above interceptor:
#Bean
public RestTemplate getRestTemplate() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new HeaderRequestInterceptor(<<MY-CUSTOM-HEADER>>, <<MY-DYNAMIC-VALUE>>));
final RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
Please look at the way I am creating the interceptor. MY-CUSTOM-HEADER is a constant, but MY-DYNAMIC-VALUE can change for every request. How can I make the interceptor take dynamic value?
PS: This is a standalone spring application (not web). It's a kind of client library that will be used to make REST calls.
I don't see the usage of interceptor if your headers are dynamic per request.
Request based scope bean on RestTemplate can solve the issue but it creates RestTemplate object per request.
Simply adding a static utility class called HttpRequestHeaderUtils.java with addHeader() method and call it before calling RestTemplate methods or wrap it on your own CustomRestTemplate to delegate all calls of RestTemplate to have single implementation.
CustomRestTemplate extends RestTemplate{
}
HttpRequestHeaderUtils.java
public static void addHeader(final HttpRequest request, final String headerName, final String headerValue)
HttpRequest wrapper = new HttpRequestWrapper(request);
wrapper.getHeaders().set(headerName, headerValue);
}
If you still want to use interceptor, you can hijack your intercept method
by storing the dynamic value as a request attribute (which is not clean, its same as setting header directly instead )
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
HttpRequest wrapper = new HttpRequestWrapper(request);
final String dynamicHeaderValue = request.getAttribute("myDynamicHeaderValue");
wrapper.getHeaders().set(headerName, dynamicHeaderValue!=null? dynamicHeaderValue : headerValue );
return execution.execute(wrapper, body);
}
If the header value can change in every request, the RestTemplate bean should have the request scope
#Bean(scope = DefaultScopes.REQUEST)
#ScopedProxy
public RestTemplate getRestTemplate(HttpRequest request) {
// ...
interceptors.add(new HeaderRequestInterceptor(
"custom-header"
, myDynamicVal));
//
}
I used a scoped proxy because I assumed the enclosing class is injected in the default (singleton) scope. Also note that I added the HttpRequest as a dependency in the getRestTemplate() method assuming that you need request data to fill the header - if you don't simply remove it and add whatever you like.

How to use #Inject in Spring MVC Controller?

I am working with Spring MVC controller. I have one of my controller as DataController.
I am thinking to add HttpServletRequest as injectable at the top of DataController class using #Inject.
#Controller
public class DataController {
#Inject
HttpServletRequest request;
// .. some code here
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow) {
String ipAddress = request.getRemoteAddr();
System.out.println(ipAddress);
}
So my question is - Is this the right way to use #Inject? I have never used #Inject before so trying to learn whether the way I am doing it is right or not? Since everytime, who is making call to processTask method, I need to grab its ipAddress whoever is calling that processTask method.
In terms of acquiring HttpServletRequest: semantically speaking, it is definitely wrong.
Reason: HttpServletRequest is an object that is created only when users send requests and is destroyed once the requested user action is completed. You simply can store it that way (from syntax angle) but you shouldn't (from semantic angle). You need to realize that the way how web application works is not exactly same as a desktop application (and don't observe them from the same angle).
Suggestion:
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow, HttpServletRequest request) {...}
In this way you will get the corresponding request each time the processTask method is called. (HttpServletRequest object is injected by #RequestMapping.)
(If you would like to preserve something through out a session, consider use a bean that is
Suggestion: #Inject private UserService userService;
(assume we have a class registered called UserService.)
You cannot "inject" the HttpServletRequest the only way to use it as far as I know is to added as a method member. Like this:
#Controller
public class DataController {
// .. some code here
#RequestMapping(value = "process", method = RequestMethod.GET)
public #ResponseBody
DataResponse processTask(#RequestParam("workflow") final String workflow,HttpServletRequest request) {
String ipAddress = request.getRemoteAddr();
System.out.println(ipAddress);
}
look also at Spring MVC #AutoWired response not working

Categories