#Path equivalent in spring-boot - java

Say I have controller with a method defined like this:
#RestController
#RequestMapping("/{user-id}/foo")
public class FooController {
#RequestMapping("bar")
public BarController someBarLogic() {
//
}
}
Is it possible to go further than {user-id}/foo/bar without specifying the whole root-path ? (Is there a way to relativize the path like #Path annotation in Jersey or an equivalent annotation in Spring-Boot ?)

It looks like you're looking for ant-style wildcards in path patterns. Have a look here:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-patterns
Accordingly you can define a RequestMapping like this
#RestController
#RequestMapping("/foo")
public class DemoController {
#RequestMapping(value = "/bar/**", method = RequestMethod.GET)
public String getDemo() {
return "Hello world!";
}
}
which will match /foo/bar/baz.
Ok, another example based on your comment below:
#RestController
public class DemoController {
#RequestMapping(value = "/**/baz/**", method = RequestMethod.GET)
public String getDemo() {
return "Hello world!";
}
}
This will match the same url as above, and also /foo/bar/baz/bar/foo

Related

Switch endpoint responses based on condition

I would like to have two endpoints with the same path and decide which one is enabled on startup.
To do so I've tried using #ConditionalOnExpression() but an error is thrown as it says a mapping already exists. One endpoint uses ModelAndView while the other provides a html string so I can't add an if statement in the body of the endpoint.
Code
#ConditionalOnExpression("${my-property:false}")
#GetMapping(VIEW_INDEX)
public ModelAndView index(HttpServletRequest request) {
...
return modelAndView;
}
#ConditionalOnProperty("${my-property}")
#GetMapping(VIEW_INDEX)
public String otherIndex(){
return "/other/index";
}
Error
Ambiguous mapping. Cannot map 'controller' method
There is already 'controller' bean method
How can I allow only one to be enabled based on a condition without there being an Ambiguous mapping?
It will work if you try #ConditionalOnProperty or ConditionalOnExpression on 2 controllers with the same request mapping.
#RestController
#RequestMapping("/test")
#ConditionalOnProperty(value = "my-property", havingValue = "true")
public class TestTrueController {
#GetMapping("/index")
public String index() {
return "Forever true!";
}
}
#RestController
#RequestMapping("/test")
#ConditionalOnProperty(value = "my-property", havingValue = "false")
public class TestFalseController {
#GetMapping("/index")
public String index() {
return "Forever false!";
}
}
If your my-property value is true it will print "Forever true!", if false it will print "Forever false!".

Log url to Spring controller from inside it

Suppose you have a Spring MVC controller, something like this
#Controller
public class RestController {
#GetMapping(value = "/test")
public #ResponseBody Test getTestData(...) {
// console log path to controller: http://localhost:80/app/test
return testData;
}
}
Is it possible to log/print from inside the controller the url to it? In the example above the output would be something like https://localhost:80/app/test
Using .getRequestUrl from the servlet is not behaving correctly.
You can inject UriComponentsBuilder as parameter then use the method toUriString(). From the documentation, it is used to build a relative URI from the current request’s, this should work as your are expected Doc.
#Controller
public class RestController {
...
#GetMapping(value = "/test")
public #ResponseBody Test getTestData(UriComponentsBuilder ucb, ...) {
LOGGER.debug(ucb.toUriString());
// console log path to controller: http://localhost:80/app/test
return testData;
}
...
}

Can you set a dynamic value to #PreAuthorize in Spring?

Right now I use
#PreAuthorize("hasAuthority('CREATE_USER_PRIVILEGE')")
But I want the CREATE_USER_PRIVILEGE to come from a function(). Is this possible?
You could do something like this:
#RestController
class FooController {
#PreAuthorize("hasAuthority(#securityService.privilege)")
#GetMapping("/")
public ResponseEntity<String> helloSecurity(#RequestParam("id") Integer id){
return ResponseEntity.ok("Hello World");
}
}
#Service("securityService")
class SecurityService {
public String getPrivilege(){
return "CREATE_USER_PRIVILEGE";
}
}
Based on this great article
you have first to autowire your service using constructor or annotation then you can use the Spel language to use it as stated in the following example
#RequestMapping(value="/id/{domainObjectId}/dostuff", method=RequestMethod.POST, produces="application/json")
#PreAuthorize(value="hasRole('ROLE_DomainObjectAdmin') or #domainObjectServiceImpl.findDomainObject(#domainObjectId).getOwners().contains(#userAccount.getEmployee())")
public String setObjectiveComplete(#PathVariable String domainObjectId, UserAccount userAccount) {
// Do stuff
}
Based on the above solution, I've implemented something like this:
#Controller
class TestController {
//calling a PreAuthorize on method level/ can be used on class level as well
#PreAuthorize("hasAnyAuthority(#authorityService.authorities)")
#RequestMapping("/application")
public ModelAndView newPage() throws{
return new ModelAndView(view);
}
}
#Service("authorityService")
class AuthorityService{
#Value("${app.authorities}") // read roles from properties file
private String authorities;
public List<String> getAuthorities(){
// convert the comma separated Strings to list.
List<String> items = Arrays.asList(authorities.split("\\s*,\\s*"));
return items;
}
}

