How to add error on Spring MVC simpleformcontroller? - spring

I have this problem in my Spring MVC 2.5 apps and I am not sure what should I do.
Here is my code:
public class AddStationController extends SimpleFormController {
private SimpleStationManager stationManager;
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
StationDetails detail = (StationDetails) command;
//add to DB
int return = stationManager.addStation(detail);
//return value: 1 = successful,
// if not = unsuccessful
if(return != 1){
//how can I add error so that when I display my formview ,
//I could notify the user that saving to the db is not successful?
showform();
}
return new ModelAndView("redirect:" + getSuccessView());
}
}
How is it possible to add some message when I display my formview again so that I could tell the user that adding the station was not successful?
And how to handle that in my jsp?

I at first thought you might want to use Validators but instead I think you can do the following:
public class AddStationController extends SimpleFormController {
private SimpleStationManager stationManager;
protected ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws Exception {
StationDetails detail = (StationDetails) command;
//add to DB
int return = stationManager.addStation(detail);
//return value: 1 = successful,
// if not = unsuccessful
if(return != 1){
//Account for failure in adding station
errors.reject("exception.station.submitFailure", "Adding the station was not successful");
showform(request, response, errors);
}
return new ModelAndView("redirect:" + getSuccessView());
}
}
Then in your JSP you can do the following:
<form:errors path="*">
Then any errors you bind will show up there.

There are a couple ways to do that. I prefer not to use the showForm() method b/c I want more control. So I do one of the following, I'm sure there will be several alternative answers given for your question.
If you don't want to fail b/c of a specific field you can just send back an error on the model like this:
ModelAndView mav = new ModelAndView(this.getFormView());
mav.addObject(this.getCommandName(), command);
mav.addObject("errorMessage", "The thing you tried to do failed");
return mav;
Then in your jsp you would do this:
<c:if test="${not empty errorMessage}">
${errorMessage}
</c:if>
If you have a specific field that has caused the error you can attach an error to the specific field like this (this rejects the length of a field called "alternateid":
errors.rejectValue("alternateId", "longerThan",
new Object[] { Integer.valueOf(2) }, "Please enter at least two characters.");
ModelAndView mav = new ModelAndView(this.getFormView());
mav.addAllObjects(errors.getModel());
mav.addObject(this.getCommandName(), command);
return mav;
Then in your jsp you would use the form tag library and do this:
<form:errors path="alternateId"/>
That's assuming you're using the spring form tag library.

Related

Spring controller should not do anything (void, response body)

I have an issue similar to this one, the solution does not work as I wished however:
Spring MVC how to create controller without return (String) view?
I have a form which should pass the file:
example
And the controller for it:
#PostMapping("/uploadFile")
public #ResponseBody void uploadFile(Model model, #RequestParam("file") MultipartFile multipartFile) throws InterruptedException {
//, RedirectAttributes redirectAttributes) throws InterruptedException {
Reservation reservation=new Reservation( );
fileService.uploadFile( multipartFile );
File file = new File("\\car-rental\\src\\main\\resources\\static\\attachments", multipartFile.getOriginalFilename());
log.info( "name and path " + file.getName() + file.getPath() );
Picname picname=new Picname();
picname.setPicnameAsString(file.getName() );
log.info( "picname file " + picname.getPicnameAsString() );
TimeUnit.SECONDS.sleep(2);
}
}
I want the controller ONLY to perform the logic without returning anything: it returns however an empty page:
empty page returned
How can I make it not returning anything, just staying on the site with the form? The only idea was to set an delay with the .sleep(), but it would be a workaround and I would like to solve it with a cleaner solution
It is the nature of controllers to return a response since you are developing an MVC application which will receive POST requests to the endpoint you specified.
What you can do is declare the controller to be a #RestController which returns a ResponseEntity indicating that everything went OK or any other appropriate response in case some failure happens.
#RestController
public class ControllerClassName{
#PostMapping("/uploadFile")
public ResponseEntity<?> uploadFile(Model model, #RequestParam("file") MultipartFile multipartFile) throws InterruptedException {
try{
// logic
return ResponseEntity.ok().build();
}catch(Exception e){
return ResponseEntity.badRequest().build();
}
}
}
To address your issue you may need to change the return type of your function.
Using a ResponseEntity return type may be more appropriate than using a ResponseBody return type.

