Remove direct access to "/" on spring boot - java

I have a spring boot application where i have only 2 endpoints defined.
#RequestMapping(value = "/getAllFeatures", method = RequestMethod.GET)
#Cacheable("features")
public ResponseEntity<?> getAllFeatures() {
...
}
#RequestMapping(name = "/getFeatureStatus", method = RequestMethod.GET)
#Cacheable("features")
public ResponseEntity<?> getFeatureStatus(#RequestParam(value = "groupName", required = false) String groupName,
#RequestParam(value = "featureName", required = false) String featureName) {
...
}
And i have defined the context to be server.context-path=/abc
How the problem is when i do a GET call on /abc/ the application gives me a valid response. Where as i have never mapped "/" in my rest controller. Any ideas on how to block requests to "/". Also this application doesn't require any kind of spring security.

It should be
#RequestMapping(path= "/getFeatureStatus"....)
instead of
#RequestMapping(name = "/getFeatureStatus"....)
the name attribute just assigns a name to this mapping.

Sometimes "/" endpoint is mapped to index.html file of your resource folder.
Does this file exist ?
You also need to annotate your class with #RestController.

Related

Zero length part of URL in Spring controller RequestMapping PathVariable breaks resolution

I'm trying to make an app's REST API more RESTful and it feels like I'm not using the Spring RequestMappings in the way intended.
I have a single GET end point for doing reads:
#RequestMapping(value = "thing/{thingName}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThing(
#PathVariable(value = "thingName", required = false)
String thingName,
#RequestParam(value = "findByComponent", required = false)
String findByComponentQuery,
#AuthenticationPrincipal User user) {
...
To be more restful, I want this endpoint to do both:
GET /thing/{thingName} returns a single thing having that name
GET /thing or /thing/ with query params returns lists of things
So in my controller, I can test whether {thingName} is null or zero-length and if so, check the query params for known query names.
However calling this with http://localhost:8080/thing/?findByComponent=component123 returns a 404 from Spring with this logging:
12:45:18.485 PageNotFound : No mapping found for HTTP request with URI [/thing/] in DispatcherServlet with name 'dispatcher' : WARN : XNIO-1 task-3 : org.springframework.web.servlet.DispatcherServlet
Spring does not allow path variables ({thingName}) to be mapped to an empty String. In practice, this means that the URL /thing/?findByComponent=component123 does not map to thing/{thingName} with an empty {thingName}, but rather, it expects there to be some mapping for thing. Since there is no endpoint that maps to the path thing (without the path variable), a 404 error is returned.
To solve this issue, you can break this single endpoint into two separate endpoints:
#RequestMapping(value = "thing/{thingName}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThing(
#PathVariable(value = "thingName") String thingName,
#AuthenticationPrincipal User user) {
// ...
}
#RequestMapping(value = "thing",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThings(,
#RequestParam(value = "findByComponent", required = false) String findByComponentQuery,
#AuthenticationPrincipal User user) {
// ...
}
For more information, see With Spring 3.0, can I make an optional path variable?.
The required=false flag allows for two types of requests:
/thing
/thing/<some_value>
Strictly speaking, including a trailing slash at the end of the URL (i.e. /thing/) means that a decision was made to include a value for the path variable, but none was provided. In the context of REST APIs, /thing and /thing/ are two different endpoints, where the latter means that a value after the trailing slash was expected.
A workaround for not having to create three separate request mappings (one for each case above) is to set the #RequestMapping value for the controller to the base path and then have a "" and "/{thingName} request mapping for the two endpoints:
#RestController
#RequestMapping("thing")
public class ThingController {
#RequestMapping(value = "/{thingName}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThing(
#PathVariable(value = "thingName") String thingName) {
return "foo";
}
#RequestMapping(value = "",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThings(
#RequestParam(value = "findByComponent", required = false) String findByComponentQuery) {
return "bar";
}
}
In this case, the following mappings will occur:
/thing: getThings
/thing/: getThings
/thing/foo: getThing
An example of this workaround, including test cases can be found here.

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 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) {
......
}

How to get request URL in Spring Boot RestController

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).

what is the dif between requestMapping on controller and method

If I have:
#RequestMapping("/user")
public class RegistrationController {
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String getRegisterPage(Model model) {
What is the difference? I mean what will happen if I remove the /user mapping, will my /register mapping still work?
A #RequestMapping on the class level is not required. Without it, all paths are simply absolute, and not relative.
see 15.3.2 Mapping requests with #RequestMapping
This means if you specify the classlevel annotations, the url shall be relative, so for register it shall be /user/register(URL to Handler mapping) and likewise.
As described here you can also use Type level mapping and relative path mappings on method level to be dry and don't duplicate root at every methods.
#Controller
#RequestMapping("/employee/*")
public class Employee {
#RequestMapping("add")
public ModelAndView add(
#RequestParam(value = "firstName") String firstName,
#RequestParam(value = "surName") String surName) {
//....
}
#RequestMapping(value={"remove","delete"})
public ModelAndView delete(
//....
}
}
Spring doc: At the method level, relative paths (e.g. "edit.do") are supported within the primary mapping expressed at the type level.

Categories