My usecase: a single html-form can triggered as a save or an update event. Depending on the event the validation is is performed by a different validator. This works so far with the following code. The only problem I have, that I want the field-errors to be mapped in all cases to "saveDto", so I can map them in my form.
Any hints welcome.
#Inject
private SaveValidator saveValidator;
#Inject
private UpdateValidator updateValidator;
#RequestMapping(value = EVENT_SAVE, method = RequestMethod.POST)
protected String doSave(#Valid #ModelAttribute("saveDto") final SaveDto saveDto,
final BindingResult bindingResult, final Model model, final HttpServletRequest request)
{
if (bindingResult.hasErrors())
{
// ...
}
}
#RequestMapping(value = EVENT_UPDATE, method = RequestMethod.POST)
protected String doUpdate(#Valid #ModelAttribute("updateDto") final SaveDto saveDto,
final BindingResult bindingResult, final Model model, final HttpServletRequest request)
{
if (bindingResult.hasErrors())
{
// ...
}
}
#InitBinder("saveDto")
protected void initSaveValidator(final WebDataBinder binder)
{
binder.addValidators(saveValidator);
}
#InitBinder("updateDto")
protected void initUpdateValidator(final WebDataBinder binder)
{
binder.addValidators(updateValidator);
}
Try the hibernate validation group feature along with #Validated annotation
Related
I have problem with CRUD edit operation. When i click Edit which is written in this way in jsp file
Edit
i got error
HTTP Status 404 – Not Found
Type Status Report
Message /Firstaidkit/editMedicines
Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
EditController
#WebServlet(value = "/editMedicines")
public class MedicinesEditController extends HttpServlet {
private static final long serialVersionUID = 1L;
#RequestMapping(method = RequestMethod.GET)
public ModelAndView editMedicines(HttpServletRequest request) {
int medicinesId = Integer.parseInt(request.getParameter("id"));
Medicines medicines = GenericDAO.get(medicinesId);
ModelAndView model = new ModelAndView("editform");
model.addObject("medicines", medicines);
return model;
}
}
GenericDAO
public interface GenericDAO <T, PK extends Serializable> {
//CRUD
T create(T newObject);
T read(PK primaryKey);
public void update(Medicines medicines);
public void delete(T id);
List<T> getAll();
public static Medicines get(int medicinesId) {
return null;
}
}
MedicinesDAOImpl
private final static String UPDATE_MEDICINES =
"UPDATE medicines SET name=:name, drugform=:drugform, quantity=:quantity, expiration_date=:expiration_date, description=:description WHERE id_medicines=:id_medicines;";
#Override
public void update(Medicines medicines) {
jdbcTemplate.update(UPDATE_MEDICINES, medicines.getName(), medicines.getDrugForm(),
medicines.getQuantity(), medicines.getExpirationDate(), medicines.getId());
}
}
editform.jsp
<form class="form-signin" method="post" action="editMedicines">
I believe the problem is that the URL your anchor is linked to does not exist. You should have an annotation dictating the path on your MedicinesEditController at a class level. Assuming you want the path of this endpoint to be /Firstaidkit/editMedicines, the following should work:
#Path(value = "/Firstaidkit") // possibly a different but similar annotation
public class MedicinesEditController extends HttpServlet {
#RequestMapping(value = "/editMedicines", method = RequestMethod.GET)
public ModelAndView editMedicines(HttpServletRequest request) {
int medicinesId = Integer.parseInt(request.getParameter("id"));
Medicines medicines = GenericDAO.get(medicinesId);
ModelAndView model = new ModelAndView("editform");
model.addObject("medicines", medicines);
return model;
}
}
Otherwise, you need to alter the link of the anchor to reference the root of the application, followed by your endpoint of /editMedicines. This can be accomplished by using the following anchor, as described here:
Edit
Edit: Try the following
MedicinesEditController
#RequestMapping(value = "/editMedicines")
public class MedicinesEditController extends HttpServlet {
private static final long serialVersionUID = 1L;
#GetMapping
public ModelAndView editMedicines(HttpServletRequest request) {
int medicinesId = Integer.parseInt(request.getParameter("id"));
Medicines medicines = GenericDAO.get(medicinesId);
ModelAndView model = new ModelAndView("editform");
model.addObject("medicines", medicines);
return model;
}
}
editform.jsp
<form class="form-signin" method="GET" action="editMedicines">
anchor
(note: try these variations, as the link changes depending on where you are when you click it. View this stack to help determine the correct link)
Edit
Edit
Edit
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
#Controller
public ModeAndView createUser(User user){
}
how can get controller method createUser's parameter user value
in interceptor 's postHandle ???
You cannot directly.
As you see in its parameters, an Interceptor has direct access to the request, the response and the ModelAndView. If you need to have access to the method parameter user, the simplest is to put it in model.
public ModeAndView createUser(User user){
ModelAndView mav = new ModelAndView();
mav.addAttribute("user", user);
...
return mav;
}
Then in interceptor postHandle method, you simply do
User user = (User) modelAndView.getAttribute("user");
You could write an org.springframework.web.servlet.HandlerInterceptor. (or its convenience subclass HandlerInterceptorAdapter)
#See: Spring Reference chapter: 15.4.1 Intercepting requests - the HandlerInterceptor interface
It has the method:
void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception;
This method is invoked after the controller is done and before the view is rendered. So you can use it, to add some properties to the ModelMap
An example:
public class VersionAddingHandlerInterceptor extends HandlerInterceptorAdapter {
/**
* The name under which the version is added to the model map.
*/
public static final String VERSION_MODEL_ATTRIBUTE_NAME =
"VersionAddingHandlerInterceptor_version";
/**
* it is my personal implmentation
* I wanted to demonstrate something usefull
*/
private VersionService versionService;
public VersionAddingHandlerInterceptor(final VersionService versionService) {
this.versionService = versionService;
}
#Override
public void postHandle(final HttpServletRequest request,
final HttpServletResponse response, final Object handler,
final ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
modelAndView.getModelMap().
addAttribute(VERSION_MODEL_ATTRIBUTE_NAME,
versionService.getVersion());
}
}
}
webmvc-config.xml
<mvc:interceptors>
<bean class="demo.VersionAddingHandlerInterceptor" autowire="constructor" />
</mvc:interceptors>
I am just a newbie about Spring.
I am now using #ExceptionHandler to handle all the exception for my web application. And after I catch the exception, it will go to and error.jsp page displaying the error message.
I have a ParentController and in that, I have:
#org.springframework.web.bind.annotation.ExceptionHandler(PortalException.class)
public ModelAndView handle(PortalException e, HttpServletRequest request) {
ModelMap map = new ModelMap();
map.addAttribute("message", e.getMessage());
return new ModelAndView("/error", map);
}
and I have a ErrorControllerextends the ParentController to add the attributes:
#Controller
public class ErrorController extends ParentSecureController {
#RequestMapping(value = "/error", method = RequestMethod.POST)
#ResponseBody
public String errorHandler(Model model, HttpServletRequest request) {
model.addAttribute("excetpion.message", request.getParameter("message"));
return "/error";
}
}
In the error.jsp:
<p>Excpetion is: ${exception.message}</p>
When I run my application, I can catch the exception and jump to error.jsp, but no exception message is display.
Anyone can help me to figure out how to solve it.
Please try use:
#Controller
public class ErrorController extends ParentSecureController {
#RequestMapping(value = "/error", method = RequestMethod.POST)
#ResponseBody
public String errorHandler(Map<String, Object> map, HttpServletRequest request) {
map.put("excetpion.message", request.getParameter("message"));
return "/error";
}
}
UPDATE
Map you get it messae from #Controller to View in this case error.jsp
I hope these helped! :)
Is it possible to run #Validated after the #RequestMapping method has started? The reason is that I need to modify the #ModelAttribute before actually validating it.
Ideally something like this.
#RequestMapping(value = "/doSomething",
method = RequestMethod.POST)
public final String DoSomething(
#ModelAttribute(value = "myobject") final MyObject myobject) {
//.... do some processing on myobject
//.... now validate
BindingResult bindingResult = validate(myobject);
//...
And a method like this
private final BindingResult validate(
#Validated(value = {Group1.class, Group2.class}) MyObject myobject) {
return bindingResult //somehow return a BindingResult
}
Use your own custom Validator
http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/validation.html#validation-binder
Using Spring 3.2.3, I'm trying to implement a simple CRUD controller that handles REST-ful URLs. It relies on a PropertyEditor to convert a path variable to a BusinessService entity by loading it from an application service. Code is as follows:
#Controller
public class BusinessServiceController {
#Autowired
private BusinessServiceService businessSvcService;
public BusinessServiceController() {
}
#InitBinder
public void initBinder(final WebDataBinder binder) {
binder.registerCustomEditor(BusinessService.class, new BusinessServicePropertyEditor(businessSvcService));
}
#RequestMapping(value = "/ui/account/business-services/{businessSvc}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView update(#ModelAttribute("businessSvc") #Valid final BusinessService businessSvc, final BindingResult result,
final RedirectAttributes redirectAttribs) throws UnknownBusinessServiceException {
ModelAndView mav;
if (result.hasErrors()) {
mav = new ModelAndView("/business-service/edit");
}
else {
businessSvcService.updateBusinessService(XSecurity.principal().getId(), businessSvc);
mav = new ModelAndView("redirect:/ui/account/business-services");
redirectAttribs.addFlashAttribute("message", Message.info("businessService.updated", businessSvc.getTitle()));
}
return mav;
}
}
public class BusinessServicePropertyEditor extends PropertyEditorSupport {
private final BusinessServiceService businessSvcService;
public BusinessServicePropertyEditor(final BusinessServiceService businessSvcService) {
this.businessSvcService = businessSvcService;
}
#Override
public String getAsText() {
final BusinessService svc = (BusinessService) getValue();
return Long.toString(svc.getId());
}
#Override
public void setAsText(final String text) {
final BusinessService svc = businessSvcService.getBusinessService(Long.parseLong(text));
setValue(svc);
}
}
According to SPR-7608, starting from Spring 3.2, #ModelAttribute method argument resolution checks if a path variable by the same name exists (it does here), in which case it tries to convert that path variable's value to the target parameter type through registered Converters and PropertyEditors. This is not what I'm experiencing. When I inspect what ServletModelAttributeMethodProcessor does, it clearly uses the request DataBinder's ConversionService to perform type conversion, which does not consider registered PropertyEditors, and hence BusinessServicePropertyEditor#setAsText is never called.
Is this a configuration problem or an actual bug?
Thanks for your help!
Spring's ConversionService and Converters are replacement for standard Java Beans PropertyEditors.
You need to implement Converter instead of PropertyEditor if this feature is based purely on conversion service.
To register your custom converters in WebDataBinder you might use ConfigurableWebBindingInitializer or #InitBinder method.