I have the following function in my abstract controller
public abstract class GenericController<T extends PersistentObject> {
...
...
...
#RequestMapping(value = "/validation.json", method = RequestMethod.POST)
#ResponseBody
public ValidationResponse ajaxValidation(#Valid T t, BindingResult result) {
ValidationResponse res = new ValidationResponse();
if (!result.hasErrors()) {
res.setStatus("SUCCESS");
} else {
res.setStatus("FAIL");
List<FieldError> allErrors = result.getFieldErrors();
List<ErrorMessage> errorMesages = new ArrayList<ErrorMessage>();
for (FieldError objectError : allErrors) {
errorMesages.add(new ErrorMessage(objectError.getField(),
objectError.getDefaultMessage()));
}
res.setErrorMessageList(errorMesages);
}
return res;
}
At most cases the validation is sufficient for different kind of entities. Now I would like to customize the validation on my concrete controller as
#Controller
#RequestMapping("user")
public class UserController extends GenericController<User> {
#RequestMapping(value = "/validation.json", method = RequestMethod.POST)
#ResponseBody
public ValidationResponse ajaxValidation(#Valid User user,
BindingResult result, Locale locale) {
ValidationResponse res = super.ajaxValidation(user, result);
if (!user.getPassword().equals(user.getConfirmPassword())) {
res.setStatus("FAIL");
res.getErrorMessageList().add(
new ErrorMessage("confirmPassword", messageSource
.getMessage("password.mismatch", null, locale)));
}
return res;
}
}
With this I get the following error java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'userController' bean method. How can I solve this issue ? Is there a better approach ?
The problem here is that after extending your generic controller you have two different methods
public ValidationResponse ajaxValidation(#Valid T t, BindingResult result)
and
public ValidationResponse ajaxValidation(#Valid User user,
BindingResult result, Locale locale)
with the exact same mapping
user/validation.json
An ugly solution would be to add the Locale locale param to your abstract controller method (even if you don't use it) and add the #Overwrite annotation to the UserController method (you'll get a compilation error otherwise). This way the two methods become one.
The generic controller is extended by other classes ? Then you will have two identical requestmappings.
Are you sure the UserController is correctly getting user prepend request mapping applied ? As this works :
#RequestMapping("test")
public class ExampleController extends AbstractController {
#RequestMapping(value = "/Home", method = RequestMethod.GET)
public String getHome(Model model) {
return "home";
}
with the same mapping in AbstractController
#RequestMapping(value = "/Home", method = RequestMethod.GET)
public String getHome(Model model) {
return "home";
}
Related
I have startController and start view. In this view I input number and amount and validate it. If validation was successful, I want pass this parameters(number and amount) to another controller, and after that make some operations with it, in this controller. I see two way:
make this operations in first controller, in another methods and use second view for it. But my controller will very big and all logic will be this.
create second controller and second view and pass parameters to this controller.
I make this:
#Controller
#RequestMapping("/")
public class StartController {
#Autowired
private ValidateService validateService;
#RequestMapping(method = RequestMethod.GET)
public ModelAndView printWelcome() {
ModelAndView modelAndView = new ModelAndView("start");
return modelAndView;
}
#RequestMapping(value = "process", method = RequestMethod.POST)
public ModelAndView process(HttpServletRequest request) {
ModelAndView modelAndView;
String phoneNumber = request.getParameter("phone_number");
int amount = Integer.parseInt(request.getParameter("amount"));
String result = validateService.validate(phoneNumber, amount);
if (!result.equals("OK")) {
modelAndView = new ModelAndView("start");
modelAndView.addObject("result",result);
}else {
modelAndView = new ModelAndView("redirect:/check/process");
modelAndView.addObject("phone_number", phoneNumber);
modelAndView.addObject("amount",amount);
}
return modelAndView;
}
and if result != OK I redirect to new controller
#Controller
#RequestMapping("/check")
public class CheckController {
#RequestMapping(value = "process", method = RequestMethod.GET)
public ModelAndView process(HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView("check");
String phoneNumber = request.getParameter("phone_number");
int amount = Integer.parseInt(request.getParameter("amount"));
return modelAndView;
}
}
But I need pass parameters with RequestMethod.POST and it will not work. How do it?
You can return a ModelAndView with parameters as follow:
return new ModelAndView("redirect:/check/process?phone_number="+yourPhoneNumber+"&amount="+amount)
You can use forward to go to a new controller right?
"forward:/test2?param1=foo¶m2=bar";
Please see below link for more details.
Spring forward with added parameters?
spring mvc
#ModelAttribute("classname"),
How to make the argument "classname" a dynamic one ?
Whatever comes from view can get appended there.
Instantiation of the command object is the only place where Spring needs to know a command class. However, you can override it with #ModelAttribute annotated method:
#RequestMapping(method = RequestMethod.POST)
public void show(HttpServletRequest request,
#ModelAttribute("objectToShow") Object objectToShow)
{
...
}
#ModelAttribute("objectToShow")
public Object createCommandObject() {
return getCommandClass().newInstance();
}
By the way, Spring also works fine with the real generics:
public abstract class GenericController<T> {
#RequestMapping("/edit")
public ModelAndView edit(#ModelAttribute("t") T t) { ... }
}
#Controller #RequestMapping("/foo")
public class FooController extends GenericController<Foo> { ... }
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! :)
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.
Can I do the following in Spring MVC
Suppose I have the Base GenericController as follows with one request mapping "/list"
#Controller
public class GenericController<T>{
#RequestMapping(method = RequestMethod.GET, value = "/list")
public #ResponseBody List<T> getMyPage(){
// returns list of T
}
}
Below are my two controllers
#Controller(value = "/page1")
public class Page1Controller extends GenericController<Page1>{
}
#Controller(value = "/page2")
public class Page2Controller extends GenericController<Page2>{
}
Now will i be able to access the url "/page1/list" and "/page2/list" where first goes to Page1Controller and second goes to Page2Controller.
That's not possible and was already rejected, see SPR-10089. I think this would be somewhat confusing and in addition, it is very unlikely that those method behave exactly the same besides the different mapping.
But you can use delegation instead:
public class BaseController<T> {
public List<T> getPageList(){
// returns list of T
}
}
#Controller(value = "/page1")
public class Page1Controller extends BaseController<Page1>{
#RequestMapping(method = RequestMethod.GET, value = "/list")
public #ResponseBody List<Page1> getMyPage() {
return super.getPageList();
}
}
#Controller(value = "/page2")
public class Page2Controller extends BaseController<Page2>{
#RequestMapping(method = RequestMethod.GET, value = "/list")
public #ResponseBody List<Page2> getMyPage() {
return super.getPageList();
}
}
For those looking for something similar with Spring Framework 4.x, the class hierarchy provided by the OP is possible. A sample application is available on Github. It allows users to view a list of books or a list of magazines as JSON.