Spring Boot Redirecting to another controller method from current controller method - spring-boot

Hi all am new to spring boot. Am stuck in the middle of my learning path. I have two controllers(#Controller) with some methods define in them. Am submitting form data to a method in index controller and wants to move to the method in home controller if form submission gets successful(on succesfull login). while loading http://localhost:9090/method of index controller it loads all the static content correctly, but when i redirectreturn "redirect:/dashboard/index" then it navigates to http://localhost:9090/dashBoard/index. And /dashboard/index method is as follow
#Controller
public class HomeController {
#GetMapping(value = "/dashBoard/index")
public String hello(Model model, #RequestParam(value = "name", required = false, defaultValue = "World") String name) {
model.addAttribute("name", name);
return "index";
}
}
this method return "index" which is .jsp page but redirecting to this method changes static content path like http://localhost:9090/dashBoard/assets/images/avatar/1.jpg
it seems like appending /dashBoard/ in path of static content. Am not understanding what to do please help. Am adding my project properties and structure please have a look
application.properties
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
spring.resources.static-locations=file:/var/www/static,classpath:static
spring.mvc.static-path-pattern=/resources/**
server.port=9090
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57InnoDBDialect
spring.jackson.serialization.fail-on-empty-beans=false
main class
[#SpringBootApplication
#EnableAutoConfiguration
public class SchoolpageApplication extends SpringBootServletInitializer {
public static void main(String\[\] args) {
SpringApplication.run(SchoolpageApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SchoolpageApplication.class);
}
}
Project Structure
Static content path

#Controller
#RequestMapping("/dashBoard")
public class HomeController {
#GetMapping("index")
public String hello(Model model, #RequestParam(value = "name", required = false, defaultValue = "World") String name) {
model.addAttribute("name", name);
return "index";
}
}
You could try this method, everytime you get throught an url with /dashboard you will get inside this controller, and you can control every option you want with the next income like /foo (in this case /index) and in the return it will get you into /dashboard/+your return value(in this case index).
I´m not an expert with spring boot, I´m pretty new with it too but I hope this can help you.

Related

Spring MVC: Refusing matched mapping

Consider a situation where we can have several mappings with the same regular expression, which should be validated programmatically (for instance against database).
(this is not a valid piece of code, I am trying just to explain what I am trying to achieve. Note the regular expressions in the url path)
// Animal controller
#GetMapping(path = "/{animal-category [a-z-]+}/{animal-name [a-z-]+}")
public void show(#PathVariable String animalCategory, #PathVariable String animalName) {
// if animalCategory is not found in database, continue with next controller
}
// Plants controller
#GetMapping(path = "/{plant-category [a-z-]+}/{plant-name [a-z-]+}")
public void show(#PathVariable String plantCategory, #PathVariable String plantName) {
// if plantCateogry is not found in database, continue with next controller - as there is no more, it should return 404
}
You can achieve this problem with a general controller method like this:
// General controller method
#GetMapping(path = "/{category [a-z-]+}/{name [a-z-]+}")
public void show(#PathVariable String category, #PathVariable String name) {
// look in database for the category
if(isAnimalCatagory) {
return showAnimal(category, name);
}
else if(isPlantCategory) }
return showPlant(category, name);
}
return "redirect:/404";
}
public void showAnimal(String animalCategory, String animalName) {
// for animal categories
}
public void showPlant(String plantCategory, String plantName) {
// for plant categories
}

How can I check if the user have correctly submitted the previous form into a Spring MVC application that contemplate some steps?

I am pretty new in Spring MVC and I have the following situation.
I am working on a Spring MVC application that implement a user registration process. The prcess is divided into 4 steps. In each step the user insert some information into a form that is submitted and that is handled by the related method into the controller class. Each of these controller method take the related command object that contains the information of the submitted form.
So I have something like this:
#Controller
public class RegistrazioneController {
// This is the first step and show a view that contain the first form:
#RequestMapping(value = "/registrationStep1")
public String registrationStep1(Model model) {
return "/registrazione/registration-step1";
}
#RequestMapping(value = "/registrationStep2", method = RequestMethod.POST)
public String registrationStep2(#ModelAttribute RegistrationStep1 registrationStep1, Model model) throws APIException {
.......................................................
.......................................................
.......................................................
return "/registrazione/registration-step2";
}
#RequestMapping(value = "/registrationStep3", method = RequestMethod.POST)
public String registrationStep3(#ModelAttribute RegistrationStep3 registrationStep3, Model model) throws APIException {
.......................................................
.......................................................
.......................................................
return "/registrazione/registration-step3";
}
// This method return the final view after the completation of the user registration:
#RequestMapping(value = "/registrationStep4", method = RequestMethod.POST)
public String registrationStep2(#ModelAttribute RegistrationStep4 registrationStep4, Model model) throws APIException {
.......................................................
PERFORM THE USER REGISTRATION
.......................................................
return "/registrazione/registration-step4";
}
}
So it works pretty fine. My problem is that the application have tho check that, when enter into a registration step, the previous steps are completed (the previous form was compiled and submitted).
So I think that I have to do something like this, for example: ** when enter into the registrationStep3() have to check if the command object of the previous registrationStep2() step method was correctly setted (it is valid), so it means that the user have completed the previous registration step.
The application have to prevent that the user try to acces the registration starting from a step without having complete the previous steps of the registration process.
What is the best way to implement this behavior?
I have worked in some Sap Hybris projects and this platform suggest to use the following process :
Step1Form, Step2Form and Step3Form, if you have first name and last name in your 1 step form you ll have the same in Step1Form class as attributes.
and for each class create a validator, in the next step controller u have to validate the previous step if it is not valid redirect the user to the previous step.
you already have RegistrationStep1, and RegistrationStep2 and RegistrationStep3
lets create a validator for RegistrationStep1 :
import org.apache.commons.validator.routines.EmailValidator;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
#Component(value = "registrationStep1Validator")
public class RegistrationStep1Validator implements Validator
{
#Override
public boolean supports(final Class<?> aClass)
{
return RegistrationStep1.class.equals(aClass);
}
#Override
public void validate(final Object object, final Errors errors)
{
final RegistrationStep1 step1= (RegistrationStep1) object;
final String name = step1.getName();
final String email = step1.getEmail();
if (email.isEmpty or email == null)
{
errors.reject("email", "Email must not be blank or null");
}
if (name.isEmpty or name== null)
{
errors.reject("name", "Name must not be blank");
}
if (!EmailValidator.getInstance().isValid(email))
{
errors.reject("email", "Email must be valid");
}
}
}
//later in your controller
#RequestMapping(value = "/registrationStep2", method = RequestMethod.POST)
public String registrationStep2(#ModelAttribute RegistrationStep1 registrationStep1,final BindingResult bindingResult, Model model) {
registrationStep1Validator.validate(registrationStep1,bindingResult);
if (bindingResult.hasErrors())
{
return "/registrazione/registration-step1";
}
return "/registrazione/registration-step2";
}

Relation b/w view name in the controller method and #RequestMapping(value="/...") in Spring MVC

I am new to Spring MVC & going through Craig Walls Spring4 in Action.
Consider the snippet,
#RequestMapping(value = "/spittles", method = RequestMethod.GET)
public String spittles(Model model, #RequestParam("max") long max,
#RequestParam("count") int count) {
model.addAttribute("spittleList",spittleRepository.findSpittles(max, count));
return "spittles"; // <-- return view name
}
The image shows the spittles.jsp resides in /WEB-INF/views/
WebConfig.java:
#Configuration
#EnableWebMvc // Enable Spring MVC
#ComponentScan(basePackages={"org.spittr"})
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
/* configure static content handling */
configurer.enable();
}
}
1) Why do I need to return the string "spittles" in the controller method?
2) Does it(return string) holds a relationship to the
#RequestMapping(value = "/spittles", method = RequestMethod.GET)
as the value(/spittles) is the same as the returned string in the controller method?
3) Why don't I see a .jsp extension when i enter the URL
http://localhost:8080/web/spittles?max=238900&count=5
and the o/p is resolved as:
To your questions:
The String "spittles" will be passed to the view resolver, which looks for a view /WEB-INF/views/splittles.jsp. If you'd return "hello_world", you would need a view /WEB-INF/views/hello_world.jsp.
No - that's the controller URL. You could define a completely different Controller URL like e.g. /my/super/vality/url if you'd like - that's just the path under which you accept the (GET) request.
See answers to 1.) and 2.) Although its good practice keep Spring-Controller-URLs and JSP view names alike, so it is obvious to the developer what's happening here.
You could for example have to controller methods for the same path and one answering to GET and the other answering to POST requests and both resulting in differne views:
#RequestMapping(value = "/spittles", method = RequestMethod.GET)
public String spittles(Model model, #RequestParam("max") long max,
#RequestParam("count") int count) {
// ...
return "splittles_get";
}
#RequestMapping(value = "/spittles", method = RequestMethod.POST)
public String spittles(Model model, #RequestParam("max") long max,
#RequestParam("count") int count) {
// ...
return "splittles_post";
}
You can even return a relative path like splittles/jspName meaning that you can organize your JSPs in folders - here /WEB-INF/views/splittles/something.jsp

