How to get request URL in Spring Boot RestController - java

I am trying to get the request URL in a RestController. The RestController has multiple methods annotated with #RequestMapping for different URIs and I am wondering how I can get the absolute URL from the #RequestMapping annotations.
#RestController
#RequestMapping(value = "/my/absolute/url/{urlid}/tests"
public class Test {
#ResponseBody
#RequestMapping(value "/",produces = "application/json")
public String getURLValue(){
//get URL value here which should be in this case, for instance if urlid
//is 1 in request then "/my/absolute/url/1/tests"
String test = getURL ?
return test;
}
}

You may try adding an additional argument of type HttpServletRequest to the getUrlValue() method:
#RequestMapping(value ="/",produces = "application/json")
public String getURLValue(HttpServletRequest request){
String test = request.getRequestURI();
return test;
}

If you don't want any dependency on Spring's HATEOAS or javax.* namespace, use ServletUriComponentsBuilder to get URI of current request:
import org.springframework.web.util.UriComponentsBuilder;
ServletUriComponentsBuilder.fromCurrentRequest();
ServletUriComponentsBuilder.fromCurrentRequestUri();

Allows getting any URL on your system, not just a current one.
import org.springframework.hateoas.mvc.ControllerLinkBuilder
...
ControllerLinkBuilder linkBuilder = ControllerLinkBuilder.linkTo(methodOn(YourController.class).getSomeEntityMethod(parameterId, parameterTwoId))
URI methodUri = linkBuilder.Uri()
String methodUrl = methodUri.getPath()

Add a parameter of type UriComponentsBuilder to your controller method. Spring will give you an instance that's preconfigured with the URI for the current request, and you can then customize it (such as by using MvcUriComponentsBuilder.relativeTo to point at a different controller using the same prefix).

Related

Find which url was used to access the controller when multiple url mapping to the same controller method

I see Spring MVC multiple url mapping to the same controller method
So now I have a method defined as
#RequestMapping(value = {"/aaa", "/bbb", "/ccc/xxx"}, method = RequestMethod.POST)
public String foo() {
// was it called from /aaa or /bbb
}
At run time, I want to know if the controller was called from /aaa or /bbb
You can use HttpServletRequest#getServletPath which:
Returns the part of this request's URL that calls the servlet. This
path starts with a "/" character and includes either the servlet name
or a path to the servlet, but does not include any extra path
information or a query string.
As follow:
#RequestMapping(value = {"/aaa", "/bbb", "/ccc/xxx"}, method = RequestMethod.POST)
public String foo(HttpServletRequest request) {
String path = request.getServletPath(); // -> gives "/aaa", "/bbb" or "/ccc/xxx"
}

Spring restful API, is there a method being used like router to get other method's end points or URL?

#RequestMapping("/accounts")
public class controller {
#GetMapping("/get/{id}")
public final ResponseEntity<?> getHandler(){
}
#PostMapping(value = "/create")
public final ResponseEntity<?> createHandler(){
/*
trying to use some spring library methods to get the url string of
'/accounts/get/{id}' instead of manually hard coding it
*/
}
}
This is the mock code, now I am in createHandler, after finishing creating something, then I want to return a header including an URL string, but I don't want to manually concat this URL string ('/accounts/get/{id}') which is the end point of method getHandler(), so I am wondering if there is a method to use to achieve that? I know request.getRequestURI(), but that is only for the URI in the current context.
More explanation: if there is some library or framework with the implementation of route:
Routes.Accounts.get(1234)
which return the URL for the accounts get
/api/accounts/1234
The idea is, that you don't need to specify get or create (verbs are a big no-no in REST).
Imagine this:
#RequestMapping("/accounts")
public class controller {
#GetMapping("/{id}")
public final ResponseEntity<?> getHandler(#PathVariable("id") String id) {
//just to illustrate
return complicatedHandlerCalculation(id).asResponse();
}
#PostMapping
public final ResponseEntity<?> createHandler() {
//return a 204 Response, containing the URI from getHandler, with {id} resolved to the id from your database (or wherever).
}
}
This would be accessible like HTTP-GET: /api/accounts/1 and HTTP-POST: /api/accounts, the latter would return an URI for /api/accounts/2 (what can be gotten with HTTP-GET or updated/modified with HTTP-PUT)
To resolve this URI, you could use reflection and evaluate the annotations on the corresponding class/methods like Jersey does.
A Spring equivalent could be:
// Controller requestMapping
String controllerMapping = this.getClass().getAnnotation(RequestMapping.class).value()[0];
and
//Method requestMapping
String methodMapping = new Object(){}.getClass().getEnclosingMethod().getAnnotation(GetMapping.class).value()[0];
taken from How do i get the requestmapping value in the controller?

Spring path params with multiple slash

