Bad design letting controller return an Object? - spring

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.

Related

Handle form submission in SpringBoot

Here is my controller:
#Controller
#RequestMapping("/accounts/*")
public class AccountController {
#Autowired
private AccountService accountService;
#GetMapping
public ModelAndView home() {
final ModelAndView modelAndView = new ModelAndView();
final List<Account> accountsForCurrentUser = this.accountService.getAccountsForCurrentUser();
modelAndView.addObject("accounts", accountsForCurrentUser);
modelAndView.setViewName("pages/accounts/index");
return modelAndView;
}
#GetMapping("create")
public ModelAndView create() {
final ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("account", new Account());
modelAndView.setViewName("pages/accounts/create");
return modelAndView;
}
#PostMapping("create")
public ModelAndView createSubmit(#Valid #ModelAttribute(name = "account") Account account, BindingResult bindingResult, ModelAndView modelAndView) {
if (bindingResult.hasErrors()) {
return modelAndView;
}
return new ModelAndView("redirect:/accounts");
}
}
What I'd like to do is redirecting user to /accounts/ when the form is validated but taking him back to /accounts/create/ with errors shown if errors has been reported.
But, on error, I have:
Error resolving template "accounts/create", template might not exist or might not be accessible by any of the configured Template Resolvers
You also need set model and view name in post/create method.
By the way, handling methods with ModelAndView is valid but I think it would be better to use the String approach. It's much better to read and a standart way. So your controller will look like:
#Controller
#RequestMapping("/accounts")
public class AccountController {
#Autowired
private AccountService accountService;
#GetMapping("")
public String home(Model Model) {
List<Account> accountsForCurrentUser = this.accountService.getAccountsForCurrentUser();
model.addAttribute("accounts", accountsForCurrentUser);
return "pages/accounts/index";
}
#GetMapping("/new")
public String newAccount(Model model) {
model.addAttribute("account", new Account());
return "pages/accounts/create";
}
#PostMapping("/new")
public String createAccount(#Valid #ModelAttribute Account account, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "pages/accounts/create";
}
"redirect:/accounts";
}
}

Spring ModelAndView Attribute is null

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

Request method 'GET' not supported

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";
}

HTTP redirect: 301 (permanent) vs. 302 (temporary) in Spring

I want to make a 301 redirect in Spring, So here the piece of code I use
#RequestMapping(value = { "/devices" } , method = RequestMethod.GET)
private String initGetForm(#ModelAttribute("searchForm") final SearchForm searchForm,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response,
Model model, Locale locale) throws Exception {
String newUrl = "/devices/en";
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", newUrl);
response.setHeader("Connection", "close");
return "redirect:" + newUrl;
}
But checking the IE Developer Tools I got this Status 302 Moved Temporarily !
Spring is resetting your response headers when it handles the redirection since you are returning a logical view name with a special redirect prefix.If you want to manually set the headers handle the response yourself without using Spring view resolution. Change your code as follows
#RequestMapping(value = { "/devices" } , method = RequestMethod.GET)
private void initGetForm(#ModelAttribute("searchForm") final SearchForm searchForm,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response,
Model model, Locale locale) throws Exception {
String newUrl = request.getContextPath() + "/devices/en";
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", newUrl);
response.setHeader("Connection", "close");
}
You can use RedirectView with TEMPORARY_REDIRECT status.
#RequestMapping(value = { "/devices" } , method = RequestMethod.GET)
private ModelAndView initGetForm(#ModelAttribute("searchForm") final SearchForm searchForm,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response,
Model model, Locale locale) throws Exception {
....
RedirectView redirectView = new RedirectView(url);
redirectView.setStatusCode(HttpStatus.TEMPORARY_REDIRECT);
return new ModelAndView(redirectView);
}

Trying to pass objects to controller(Spring MVC)

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.

Resources