I am working on a Spring MVC application and have an issue with the validation. I need to create an user and it is a 2 step process(meaning 2 jsps with the same controller). The jsps have 2 different commandNames. So when I created the validator for the first model and initialize it, the first page loads fine. For testing, I am not using an if condition, but forwarding to next page. I get an error as the controller tries to load the validator with the current commandName object and it fails.
When I enter the url http://ip:port/data, the page loads as there is no validation, it is just initial load
When I try to submit the page without entering the firstName and submit, the method #RequestMapping(value = "/user") is called and the the next page 2 is supposed to be loaded. But the page 2 fails.
binder.getTarget() prints UserData the first time which is right
binder.getTarget() prints userinfo the 2nd time, when the next page is loading and it fails with the error
StackTrace:
HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalStateException: Invalid target for Validator [.validator.UserDataValidator#4366febc]: .model.UserInfo#4e3a061b
type Exception report
message Request processing failed; nested exception is java.lang.IllegalStateException: Invalid target for Validator [.validator.UserDataValidator#4366febc]: .model.UserInfo#4e3a061b
description The server encountered an internal error that prevented it from fulfilling this request.
exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Invalid target for Validator [.validator.UserDataValidator#4366febc]: .model.UserInfo#4e3a061b
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
root cause
java.lang.IllegalStateException: Invalid target for Validator [.validator.UserDataValidator#4366febc]: .model.UserInfo#4e3a061b
org.springframework.validation.DataBinder.assertValidators(DataBinder.java:516)
org.springframework.validation.DataBinder.setValidator(DataBinder.java:507)
.controller.AccountDataController.initBinder(AccountDataController.java:65)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:606)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
org.springframework.web.method.annotation.InitBinderDataBinderFactory.initBinder(InitBinderDataBinderFactory.java:62)
org.springframework.web.bind.support.DefaultDataBinderFactory.createBinder(DefaultDataBinderFactory.java:53)
org.springframework.web.method.annotation.ModelFactory.updateBindingResult(ModelFactory.java:251)
Any suggestions on how to handle this would be helpful. Is there a way to make this work - having 2 different commandNames and trying to validate using one controller or do I need to update the commandName to one object and that object has all the classes inside it.
e.g.
class Parent{
UserData UserData;
UserInfo userInfo;
getter & setter
}
My Controller:
#Controller
#SessionAttributes("userData")
public class AController {
#Autowired
#Qualifier("userDataValidator")
private Validator validator;
#InitBinder
private void initBinder(WebDataBinder binder) {
System.out.println("getTarget: "+binder.getTarget()); ------------
binder.setValidator(validator);
}
#RequestMapping(value = "/data", method = RequestMethod.GET)
public String initForm(Model model){
UserData userData = new UserData();
model.addAttribute("userData", userData);
return "page1";
}//
#RequestMapping(value = "/user", method=RequestMethod.POST )
public String details(Model model, #Validated UserData userData, BindingResult result) {
*****
model.addAttribute("userinfo", userinfo);
return "page2";
}//
**EDIT**
#RequestMapping(value = "/create", method=RequestMethod.POST )
*****************
public String create(Model model, Userinfo userinfo, UserData userData) {
*********************user creation
}
**EDIT**
}
My JSPs
page1.jsp
<form:form method="POST" action="user" commandName="userData">
<form:label path="firstName"><b>Name</b></form:label> <br />
<form:input class="formLabel" path="firstName" />
****
</form>
page2.jsp
<form:form method="POST" action="create" commandName="userinfo">
***********fields
</form>
My validator
public class UserDataValidator implements Validator{
public boolean supports(Class<?> paramClass) {
return UserData.class.equals(paramClass);
}
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "valid.firstName");
}
}
My Model
UserData.java
public class UserData {
String firstName;
getter & setter for firstName
}
Let me know if any more details are needed. Thanks.
Related
I am trying to create pdf file using POJO and download it using spring mvc.i have got the Document object but can not download the file. it gives error "could not find convertor".
#RequestMapping(value = "/downloadPDF", method = RequestMethod.GET)
public Document downloadPDF() throws FileNotFoundException, DocumentException {
// create some sample data
List<EmployeeInfo> employeeList = new ArrayList<EmployeeInfo>();
employeeList.add(new EmployeeInfo("1", "Anish", "surat"));
return downloadPDFService.createPDF(employeeList);
how can i download this file.
please provide answer
public Document createPDF( List<EmployeeInfo> employeeList) throws FileNotFoundException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("AddTableExample.pdf"));
document.open();
PdfPTable table = new PdfPTable(3);
table.setWidthPercentage(100); //Width 100%
table.setSpacingBefore(10f); //Space before table
table.setSpacingAfter(10f); //Space after table
PdfPCell cell = new PdfPCell();
cell.setBackgroundColor(BaseColor.BLUE);
cell.setPadding(5);
Font font = FontFactory.getFont(FontFactory.HELVETICA);
font.setColor(BaseColor.WHITE);
cell.setPhrase(new Phrase("Id", font));
table.addCell(cell);
cell.setPhrase(new Phrase("Name", font));
table.addCell(cell);
cell.setPhrase(new Phrase("Address", font));
table.addCell(cell);
for (EmployeeInfo aBook : employeeList) {
table.addCell(aBook.getEmpId());
table.addCell(aBook.getEmpName());
table.addCell(aBook.getEmpAddress());
}
document.add(table);
document.close();
writer.close();
return document;
}
this is a view creator.next is stack trace
HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException:
No converter found for return value of type: class com.itextpdf.text.Document
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: No converter found for return value of type: class com.itextpdf.text.Document
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
java.lang.IllegalArgumentException: No converter found for return value of type: class com.itextpdf.text.Document
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:178)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:153)
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:165)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:80)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Though you don't want jsp, you still can use ModelAndView.
Change your DownloadPDFService as below.
#Component
public class DownloadPDFService extends AbstractPdfView {
#Override
protected void buildPdfDocument(Map<String, Object> model, Document doc,
PdfWriter writer, HttpServletRequest req, HttpServletResponse resp)
throws Exception {
// Retrieve your model as below
List<EmployeeInfo> employeeList = (List<EmployeeInfo>) model.get("employeeList");
// continue your document build logic
}
}
Change your controller as below
#RequestMapping(value = "/downloadPDF", method = RequestMethod.GET)
public Document downloadPDF() throws FileNotFoundException, DocumentException {
// create some sample data
List<EmployeeInfo> employeeList = new ArrayList<EmployeeInfo>();
employeeList.add(new EmployeeInfo("1", "Anish", "surat"));
return new ModelAndView("pdfView", "employeeList", employeeList);
}
Add below views configuration in views.properties
pdfView.(class)= YourpackageName.DownloadPDFService
Configure a new ResourceBundleViewResolver for above "views" properties.
I am having trouble while trying to make MockMvc to include the exception message in the response body. I have a controller as follows:
#RequestMapping("/user/new")
public AbstractResponse create(#Valid NewUserParameters params, BindingResult bindingResult) {
if (bindingResult.hasErrors()) throw BadRequestException.of(bindingResult);
// ...
}
where BadRequestException looks sth like this:
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "bad request")
public class BadRequestException extends IllegalArgumentException {
public BadRequestException(String cause) { super(cause); }
public static BadRequestException of(BindingResult bindingResult) { /* ... */ }
}
And I run the following test against /user/new controller:
#Test
public void testUserNew() throws Exception {
getMockMvc().perform(post("/user/new")
.param("username", username)
.param("password", password))
.andDo(print())
.andExpect(status().isOk());
}
which prints the following output:
Resolved Exception:
Type = controller.exception.BadRequestException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 400
Error message = bad request
Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Does anybody have an idea on why is Body missing in the print() output?
Edit: I am not using any custom exception handlers and the code works as expected when I run the server. That is, running the application and making the same request to the server returns back
{"timestamp":1423076185822,
"status":400,
"error":"Bad Request",
"exception":"controller.exception.BadRequestException",
"message":"binding failed for field(s): password, username, username",
"path":"/user/new"}
as expected. Hence, there is a problem with the MockMvc I suppose. It somehow misses to capture the message field of the exception, whereas the default exception handler of the regular application server works as expected.
After opening a ticket for the issue, I was told that the error message in the body is taken care of by Spring Boot which configures error mappings at the Servlet container level and since Spring MVC Test runs with a mock Servlet request/response, there is no such error mapping. Further, they recommended me to create at least one #WebIntegrationTest and stick to Spring MVC Test for my controller logic.
Eventually, I decided to go with my own custom exception handler and stick to MockMvc for the rest as before.
#ControllerAdvice
public class CustomExceptionHandler {
#ExceptionHandler(Throwable.class)
public #ResponseBody
ExceptionResponse handle(HttpServletResponse response, Throwable throwable) {
HttpStatus status = Optional
.ofNullable(AnnotationUtils.getAnnotation(throwable.getClass(), ResponseStatus.class))
.map(ResponseStatus::value)
.orElse(HttpStatus.INTERNAL_SERVER_ERROR);
response.setStatus(status.value());
return new ExceptionResponse(throwable.getMessage());
}
}
#Data
public class ExceptionResponse extends AbstractResponse {
private final long timestamp = System.currentTimeMillis();
private final String message;
#JsonCreator
public ExceptionResponse(String message) {
checkNotNull(message, "message == NULL");
this.message = message;
}
}
This likely means that you either didn't handle the exception or you've really left the body empty. To handle the exception either add an error handler in the controller
#ExceptionHandler
public #ResponseBody String handle(BadRequestException e) {
return "I'm the body";
}
or user the global error handler if you're on 3.2 or above
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler
public #ResponseBody String handleBadRequestException(BadRequestException ex) {
return "I'm the body";
}
}
with this the body will be populate, you should populate it with your error message
Updated solution:
If you don't want to do a full integration test but still want to make sure the message is as expected, you can still do the following:
String errorMessage = getMockMvc()
.perform(post("/user/new"))
...
.andReturn().getResolvedException().getMessage();
assertThat(errorMessage, is("This is the error message!");
this example is useful when I want to validate the existence of an object.
#ResponseStatus(value=HttpStatus.NOT_FOUND)
public class CustomGenericException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String errCode;
private String errMsg;
#Controller
public class MainController {
#RequestMapping(value = "/units/{id}", method = RequestMethod.GET)
public ModelAndView getPages(Integer id)
throws Exception {
if ( service.getUnidad(id) == null) {
// go handleCustomException
throw new CustomGenericException("E888", "This is custom message");
}
}
#ExceptionHandler(CustomGenericException.class)
public ModelAndView handleCustomException(CustomGenericException ex) {
ModelAndView model = new ModelAndView("error/generic_error");
model.addObject("errCode", ex.getErrCode());
model.addObject("errMsg", ex.getErrMsg());
return model;
}
URL : /units/85
The unit 85 does not exist.
But I want to custime exception when I enter a URL invalid (For example /thisurlnoexists),
and the output should be THIS URL IS INCORRECT.
So I want to know if there is any way to intercept url exepcion customize without having to type throw new EXAMPLEEXCEPTION in the method. The same would like to know if I get an SQL error.
Thanks in advance
UPDATE
For 404 page not found , its work fine. The code is
web.xml
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
controller
#RequestMapping("error")
public String customError(HttpServletRequest request, HttpServletResponse response, Model model) {
model.addAttribute("errCode", "324");
model.addAttribute("errMsg", "PAGE NOT FOUND");
return "error";
}
But for Database this code not found
#ControllerAdvice
public class GeneralExceptionController {
#ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError(ModelMap model, Exception exception) {
model.addAttribute("errCode", "ERROR");
model.addAttribute("errMsg", "SQL");
return "error";
}
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception exception) {
ModelAndView mav = new ModelAndView();
mav.addObject("errCode", exception);
mav.addObject("errMsg", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
Controller
#RequestMapping(value = "/sites", method = RequestMethod.GET)
public String getSites(#RequestParam(required = false) String error, ModelMap modelMap) {
List sites = siteBusiness.getAllSites(); //assume that the database is offline, at this point the exception originates
modelMap.put("sites", sites);
return "sites";
}
Spring controller has different notions for inexistant, and invalid Urls.
Taking your example :
/uuuunits/* : NoSuchRequestHandlingMethodException (at DispatcherServlet level) -> 404
/units/foo : (you asked for an Integer ) : TypeMismatchException -> 400
/units/85 : to be dealt with by controller.
You will find references on Spring Reference Manual/ Web MVC framework / Handling Exceptions
If you're looking for Urls that are invalid, it means those URL don't Exist. Hence, all that you need is a 404-Page not Found handler, and you can easily set up that in spring.
About connection error to database, The same applies to it also.
You can make your application container handle such exceptions.
Uncaught exceptions within an application can be forwarded to an error page as defined in the deployment descriptor (web.xml).
<error-page>
<exception-type>Your-exception-here</exception-type>
<location>/error</location>
</error-page>
You can a common page for all your DB errors using the following code snippet.
I want to insert a record to database so this is my controller :
#RequestMapping(value="/ajouter_activite",method = RequestMethod.POST)
public String AddActivity(#ModelAttribute Movement movement, ModelMap model,BindingResult result){
AddActivityValidator actvalidator = new AddActivityValidator();
actvalidator.validate(movement, result);
if(!result.hasErrors()){
boolean n;
n=actservice.addMovement(movement);
if(n==true){model.addAttribute("success","true");}
else {model.addAttribute("echec","true");}
return "/FicheService";}
else{return "/FicheService";
}
}
When i send my form i get this exception :
Etat HTTP 500 - Request processing failed; nested exception is org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.lang.String gestion.delegation.controller.FicheServiceController.AddActivity(gestion.delegation.domaine.Movement,org.springframework.ui.ModelMap,org.springframework.validation.BindingResult)]; nested exception is java.lang.IllegalStateException: Errors/BindingResult argument declared without preceding model attribute. Check your handler method signature!
Where is the wrong with that ?
Try with
public String AddActivity(#ModelAttribute Movement movement, BindingResult result, ModelMap model)
method signature.
see example 17.1 in http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-methods for more info.
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>