How to keep request parameters after redirect?

I'm trying to resolve a bug when I send a form with an empty input.
This is my methode:
#RequestMapping(value = "/modifier.html", method = RequestMethod.POST)
public String modifier(ModelMap map, #ModelAttribute("FormObject") FormObject formObject, BindingResult result, HttpServletRequest req) {
formObject.setModif(true);
String idParam = req.getParameter("idTypeOuverture");
if (result.hasErrors()) {
return "redirect:/gestion.html?section=Configuration&panel=4&ouvrir=modifier";
} else {
//Instructions
}
When there are errors (empty input) the controller redirects to this link to tell user to correct errors. The problem is when I check parameters here they look correct (id, name ...), but the id becomes null in the following method:
#Override
public ModelAndView dispatcher(HttpServletRequest request, HttpServletResponse response) throws RorException {
Map<String, Object> myModel = (Map<String, Object>) request.getAttribute(EnumParam.R_MY_MODEL.getKey());
Enumeration<?> keys = request.getParameterNames();
while (keys.hasMoreElements()) {
String paramName = (String) keys.nextElement();
String value = request.getParameter(paramName);
myModel.put(paramName, value);
}
GlobalSession globalSession = (GlobalSession) getApplicationContext().getBean(Utilities.GLOBALSESSION_BEAN_REF);
myModel.put("module", globalSession.getModule().getKeyMessage());
String section = request.getParameter("section");
// This instruction returns null
String idForm = request.getParameter("id");
id = Integer.parseInt(idForm);
// This instruction returns NumberFormatException
ObjectForm of = getForm(id);
// ...
}
Well, I don't know why parameter id changed after redericting? do you have any idea? I tried to redifine parameters in the first method but still got the same NFE.
Thank you in advance.
Thank you
Although the previous answer is accepted, I am adding this answer just for your information.
You can also use RedirectAttributes with and without FlashAttributes also Before issuing redirect, post method should take RedirectAttributes as argument These attributes will be passed as request parameters Look at my code example and see if its helpful.
Way 1 :
#RequestMapping(value={"/requestInfo.html"}, method=RequestMethod.POST)
public String requestInfoPost1(
#ModelAttribute("requestInfoData") RequestInfoData requestInfoData,
BindingResult result,
RedirectAttributes redirectAttributes,
SessionStatus status
) {
// some logic
redirectAttributes.addAttribute("name", requestInfoData.getName());
redirectAttributes.addAttribute("age", requestInfoData.getAge());
// some logic
return "redirect:requestInfoSuccessRedirect";
}
#RequestMapping("requestInfoSuccessRedirect")
public String requestInfoSuccessRedirect()
{
return "requestInfoSuccess";
}
Way 2:
Whatever data is added in flash attribute will be added in session It will be in session only till redirect is successful On redirect, data is retrieved from session and added to Model for new Request. Only after redirect is successful, data is removed
#RequestMapping(value={"/requestInfo.htm"}, method=RequestMethod.POST)
public String requestInfoPost(
#ModelAttribute("requestInfoData") RequestInfoData requestInfoData,
BindingResult result,
RedirectAttributes redirectAttributes,
SessionStatus status
) {
// some logic
redirectAttributes.addFlashAttribute("requestInfoData",
requestInfoData);
// some logic
return "redirect:requestInfoSuccessRedirect";
}
#RequestMapping("requestInfoSuccessRedirect")
public String requestInfoSuccessRedirect()
{
return "requestInfoSuccess";
}
The request parameter is only for one request.
You make a redirect, it means that you make another new "request".
You should add it to the redirect:
return "redirect:/gestion.html?section=Configuration&panel=4&ouvrir=modifier&idTypeOuverture="+idParam;

Spring Boot - redirect to a different controller method

I am creating a very basic application with SpringBoot and Thymeleaf. In the controller I have 2 methods as follows:
Method1 - This method displays all the data from the database:
#RequestMapping("/showData")
public String showData(Model model)
{
model.addAttribute("Data", dataRepo.findAll());
return "show_data";
}
Method2 - This method adds data to the database:
#RequestMapping(value = "/addData", method = RequestMethod.POST)
public String addData(#Valid Data data, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "add_data";
}
model.addAttribute("data", data);
investmentTypeRepo.save(data);
return "add_data.html";
}
HTML files are present corresponding to these methods i.e. show_data.html and add_data.html.
Once the addData method completes, I want to display all the data from the database. However, the above redirects the code to the static add_data.html page and the newly added data is not displayed. I need to somehow invoke the showData method on the controller so I need to redirect the user to the /showData URL. Is this possible? If so, how can this be done?
Try this:
#RequestMapping(value = "/addData", method = RequestMethod.POST)
public String addData(#Valid Data data, BindingResult bindingResult, Model model) {
//your code
return "redirect:/showData";
}
sparrow's solution did not work for me. It just rendered the text "redirect:/"
I was able to get it working by adding HttpServletResponse httpResponse to the controller method header.
Then in the code, adding httpResponse.sendRedirect("/"); into the method.
Example:
#RequestMapping("/test")
public String test(#RequestParam("testValue") String testValue, HttpServletResponse httpResponse) throws Exception {
if(testValue == null) {
httpResponse.sendRedirect("/");
return null;
}
return "<h1>success: " + testValue + "</h1>";
}
Below Solution worked for me.
getAllCategory() method displays the data and createCategory() method add data to the database. Using return "redirect:categories";, will redirect to the getAllCategory() method.
#GetMapping("/categories")
public String getAllCategory(Model model) {
model.addAttribute("categories",categoryRepo.findAll());
return "index";
}
#PostMapping("/categories")
public String createCategory(#Valid Category category) {
categoryRepo.save(category);
return "redirect:categories";
}
OR using ajax jQuery also it is possible.
You should return a http status code 3xx from your addData request and put the redirct url in the response.