spring-boot application displaying html code for view when executed in the browser

I am recently start to work with spring-boot in my spring projects, and right now I am facing this problem:
I have one spring-boot application with this main class:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and this controller:
#Controller
public class AcessoController {
#RequestMapping(value = "/signin")
public String signin(Model model) {
return "acesso/signin";
}
#RequestMapping(value = "/admin")
public String admin(Model model) {
return "private/admin";
}
#RequestMapping(value = "/index")
public String index(Model model) {
return "public/index";
}
}
when I run the application and try access the url mapping /signin, for example, the browser display the html code for this view, instead of the actual content.
What I am doing wrong here?
Are you trying to render a view using a template engine, or just return a static HTML file?
If you are trying to render a template, then you most likely do not have the right dependency in place to pull in a template engine. (Per your code, I believe this is what you are trying to do.) Even if you don't intend to use the template engine for templates, you will want one to render the HTML for you. Depending on your spring-boot setup, try starting with spring-boot-starter-web, or pull in Thymeleaf (spring-boot-starter-thymeleaf) or Freemarker (spring-boot-starter-freemarker) specifically.
If you want to simply return static content and do not want do custom configuration, you'll need to place the files in a certain location and do not need specific controller request mappings.
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-static-content

Strange Spring #SessionAttributes Behavior

