I have one simple controller and one interceptor.
Within interceptor in postHandle-method I am checking user.
Problem: My user-model is sometimes null, between controller-handles.
postHandle invoked by home-handle==> User-Model is not null
postHandle invoked by check_user-handle ==> User-model is null
postHandle invoked by redirectToErrorPage-handle ==> User-model is
not null anymore and contains everything, what i've expected by
check_user-PostHandle Invocation.
Here is my controller
#Controller
#SessionAttributes("user")
public class HomeController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView home(Model model, HttpServletRequest request, HttpSession session) {
User user = new User();
return new ModelAndView("login", "user", user);
////now User-Model was saved in the session
}
//now i'am redirectring user in the "check_user"-handle
#RequestMapping(value = "/check_user", method = RequestMethod.POST)
public ModelAndView checkUser(#Valid #ModelAttribute("user") User user, BindingResult bindingResult, Model model,
HttpServletRequest request, RedirectAttributes redirectAttr) {
RedirectView redirectView = null;
ModelAndView mav=null;
try {
if(!bindingResult.hasErrors()){
redirectView = new RedirectView("home");
redirectView.setStatusCode(HttpStatus.FOUND);
redirectAttr.addFlashAttribute("httpStatus", HttpStatus.FOUND);
mav = new ModelAndView(redirectView);
return mav; //at next i entry post-handle from interceptor,
//which says to me, that user-model is null.
}
}
//My interceptor redirects me to this handle.
//After this handle within interceptor method "postHandle", i see, that
//user-object exists
#RequestMapping(value = "/error", method = RequestMethod.GET)
public String redirectToErrorPage(HttpServletRequest request){
return "error";
}
}
And my Interceptor:
public class UserInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
User user = (User) modelAndView.getModel().get("user");
if(user == null || !user.isAdmin()){
response.sendRedirect(request.getContextPath()+"/failed");
}
}
}
While I retrive, which keys my model has, when postHandle was invoked by "check_user", I have only one key "totalTime". Whats going on with my model?
Try modifying your postHandle() as below:
...
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user = (User) request.getSession().getAttribute("user");
...
Related
Trying to redirect from controller method to another controller, facing below error
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
I have submitForm method in controller 1, once i call submit method it should call controller 2
Controller 1
#RequestMapping(method = RequestMethod.POST)
public ModelAndView submitForm(#ModelAttribute("loginForm") Login login, BindingResult errors, SessionStatus status, HttpServletRequest request, HttpServletResponse response) throws IOException {
return new ModelAndView("path2.sp");
}
Controller 2
#Controller
#RequestMapping("path2.sp")
public class DestinationController {
System.out.println("");
}
This is not the way to do redirection.
First, fix your #RequestMapping in Controller 2, like this:
#Controller
#RequestMapping("/path2")
public class DestinationController {
System.out.println("");
}
Now, in controller 1 just do this:
#RequestMapping(method = RequestMethod.POST)
public String submitForm(#ModelAttribute("loginForm") Login login, BindingResult errors, SessionStatus status, HttpServletRequest request, HttpServletResponse response) throws IOException {
return "redirect:/path2";
}
In my current spring-boot project, I have this ExceptionHandler:
#ControllerAdvice
public class GlobalDefaultExceptionHandler {
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception error) throws Exception {
if (AnnotationUtils.findAnnotation(error.getClass(), ResponseStatus.class) != null)
throw error;
ModelAndView mav = new ModelAndView();
mav.setAttribute("error", error);
return mav;
}
}
What I want to do it's make this handler redirect to different error pages, depending of origin the error.
I have two "types" of pages in my project: a public one, acessed both by an anonymous user or an authenticated user, and the admin pages (private), acessed only by authenticated users.
This two types of page have different styles. I want, when an error occurs when the user is in the public pages, an error page with the public style be shown. If the errors occurs when the user is in the private pages, another error page, with the style of the private pages be shown.
You can construct a selection of what exception classes it needs to throw in your controller , Assuming like this:
#RequestMapping(value = "/check")
public ModelAndView processUser( ) throws Exception {
ModelAndView modelAndView = new ModelAndView();
if (something... ) {
throw new GlobalDefaultExceptionHandler( ); // throws GlobalDefaultExceptionHandler
}
if (something else... ) {
throw new AnotherExceptionHandler( );// throws 'anotherExceptionHandler'
}
// If there isn't exception thrown....do something
}
And assuming this is the AnotherExceptionHandler class:
#ControllerAdvice
public class AnotherExceptionHandler{
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception error) throws Exception {
if (AnnotationUtils.findAnnotation(error.getClass(), ResponseStatus.class) != null)
throw error;
// Go to another view
ModelAndView mav = new ModelAndView();
mav.setAttribute("anotherError", error);
return mav;
}
}
But if you are forced to use only an handler , you can just use selection directly just :
#ControllerAdvice
public class GlobalDefaultExceptionHandler {
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception error) throws Exception {
ModelAndView mav =null;
if ( // something...){
mav = new ModelAndView()
mav.setAttribute("error", ... );
return mav;
}
else if (// another something...){
mav = new ModelAndView()
mav.setAttribute("anotherError", ...);
return mav;
}
return mav;
}
There are several options. Some of them are:
You can check if user has been authenticated by using request.getUserPrincipal(). The return value will be null if user is not authentcicated. Depending on the result, you can return a different view.
Have all your Contollers service public pages extend from one PublicBaseController and controllers service private pages extend PrivateBaseController. Add method annotated with #ExceptionHandler to base controllers, which return appropriate views.
I am trying to test my controller. Spring populates my Profile object but it is empty. I can set the email before the call bu it still is null. How to jag pass a Profile in a proper way?
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Autowired
private RequestMappingHandlerAdapter handlerAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
request.setContentType("application/json");
this.response = new MockHttpServletResponse();
}
#Test
public void testPost() {
request.setMethod("POST");
request.setRequestURI("/user/"); // replace test with any value
final ModelAndView mav;
Object handler;
try {
Profile p = ProfileUtil.getProfile();
p.setEmail("test#mail.com");
request.setAttribute("profile", p);
System.out.println("before calling the email is " + p.getEmail());
handler = handlerMapping.getHandler(request).getHandler();
mav = handlerAdapter.handle(request, response, handler);
Assert.assertEquals(200, response.getStatus());
// Assert other conditions.
} catch (Exception e) {
}
}
This is the controller
#RequestMapping(value = "/", method = RequestMethod.POST)
public View postUser(ModelMap data, #Valid Profile profile, BindingResult bindingResult) {
System.out.println("The email is " + profile.getEmail());
}
Try using following signature for the controller function postUser.
public View postUser(ModelMap data, #ModelAttribute("profile") #Valid Profile profile, BindingResult bindingResult)
Hope this helps you. Cheers.
Hi is this bad design? I would like return Profile if everything goes according to plan. If not I would like to return my custom error message.
Is this ok?
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT)
public #ResponseBody
Object saveUser(#PathVariable Long userId, ModelMap data, #Valid Profile profile, BindingResult bindingResult, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return ValidationUtil.createValidationErrors(bindingResult);
}
profileService.save(profile);
return profile;
}
You should use #ExceptionHandler (Exception.class) and #ResponseStatus Annotation
Example
#ExceptionHandler (Exception.class)
#RequestMapping(value = "/{userId}", method = RequestMethod.PUT)
public #ResponseBody
Profile saveUser(#PathVariable Long userId, ModelMap data, #Valid Profile profile, BindingResult bindingResult, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
throw new Exception("message");
if(ValidationUtil.createValidationErrors(bindingResult)) {
throw new Exception("message");
}
}
profileService.save(profile);
return profile;
}
For more details refer
http://jnb.ociweb.com/jnb/jnbNov2010.html
http://www.stormpath.com/blog/spring-mvc-rest-exception-handling-best-practices-part-1
http://blog.cuttleworks.com/2011/12/spring-restful-controllers-and-error-handling/
If you need access to the profile object, I'd suggest you add it to the session or the request context. Then you can return a string from your saveUser method (or a ModelAndView object, if you prefer), and still have the object available to later requests.
I am new to spring. I am creating a simple login page. But the processFormSubmission() is not being called. But the showForm() is working.
public class LoginController extends SimpleFormController
{
private LoginService loginService;
private String loginView;
public LoginService getLoginService() {
return loginService;
}
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
public String getLoginView() {
return loginView;
}
public void setLoginView(String loginView) {
this.loginView = loginView;
}
public LoginController() {
setBindOnNewForm(true);
}
#Override
protected ModelAndView processFormSubmission(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception
{
TraceUser tr = (TraceUser) command;
System.out.println(tr);
//loginService.
return super.processFormSubmission(request, response, command, errors);
}
#Override
protected ModelAndView showForm(HttpServletRequest request,
HttpServletResponse response, BindException errors)
throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("traceUser", new TraceUser());
mav.setViewName(getLoginView());
return mav;
}
}
And please help me out with how should the ModelAndView object should be processed further.
First of all, the use of the Controller API has been left aside in favor of the new annotation-based controllers (see the #RequestMapping annotation) and classes like SimpleFormController have been deprecated since quite a while now.
However, to answer your question, I assume your form does not declare method="post" and by default, the SFC will consider only POST requests as form submissions (see the isFormSubmission() method in AbstractFormController). Is this the case ?