Spring MVC handle Exceptions and how to show in same view

If I handle exceptions with #ControllerAdvice and #ExceptionHandler How can I show error message to user in the same View. For ex. suppose user in "customer/new" view. and invoke "save" action, then it will go to controller and I call methods service layer. but if internal exception occurred in service layer, I want to show error message on same "customer/new" View.
I have written a separate class to handle exceptions as follow.
#ControllerAdvice
public class DefaultControllerHandler {
#ExceptionHandler({MyProjectException.class, DataAccessException.class})
public ResponseEntity<String> handleInternalErrorException(Exception e) {
logger.error(e.getMessage(), e);
return new ResponseEntity<String>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Appreciate your ideas.
Thanks!
You can use flash redirect attributes.
#RequestMapping(value = "/administrator/users", method = RequestMethod.POST)
public String adminAddUser(#ModelAttribute("user") #Valid User user, BindingResult bindingResult, Model model, RedirectAttributes redirectAttrs) {
String redirectUrl = "/administrator/users";
try {
userService.save(user);
} catch (YourServiceException e) {
redirectAttrs.addFlashAttribute("errorMessage", "error occured: " + e.getMessage());
redirectAttrs.addFlashAttribute("userObject", user);
redirectUrl = "/administrator/users?form"; // If error - return to same view
}
return "redirect:" + redirectUrl;
}
#RequestMapping(value = "/administrator/users", params = "form", method = RequestMethod.GET, produces = "text/html")
public String adminUsersList(#ModelAttribute("errorMessage") final String errorMessage, #ModelAttribute("userObject") final User user Model model) {
if(user == null) {
user = new User();
}
model.addAttribute("user", user);
if(errorMessage != null) {
model.addAttribure("errorMessage", errorMessage);
}
return "administrator/users/create";
}
In that case you must have section on your users.jsp page to show errorMessaage. Something like this:
<c:if test="${not empty errorMessage}">${errorMessage}</c:if>
If you can fetch url path and redirectAttributes from controller method - you can do this through #ControllerAdvice
The only solution I can think about is make your call to the service layer AJAX and then redirect only if there are no errors, if not display the error message.
It might look something like this in your Javascript file
$("#someButton").click(function(){
//make your ajax call
if (noError){
window.location.href = contextPath+"/somePath";
}
else{
//display your error message
}
});

Spring security perform validations for custom login form

I need to do some validations on the login form before calling the authenticationManager for authentication. Have been able to achieve it with help from one existing post - How to make extra validation in Spring Security login form?
Could someone please suggest me whether I am following the correct approach or missing out something? Particularly, I was not very clear as to how to show the error messages.
In the filter I use validator to perform validations on the login field and in case there are errors, I throw an Exception (which extends AuthenticationException) and encapsulate the Errors object. A getErrors() method is provided to the exception class to retrieve the errors.
Since in case of any authentication exception, the failure handler stores the exception in the session, so in my controller, I check for the exception stored in the session and if the exception is there, fill the binding result with the errors object retrieved from the my custom exception (after checking runtime instance of AuthenticationException)
The following are my code snaps -
LoginFilter class
public class UsernamePasswordLoginAuthenticationFilter extends
UsernamePasswordAuthenticationFilter {
#Autowired
private Validator loginValidator;
/* (non-Javadoc)
* #see org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter#attemptAuthentication(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
Login login = new Login();
login.setUserId(request.getParameter("userId"));
login.setPassword(request.getParameter("password"));
Errors errors = new BeanPropertyBindingResult(login, "login");
loginValidator.validate(login, errors);
if(errors.hasErrors()) {
throw new LoginAuthenticationValidationException("Authentication Validation Failure", errors);
}
return super.attemptAuthentication(request, response);
}
}
Controller
#Controller
public class LoginController {
#RequestMapping(value="/login", method = RequestMethod.GET)
public String loginPage(#ModelAttribute("login") Login login, BindingResult result, HttpServletRequest request) {
AuthenticationException excp = (AuthenticationException)
request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
if(excp != null) {
if (excp instanceof LoginAuthenticationValidationException) {
LoginAuthenticationValidationException loginExcp = (LoginAuthenticationValidationException) excp;
result.addAllErrors(loginExcp.getErrors());
}
}
return "login";
}
#ModelAttribute
public void initializeForm(ModelMap map) {
map.put("login", new Login());
}
This part in the controller to check for the instance of the Exception and then taking out the Errors object, does not look a clean approach. I am not sure whether this is the only way to handle it or someone has approached it in any other way? Please provide your suggestions.
Thanks!
#RequestMapping(value = "/login", method = RequestMethod.GET)
public ModelAndView signInPage(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout) {
ModelAndView mav = new ModelAndView();
//Initially when you hit on login url then error and logout both null
if (error != null) {
mav.addObject("error", "Invalid username and password!");
}
if (logout != null) {
mav.addObject("msg", "You've been logged out successfully.");
}
mav.setViewName("login/login.jsp");
}
Now if in case login become unsuccessfull then it will again hit this url with error append in its url as in spring security file you set the failure url.
Spring security file: -authentication-failure-url="/login?error=1"
Then your URl become url/login?error=1
Then automatically signInPage method will call and with some error value.Now error is not null and you can set any string corresponding to url and we can show on jsp using these following tags:-
<c:if test="${not empty error}">
<div class="error">${error}</div>
</c:if>

Resources