I am working on spring mvc project. Here in each controller i have multiple method assigned to specific url. e.g.
#RequestMapping(value = "/accountDetails")
public String home(HttpServletRequest request){
Book book = (Book) request.getSession().getAttribute("Book");
if (book == null) return "redirect:/";
//other things to do here
return "home";
}
Here I want to check session variable Book is empty or not at the beginning of each method. if it is return to / otherwise do some operation.
Is there any other way to check this null and return rather that the i have done it in the above code. I don't want write the same code at the beginning to each controller method.
So please suggest me an alternative way
These are several solutions. As pointed out by #chrylis, you can use #ControllerAdvice, HandlerInterceptor or even a plain Filter (or it's DelegatingFilterProxy Spring flavour) for a generic, cross-cutting solution. Depending on your current project setup and your requirements one may be easier to implement than the other and it may or may not fit your needs, so make sure to just read the docs and decide if it fits your purpose.
Another approach with a fully programmatic solution would be to use a utility method with Java 8 lambda for the code block that you want to be executed in case book is available.
public static String withBook(Function<Book, String> bookOperation) {
Book book = (Book) RequestContextHolder
.currentRequestAttributes()
.getAttribute("Book", RequestAttributes.SCOPE_SESSION);
if (book == null) {
return "redirect:/";
} else {
return bookOperation.apply(book);
}
}
RequestContextHolder gives you access to the attributes of the current request and session.
You can use the utility method like this:
#RequestMapping(value = "/accountDetails")
public String home() {
return withBook(book -> {
// just implement the part where book is not null
return "home";
});
}
Related
I'm wondering if it's possible to make an AJAX call without the path being available to anyone who knows the URL. For example lets say I've the following code:
#GetMapping("/responsebody")
#ResponseBody
public UserAccount testingResponseBody(Principal principal) {
if(principal != null) {
UserAccount currentUser = userRepo.findByUserName(principal.getName());
return currentUser;
}else {
return null;
}
}
As far as I'm aware, this allows anyone who types in "/responsebody" in the search bar to be able to see the corresponding JSON.
But what if I don't want the corresponding data to be available to the public what do I do then. I just want to use it internally in the application.
I'm new to Spring boot and all this so i'd really appreciate if sombody could clear things up for me.
Thanks
In the code below there are two methods annotated with #GetMapping annotation, one expects empty path, another one expects a path variable.
#Controller
#RequestMapping("/")
public class BasicController {
#GetMapping()
public String get(Model model) {
// doing something
}
#GetMapping("/{variable}")
public String getWithPathVar(#PathVariable("variable") String variable, Model model) {
// doing something different
}
}
Problem: When the app is running and I hit "www.myurl.com/" it enters both methods even though there is no path parameter. How can I fix this?
If so it sounds like a bug or some misconfiguration with filters. I can't reproduce this behaviour on the Spring 5.2.7. Here's an article that explains how Spring works under the hood.
If you can't upgrade the Spring version you can use only single endpoint as a workaround.
#GetMapping("/{variable}")
public String getWithPathVar(#PathVariable("variable") String variable, Model model) {
// doing something different
if(variable != null) {
// fulfill the normal workflow
} else {
// call ex get() workflow
}
}
I have a web application which uses Spring MVC. Is it possible that controller return temporary view depending on condition ? For example
#RequestMapping(value="/")
public String home(){
// some code here
return "home/{RANDOM_HASH}"
}
and user is redirected to this link. There is some action and when it finishes, he will be redirected somewhere else and he will not be able to connect to this even if he write full path, including random hash.
English isn’t my first language, so please excuse any mistakes.
Yes, you can use #PathVariable to bind HTTP parameters to method arguments:
#RequestMapping(value="home/{hash}")
public String link(#PathVariable String hash) {
// 1) verify hash was not made up
// 2) do whatever needs done with hash
return "somewhereElse";
}
As to how to verify the hash was really created by your application and not typed in the URL bar: you can create some sort of "token manager". It would manage all hashes: issue new tokens and invalidate old ones once they're used. Simplified implementation could be something like this:
#NotThreadSafe
class TokenService {
private final Set<String> hashes = new HashSet<String>();
public String getHash() {
String hash = "???"; // TODO: random generator
hashes.put(hash);
return hash;
}
public void invalidateHash(String hash) {
hashes.remove(hash);
}
public boolean checkHash(String hash) {
return hashes.contains(hash);
}
}
Please note that real-life implementation should make access to hashes thread-safe.
I want to manage users projects through a JSON API and I'd like to use a relative path controller. Like this:
#RequestMapping(value="/users/{userId}/projects",
headers="Accept=application/json")
#Controller
public class UserProjectsController {
#Autowired
private UserRepository userRepository;
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody Object getAllUserProjects(#PathVariable String userId) {
User user = userRepository.findById(userId);
if (user == null) {
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
return user.getGivenProjects();
}
}
I'll add numerous methods and every time I'll have to check if the user exists. Instead of adding that piece of code:
User user = userRepository.findById(userId);
if (user == null) {
return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
}
... at starting of every method, I'd like to create a custom annotation which will return a 404 if the user doesn't exist.
I found this tutorial to do that. Is this really as complicated as described? Do you know any other solution? (I'd like to avoid writing 2 classes and more than 50 lines of code only to annotate 4 lines.)
Thank you.
I firstly assume that this check has nothing to do with Security, does it?
I think WebArgumentResolver won't fit your needs. Returning a 404 status might be complicated.
Maybe a custom interceptor can be a better solution. It will be called in every request to your controllers. There you can examine the handler object in order to see if it has a parameter called userId annotated with #PathVariable, for example. Then, you can do the check and use response object for returning a 404 if it's necessary.
Another option would be create a custom #Aspect, but we maybe are overcomplicating the problem. So first, try the previous solution.
Spring's AOP functionality is pretty great, and it makes it easy to add cool and useful annotations to controllers. For example, I wrote an #Authenticated annotation that either allows authenticated users through to the controller method or redirects to the login page. Fun stuff.
However, Spring's controllers can return all sorts of different types. They can return Strings, ModelAndView objects, or even void. There are methods in my code base that use all three types. However, I'd like to change my #Authenticated annotation to render and return a particular page, which I was hoping to do by returning a ModelAndView object. Is the only way to accomplish this by requiring all of my controller methods to return a ModelAndView?
Example of a controller I'd like to have:
#Controller
public class MyController() {
#Authenticated
#RequestMapping("/myscore")
public String myScorePage(ModelMap model) {
return "myScorePage";
}
#Authenticated
#RequestMapping("/anotherPage")
public ModelAndView something() {
return new ModelAndView("anotherPage",someModelStuff());
}
}
#Aspect
public class NotVeryUsefulAspect {
#Around("#annotation(Authenticate)")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
if( isAuthenticated() ) {
return pjp.proceed();
} else {
return /* Oh no what goes here, I want to render a FAILURE page without redirecting */
}
}
}
Ha, figured it out!
I decided to use the ProceedingJoinPoint passed to the aspect method to figure out the return type of the original method. Then I made a set of possible "failure" results for the aspect method based on what type of return is passed. For example, if the method originally returned a String, I return "failure_page", and if the method returned a ModelAndView, I return a new ModelAndView("failure_page").
Works quite well! Unfortunately, I may not have an opportunity to set a model object if it returns a string and doesn't take a ModelMap as a parameter, but I can deal with that for an error page just fine.
Yes it seams that you are right.
You need to change your methods so that all return an ModelAndView.
Or you need two Aspects, one for return type ModelAndView and one for String - and then all your controller methods must match
But Authorization is already build in in Spring Security and you do not need to implement it by your own.