I have a Spring boot app where I have an API that takes other urls as path params. For example:
host:port/{eid} is my base path and after this I can have URLs like
host:port/{eid}/abc
host:port/{eid}/abc/pqr/
host:port/{eid}/abc/pqr/b=2
host:port/{eid}/abc/pqr/xyz
host:port/{eid}/abc/pqr/xyz?a=1
...and so on...
I would like to define a controller that I can map to all the above URLs and that should work something like
#RequestMapping(value = "/{eid}/{urlParts}", method = RequestMethod.GET)
public ResponseEntity<Object> share(
#PathVariable String eid,
#PathVariable String urlParts) {
......
}
I tried using #PathVariable Map<String, String> path and also #RequestMapping(value = "/{eid}/{urlParts:.+}"
but couldn't get the expected result.
Is there any solution to receive path slash(/) in path param.
Note: I can not URL encode the slash(/) in the URL. That's not an option for me.
I know the query is too old but still it's useful and this answer can help others.
You can get the full url parts using request attribute as below.
#RequestMapping(value = "/{eid}/**", method = RequestMethod.GET)
public ResponseEntity<Object> share(#PathVariable String eid, HttpServletRequest request) {
Object uriObject = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (null != uriObject) {
String urlParts = uriObject.toString().replaceFirst("^/" eid + "/", "");
}
....
}
why don't you try #RequestParam to take url if you working with jsp or other stuff..
#PathVariable means that the annotated method argument should be extracted from the path of the invoked URL. #RequestParam means that the annotated method argument must be extracted from the request parameters. None of these annotations cause the annotated arguments to be put in the request, session or application scope.
so you use your map also...
${username} means "write the value of the username attribute (found in page, or request, or session, or application scope) in the response". Since you didn't include any username attribute in any of those scopes, it doesn't write anything.
The code would work if the method returned a ModelAndView object, and the model contained a username attribute and a studentid attribute.
you can refer below code and link :
First URL : localhost:8080/projectName/test?firstname=john
Second URL :localhost:8080/projectName/test?firstname=john&secondname=roy
#Controller
#RequestMapping("/test")
public class TestController {
#RequestMapping(value = { "/test/{firstname}/test" }, method = { RequestMethod.GET })
public String someMethod(#PathVariable("firstname") String firstname){
return someMethod(firstValue )
}
#RequestMapping(value = { "/test/{firstname}/{otherString}/test" }, method = { RequestMethod.GET })
public String someOtherMethod(#PathVariable("firstname") String firstname, #PathVariable("secondname") String secondValue) {
return someMethod(firstValue + "/" + secondValue)
}
}
so I am not sure if there is a direct spring implementation to doing this however, you could us a mixture of things.
#RequestParam - returns a map of the URL params (succeeding the ?)
#PathVariable - return the eid
HttpServletRequest - use the request to return the URI and strip host:port/{eid} and anything after ? , then use Arrays.asList(str.split("/")); (remember this is a wrapper of an array use new ArrayList<Sting>(Arrays.asList(str.split("/"))) )
#RequestMapping(value = "/{eid}", method = RequestMethod.GET)
public ResponseEntity<Object> share(
#PathVariable String eid,
#RequestParam Map<String,String> allRequestParams,
HttpServletRequest request) {
......
}

Reactive Spring does not support HttpServletRequest as parameter in REST endpoint?

I created a RestController which look like this :
#RestController
public class GreetingController {
#RequestMapping(value = "/greetings", method = RequestMethod.GET)
public Mono<Greeting> greeting(HttpServletRequest request) {
return Mono.just(new Greeting("Hello..." + request.toString()));
}
}
Unfortunately when I try to hit the "greetings" endpoint I get an exception :
java.lang.IllegalStateException: No resolver for argument [0] of type
[org.apache.catalina.servlet4preview.http.HttpServletRequest]
I am using
compile('org.springframework.boot.experimental:spring-boot-starter-web-reactive')
How to fix this ?
Link to full stack-trace. Link to build.gradle
----------EDIT----------
Using the interface. Now getting :
java.lang.IllegalStateException: No resolver for argument [0] of type
[javax.servlet.http.HttpServletRequest] on method (rest is same)
You should never use the Servlet API in a Spring Reactive Web application. This is not supported and this is making your app container-dependent, whereas Spring Web Reactive can work with non-Servlet runtimes such as Netty.
Instead you should use the HTTP API provided by Spring; here's your code sample with a few changes:
import org.springframework.http.server.reactive.ServletServerHttpRequest;
#RestController
public class GreetingController {
#GetMapping("/greetings")
public Mono<Greeting> greeting(ServerHttpRequest request) {
return Mono.just(new Greeting("Hello..." + request.getURI().toString()));
}
}
You can inject either ServerWebExchange or directly ServerHttpRequest / ServerHttpResponse.
I went deep into the call hierarchy and found that there is this class
InvocableHandlerMethod, in package org.springframework.web.reactive.result.method
, which has :
private List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
There is a resolveArguments() method in this class, which is called to "resolve the arguments". Unfortunately there is no resolver for
javax.servlet.http.HttpServletRequest in this list.
There is however a ServerWebExchangeArgumentResolver which is able to resolve ServletServerHttpRequest, from this I can extract the HttpServletRequest. Yeaaa....
So the endpoint looks like :
#RequestMapping(value = "/greetings", method = RequestMethod.GET)
public Mono<Greeting> greeting(ServletServerHttpRequest servletServerHttpRequest) {
HttpServletRequest httpServletRequest = servletServerHttpRequest.getServletRequest();
.
.
.
}
It is important that the ServletServerHttpRequest be from the package org.springframework.http.server.reactive

FileSystemResource as ResponseBody in spring rest controller gives ClassCastException

I have defined a controller which serves local content like this:
#RequestMapping(value = PATH_CONTENT, method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<FileSystemResource> getContent(
#PathVariable(URI_PATH_PARAM_CONTAINER_UID) String containerUid,
#PathVariable(URI_PATH_PARAM_DOC_UID) String docUid) {
...
}
In the client side:
I am adding ResourceHttpMessageConverter to RestTemplate messageConverters.
When I finally make the following call:
ResponseEntity<FileSystemResource> response = getMyRestClient()
.getContent(url, null, FileSystemResource.class);
FileSystemResourcecontent = response.getBody();
This produces a ClassCastException saying it cant cast ByteArrayResource to FileSystemResource.
I can do some modification to my code to return a Resource instead of FileSystemResource and things will work. But I am really wondering why is this exception? I also don't want to take the path of writing to HttpServletResponse directly as I want to use Spring's HttpMessageConverter framework.

Categories