How to set defaults for #RequestMapping?

I'm using #RestController with #RequestMapping annotations to define all of my servlets with spring-mvc.
Question: how can I define some defaults for those annotation, so I don't have to repeat the same configuration regarding eg consumes and produces?
I'd like to always apply the following config, without having to repeat it on each path:
#GetMapping(produces = {APPLICATION_XML_VALUE, APPLICATION_JSON_VALUE})
#PostMapping(
consumes = {APPLICATION_XML_VALUE, APPLICATION_JSON_VALUE},
produces = {APPLICATION_XML_VALUE, APPLICATION_JSON_VALUE})
Probably it's easiest to just create a custom #RestController annotation and use that on classlevel. Then I only have to repeat the #PostMapping(consumes...) mappings:
#Target(ElementType.TYPE)
#Retention(value=RUNTIME)
#RestController
#RequestMapping(produces = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
public #interface DefaultRestController {
}
Usage like:
#DefaultRestController
public class MyServlet {
#GetMapping("/getmap") //inherits the 'produces' mapping
public void getmap() {
}
#PostMapping("/postmap", consumes = {APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE})
public void postmap() {
}
}
Better than nothing.
The target for RequestMapping annotation could be either a method or class. It can be used instead of GetMapping and PostMapping annotations that target only methods.
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/GetMapping.html
Specifically, #GetMapping is a composed annotation that acts as a
shortcut for #RequestMapping(method = RequestMethod.GET).
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/PostMapping.html
Specifically, #PostMapping is a composed annotation that acts as a
shortcut for #RequestMapping(method = RequestMethod.POST).
Assuming your Controller Name is HelloController, add the RequestMapping annotation with appropriate methods at Class level so that it applies automatically to all the paths.
#Controller
#RequestMapping(method={RequestMethod.GET,RequestMethod.POST}, consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE },produces = { MediaType.APPLICATION_XML_VALUE,MediaType.APPLICATION_JSON_VALUE },)
class HelloController{
}
This confgiuration can be overridden by annotating it in individual methods.
You can put an annotation on class. Here is an example:
#RestController
#RequestMapping(
consumes = {APPLICATION_XML_VALUE, APPLICATION_JSON_VALUE},
produces = {APPLICATION_XML_VALUE, APPLICATION_JSON_VALUE}
)
public class MyClass {
// after that you don't have to put any
// #RequestMapping default values before methods
}

AOP AfterReturning function returns twice

I have a little problem. I'm calling AfterReturning function after some function returns and my AfterReturning function's working twice and I don't want that. Here is the code:
#Aspect
#Component
#Configuration
public class AspectOP {
LogController logcontroller = new LogController();
#AfterReturning("execution(* com..*save*(..))")
public void doSomething() {
logcontroller.guestbook("XD");
}
}
I have 2 save function and we changed names but it's same again. I've tried remove #Component or #Aspect then it's not working.
EDIT
My save function
#Controller
#RequestMapping("/api")
public class EntryController {
#Autowired
EntryRepository repo;
Account account;
#RequestMapping(path = "/getir", method = RequestMethod.GET)
public #ResponseBody List<Entries> getir(){
return repo.findAll();
}
#RequestMapping(path = "/saveentry", method = RequestMethod.POST, consumes = "application/json")
public #ResponseBody Entries save(#RequestBody Entries entry) {
return repo.save(entry);
}
}
LogController.class
#Controller
public class LogController {
#MessageMapping("/guestbook")
#SendTo("/topic/entries")
public Log guestbook(String message) {
System.out.println("Received message: " + message);
return new Log(message);
}
}
My main objective is when something is saved, I send something to my socket. It's working but doSomething functions is working twice.
seems advice applied to your EntryRepository class as well. change your pointcut expression to something like, to be only applied to EntryController's save method
#AfterReturning("execution(* com.xyz.EntryController.save*(..))")
Examples here

Categories