I'm using #SessionAttributes on 2 controllers and am experiencing some very strange behavior. My first controller (ViewController) is simply a view controller that displays JSP pages. The other is a controller that handles Ajax requests (AjaxController). I have a session attribute that is simply an object that has a HashMap as a member. The object is a wrapper around the map. The map is populated from the database and put in the session, which displays fine via the ViewController. However, when I do a delete from the map via an ajax request (AjaxController) and refresh the page, ViewController SOMETIMES shows that the element is removed, yet other times the element is still there. Here's code snippets:
ViewController (the homepage simply displays the contents of the map contained by userSettings
#Controller
#SessionAttributes({"userSettings"})
public class ViewController {
#RequestMapping(value="/", method=RequestMethod.GET)
public String home(ModelMap model) {
UserSettings userSettings = (UserSettings) model.get("userSettings");
String userListenersJson = userSettings.toJson(); // for bootsrtapping the js on the front end
return "views/home";
}
}
AjaxController:
#Controller
#SessionAttributes({"userSettings"})
public class AjaxController {
#RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public #ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
#PathVariable long externalId) {
UserSettings userSettings = (UserSettings) model.get("userSettings");
userSettings.removeSetting(externalId);
return new AjaxResponse<String>(null, true);
}
}
Am I using #SessionAttributes wrong here? Why would this work sometimes and not others? I've also tried putting all of the view and ajax functionality in the same controller and experienced the same behavior.
Thanks for any help!
EDIT:
I've refactored my code a bit to use the UserPrincipal via springsecurity. My understanding is that this object is stored in the session. Regardless, I'm seeing exactly the same behavior.
Here's the UserPrincipal constructor that populates the user settings map. I've set breakpoints here to ensure that the correct listenerDBOs are set - they are, every time. This is the only time the listeners get set from the db into the UserSettings object in CustomUserPrincipal. All other adds/removes are done via the controllers (quick aside: adds never fail... only removes):
public CustomUserPrincipal(UserDBO userDBO) {
// set UserSettings obj
UserSettingsAdapter.addListeners(userDBO.getUserListenerDBOs(), userSettings);
}
The UserSettings object itself:
public class UserSettings implements Serializable {
private static final long serialVersionUID = -1882864351438544088L;
private static final Logger log = Logger.getLogger(UserSettings.class);
private Map<Long, Listener> userListeners = Collections.synchronizedMap(new HashMap<Long, Listener>(1));
// get the listeners as an arraylist
public List<Listener> userListeners() {
return new ArrayList<Listener>(userListeners.values());
}
public Map<Long, Listener> getUserListeners() {
return userListeners;
}
public Listener addListener(Listener listener) {
userListeners.put(listener.getId(), listener);
return listener;
}
// I'm logging here to try and debug the issue. I do see the success
// message each time this function is called
public Listener removeListener(Long id) {
Listener l = userListeners.remove(id);
if (l == null) {
log.info("failed to remove listener with id " + id);
} else {
log.info("successfully removed listener with id " + id);
}
log.info("Resulting map: " + userListeners.toString());
log.info("Map hashcode: " + userListeners.hashCode());
return l;
}
public Listener getListener(long id) {
return userListeners.get(id);
}
}
This is the helper function in the UserSettingsAdapter class that adds to the UserSettings object, called from CustomUserDetails constructor:
public static void addListeners(Set<UserListenerDBO> userListeners, UserSettings userSettings) {
for (UserListenerDBO userListenerDBO : userListeners) {
if (userListenerDBO.isActive()) {
addListener(userListenerDBO, userSettings);
}
}
}
I've also changed the controller code to user the CustomUserPrincipal object instead of #SessionAttributes:
In ViewController:
#RequestMapping(value="/", method=RequestMethod.GET)
public String home(ModelMap model) {
CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
UserSettings userSettings = userPrincipal.getUserSettings();
String userListenersJson = userSettings.toJson();
return "views/home";
}
In AjaxController:
#RequestMapping(value="/users/listeners/{externalId}", method=RequestMethod.DELETE)
public #ResponseBody
AjaxResponse<?> deleteListener(ModelMap model,
#PathVariable long externalId) {
CustomUserPrincipal userPrincipal = authenticationHelpers.getUserPrincipal();
UserSettings userSettings = userPrincipal.getUserSettings();
userSettings.removeListener(externalId);
return new AjaxResponse<String>(null, true);
}
I hope this helps shed some light on the issue!
I ran into a similar problem with #SessionAttributes. A controller had a #SessionAttributes annotation at the class level, and one of the methods handled POST requests, and included an instance of the session-managed object as an argument. This instance was saved to the database, but was re-used by subsequent requests, causing some data corruption. We had to add another method argument of type SessionStatus, and call SessionStatus.setComplete(). This caused the instance to be removed from the session, and prevented reuse and corruption. So try adding a SessionStatus instance to your controllers' handler methods, and invoke setComplete() where appropriate.
EDIT: I accidentally referenced the getter isComplete() in my initial answer; I meant to reference the setter setComplete().
#SessionAttributes is specific to a Controller and is not shared among several Controllers.
Instead, consider using manually session.setAttribute (class HttpSession).
You should have a look here : http://beholdtheapocalypse.blogspot.fr/2013/01/spring-mvc-framework-sessionattributes.html

Resources