Using MultiActionController - java

Currently i am using paging on my page which uses MultiActionController which displays a jsp page perfectly , on the same page now i want to validate a simple textfield (input/form:input) also want to retrieve name and id from a dropdown(Select option) once a link is clicked. Simple !!
Two questions
Can i use a class implements Validator? and inject it same way as simpleformcontroller in config or some other way within the controller? How? example please?
Can i use java bean in jsp -> i always get error of binding, how to indicated controller to use this bean? i have have passed as argument to my method add and also tried overriding newCommandObject
Controller.java
public ModelAndView add(HttpServletRequest request, HttpServletResponse response, Person person) throws Exception {
return new ModelAndView("userpage");
}
#Override
protected Object newCommandObject(Class clazz)
throws Exception {
return new Person();
}

I will do something like below in Spring version > 2.5
#Controller
public class YourController
{
protected final Log logger = LogFactory.getLog(getClass());
private final String yourInputJsp = "yourInputJsp";
private final String yourInputJspSuccess = "yourInputJspSuccess";
private YourService yourService;
#Autowired
#Qualifier("yourFormValidator")
private YourFormValidator validator;
#RequestMapping(value = "/yourRequest.htm", method = RequestMethod.GET)
public String referenceData(ModelMap model, HttpServletRequest request) throws Exception
{
yourService = new YourServiceImpl(ContextHandler.getWebAppContext(request));
YourFormData yourFormData = new YourFormData();
model.addAttribute("yourFormData", yourFormData);
return yourInputJsp;
}
#InitBinder()
public void initBinder(WebDataBinder binder) throws Exception {
binder.registerCustomEditor(String.class, new StringMultipartFileEditor());
}
#RequestMapping(value="/yourRequest.htm", method = RequestMethod.POST)
public String process(#ModelAttribute("yourFormData") YourFormData yourFormData, BindingResult result, SessionStatus status, HttpServletRequest request)
{
String mav = yourInputJsp;
validator.validate(yourFormData, result);
if(!result.hasErrors())
{
//Some business logic
mav = "redirect:yourInputJspSuccess.htm";
status.setComplete();
}
return mav;
}
}

Related

Spring changed session values

In Spring application, I need to hold user value until I don't remove or destroy.
According to it, I have used the HttpSession in the Controller as follows
#Controller
public class MyController {
#RequestMapping(value = { "/search" }, method = RequestMethod.POST) //this hander called once
public String search(SearchVo aSearchVo, BindingResult result,
ModelMap model,HttpSession httpsession) {
if (result.hasErrors()) {
model.addAttribute("searches", new SearchVo());
return "home";
}
httpSession.setAttribute("searchstring", aSearchVo.getSearchString());
return "caseResult";
}
#SuppressWarnings("unchecked")
#RequestMapping(value = { "/filtersearch" }, method = RequestMethod.POST) //This handler call again and again
public String filterSearch(#ModelAttribute("filter") FilterVo fvo,ModelMap model , HttpSession httpSession){
String searchKeyWorld=httpSession.getAttribute("searchstring");
System.out.println(searchKeyWorld);
searchKeyWorld+=fvo.getFilterWorld();
return "caseResult";
}
}
but in the session variable, the value gets changed automatically as in the last filter; as I haven't set any session variable in filtersearch Handler
You need to use #SessionAttributes for putting variable in http session.
This is a class level annoation.
#Controller
#SessionAttributes("searches")
public class MyController{
#RequestMapping(value = { "/search" }, method = RequestMethod.POST) //this hander called once
public String search(SearchVo aSearchVo, BindingResult result,
ModelMap model,WebRequest webRequest) {
if (result.hasErrors()) {
model.addAttribute("searches", new SearchVo());
return "home";
}
model.addAttribute("searches", new SearchVo());
//For removing anything from session
//webRequest.removeAttribute("searches", WebRequest.SCOPE_SESSION);
return "caseResult";
}
}

passing parameter in webservice API

I am new at webservices and currently able to run my query by calling https://localhost/application/service/v1.0/contacts/account={accountId}
I want to make my url look like https://localhost/application/service/v1.0/contacts?account={accountId}
May I know How to achieve this not using QueryParam ? I am working in spring mvc
#Controller
public class ContactListResponseController extends BaseWebServiceController
{
public static final String PATH = "/v" + VERSION + "/contacts/account={accountId}";
#Autowired
private ContactService contactService;
#RequestMapping(value = PATH, method = RequestMethod.GET)
#ResponseBody
public ContactListResponseBean doGetMyAssignedAccounts (#PathVariable String accountId,
HttpServletRequest request,
HttpSession session,
HttpServletResponse response,
#ModelAttribute(User.USER_REQUEST_VAR) User user)
throws Exception
{
List<ContactSummaryWebServiceBean> contactList = contactService.getContactsListForCallPointWebService(accountId);
ContactListResponseBean result = new ContactListResponseBean(contactList);
return result;
}
}
It is a simple thing, try this:
#Controller
public class ContactListResponseController extends BaseWebServiceController
{
public static final String PATH = "/v" + VERSION + "/contacts";
#Autowired
private ContactService contactService;
#RequestMapping(value = PATH, method = RequestMethod.GET)
#ResponseBody
public ContactListResponseBean doGetMyAssignedAccounts (#RequestParam("account") String accountId,
HttpServletRequest request,
HttpSession session,
HttpServletResponse response,
#ModelAttribute(User.USER_REQUEST_VAR) User user)
throws Exception
{
List<ContactSummaryWebServiceBean> contactList = contactService.getContactsListForCallPointWebService(accountId);
ContactListResponseBean result = new ContactListResponseBean(contactList);
return result;
}
}
This sample.
#RequestMapping("/pets/{petId}")
public void findPet(#PathVariable String ownerId, #PathVariable String petId, Model model) {
// implementation omitted
}
Your code.
#Controller
public class ContactListResponseController extends BaseWebServiceController
{
public static final String PATH = "/v" + VERSION + "/contacts/{accountId}";
#Autowired
private ContactService contactService;
#RequestMapping(value = PATH, method = RequestMethod.GET)
#ResponseBody
public ContactListResponseBean doGetMyAssignedAccounts (#PathVariable String accountId,
HttpServletRequest request,
HttpSession session,
HttpServletResponse response,
#ModelAttribute(User.USER_REQUEST_VAR) User user)
throws Exception
{
List<ContactSummaryWebServiceBean> contactList = contactService.getContactsListForCallPointWebService(accountId);
ContactListResponseBean result = new ContactListResponseBean(contactList);
return result;
}
}
Ajax url = "/v" + VERSION + "/contacts/" + accountId,
:D

Spring PathVariable used outside

I have Spring rest controller that provides operations on Project entity. All methods use same entity accessing code. I don't want to copy&paste #PathVariable parameters in all methods, so I've made something like this.
#RestController
#RequestMapping("/projects/{userName}/{projectName}")
public class ProjectController {
#Autowired
ProjectService projectService;
#Autowired
protected HttpServletRequest context;
protected Project project() {
// get {userName} and {projectName} path variables from request string
String[] split = context.getPathInfo().split("/");
return projectService.getProject(split[2], split[3]);
}
#RequestMapping(method = GET)
public Project get() {
return project();
}
#RequestMapping(method = GET, value = "/doSomething")
public void doSomething() {
Project project = project();
// do something with project
}
// more #RequestMapping methods using project()
}
Is it possible to autowire path variables into controller by annotation so I don't have to split request path and get parts of it from request string for project() method?
In order to do custom binding from request you've got to implement your own HandlerMethodArgumentResolver (it's a trivial example without checking if path variables actually exist and it's also global, so every time you will try to bind to Project class this argument resolver will be used):
class ProjectArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterType().equals(Project.class);
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return getProject(uriTemplateVars.get("userName"), uriTemplateVars.get("projectName"));
}
private Project getProject(String userName, String projectName) {
// replace with your custom Project loading logic
Project project = new Project(userName, projectName);
return project;
}
}
and register it using WebMvcConfigurerAdapter:
#Component
public class CustomWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new ProjectArgumentResolver());
}
}
In your controller you have to put Project as a method argument, but do not annotate it with #PathVariable:
#Controller
#RequestMapping("/projects/{userName}/{projectName}")
public class HomeController {
#RequestMapping(method = RequestMethod.GET)
public void index(Project project){
// do something
}
}

Restrict Spring page view into POST only

My Spring controller looks like this:
#Controller
#RequestMapping(value = "calc")
public class CalcController {
protected final Log logger = LogFactory.getLog(getClass());
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.GET)
public String showCalcPage(
#ModelAttribute("myModel") MyModel myModel,
Model model, HttpServletRequest request) {
// assemble page
return "calc";
}
#RequestMapping(method = RequestMethod.POST)
public String showResultsPage(
#ModelAttribute("myModel") MyModel myModel,
BindingResult result, Model model,
final RedirectAttributes redirectAttributes,
HttpServletRequest request) {
myService.evaluate(myModel);
redirectAttributes.addFlashAttribute("myModel", myModel);
model.addAttribute("myModel", myModel);
return "redirect:calc/results";
}
#RequestMapping(value = "/results")
public String showResultsPage(ModelMap model,
#ModelAttribute("myModel") final MyModel myModel,
final BindingResult bindingResult) {
// assemble page
return "results";
}
}
I have a mapping of the URL calc with both GET and POST and another for calc/results.
This works perfectly for me but whenever I try to access calc/results directly, the page still renders.
Hence I did a POST restriction to its RequestMethod like:
#RequestMapping(value = "/results", method = RequestMethod.POST)
public String showResultsPage(ModelMap model,
#ModelAttribute("myModel") final MyModel myModel,
final BindingResult bindingResult) {
// assemble page
return "results";
}
This eliminated the direct viewing of the mapping by throwing a 405 but when I submit my form from calc, the error still persists.
How do I merge these two situations that I have?
I actually just want two controllers like the one below to implement POST and page restriction but it's not working in my part (I diagnosed it to the different mapping of jsp).
#Controller
#RequestMapping(value = "calc")
public class CalcController {
protected final Log logger = LogFactory.getLog(getClass());
#Autowired
private MyService myService;
#RequestMapping(method = RequestMethod.GET)
public String showCalcPage(
#ModelAttribute("myModel") MyModel myModel,
Model model, HttpServletRequest request) {
// assemble page
return "calc";
}
#RequestMapping(value = "/results", method = RequestMethod.POST)
public String showResultsPage(
#ModelAttribute("myModel") MyModel myModel,
BindingResult result, Model model,
final RedirectAttributes redirectAttributes,
HttpServletRequest request) {
// assemble page
myService.evaluate(myModel);
model.addAttribute("myModel", myModel);
return "redirect:results";
}
}
I finally implemented both POST restriction and successful viewing of the calc/results page (but without redirect since it causes a "redirect loop" according to my Tomcat server).
Here is the final controller:
#Controller
public class CalcController {
protected final Log logger = LogFactory.getLog(getClass());
#Autowired
private MyService myService;
#RequestMapping(value = "calc", method = RequestMethod.GET)
public String showCalcPage(
#ModelAttribute("myModel") MyModel myModel,
Model model, HttpServletRequest request) {
// assemble page
return "calc";
}
#RequestMapping(value = "calc/results")
public String showResultsPage(
#ModelAttribute("myModel") MyModel myModel,
ModelMap model, final BindingResult bindingResult,
HttpServletRequest request) {
// assemble page
// apply BindingResult validation in da fyoochoor
myService.evaluate(myModel);
model.addAttribute("myModel", myModel);
return "results";
}
}
Visiting calc/results directly now throws an HTTP 500 and that will keep it secured. Just make sure to declare a page for this exception in your web.xml for aesthetics upon deployment.

Testing Spring #MVC annotations

I ran into a problem the other day where a #Valid annotation was accidentally removed from a controller class. Unfortunately, it didn't break any of our tests. None of our unit tests actually exercise the Spring AnnotationMethodHandlerAdapter pathway. We just test our controller classes directly.
How can I write a unit or integration test that will correctly fail if my #MVC annotations are wrong? Is there a way I can ask Spring to find and exercise the relevant controller with a MockHttpServlet or something?
I write integration tests for this kind of thing. Say you have a bean with validation annotations:
public class MyForm {
#NotNull
private Long myNumber;
...
}
and a controller that handles the submission
#Controller
#RequestMapping("/simple-form")
public class MyController {
private final static String FORM_VIEW = null;
#RequestMapping(method = RequestMethod.POST)
public String processFormSubmission(#Valid MyForm myForm,
BindingResult result) {
if (result.hasErrors()) {
return FORM_VIEW;
}
// process the form
return "success-view";
}
}
and you want to test that the #Valid and #NotNull annotations are wired correctly:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml"})
public class MyControllerIntegrationTest {
#Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
#Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
}
ModelAndView handle(HttpServletRequest request, HttpServletResponse response)
throws Exception {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
final ModelAndView mav = handlerAdapter.handle(request, response, controller);
return mav;
}
#Test
public void testProcessFormSubmission() throws Exception {
request.setMethod("POST");
request.setRequestURI("/simple-form");
request.setParameter("myNumber", "");
final ModelAndView mav = handle(request, response);
// test we're returned back to the form
assertViewName(mav, "simple-form");
// make assertions on the errors
final BindingResult errors = assertAndReturnModelAttributeOfType(mav,
"org.springframework.validation.BindingResult.myForm",
BindingResult.class);
assertEquals(1, errors.getErrorCount());
assertEquals("", errors.getFieldValue("myNumber"));
}
See my blog post on integration testing Spring's MVC annotations
Sure. There's no reason why your test can't instantiate its own DispatcherServlet, inject it with the various items which it would have in a container (e.g. ServletContext), including the location of the context definition file.
Spring comes with a variety of servlet-related MockXYZ classes for this purpose, including MockServletContext, MockHttpServletRequest and MockHttpServletResponse. They're not really "mock" objects in the usual sense, they're more like dumb stubs, but they do the job.
The servlet's test context would have the usual MVC-related beans, plus your beans to test. Once the servlet is initialized, create the mock requests and responses, and feed them into the servet's service() method. If request gets routed correctly, you can check the results as written to the mock response.
In upcoming spring 3.2 (SNAPSHOT available) or with spring-test-mvc (https://github.com/SpringSource/spring-test-mvc) you can do it like this:
first we emulate Validation as we do not want to test the validator, just want to know if validation is called.
public class LocalValidatorFactoryBeanMock extends LocalValidatorFactoryBean
{
private boolean fakeErrors;
public void fakeErrors ( )
{
this.fakeErrors = true;
}
#Override
public boolean supports ( Class<?> clazz )
{
return true;
}
#Override
public void validate ( Object target, Errors errors, Object... validationHints )
{
if (fakeErrors)
{
errors.reject("error");
}
}
}
this is our test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class RegisterControllerTest
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
#InjectMocks
private RegisterController registerController;
#Autowired
private LocalValidatorFactoryBeanMock validator;
#Before
public void setup ( )
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
// if you want to inject mocks into your controller
MockitoAnnotations.initMocks(this);
}
#Test
public void testPostValidationError ( ) throws Exception
{
validator.fakeErrors();
MockHttpServletRequestBuilder post = post("/info/register");
post.param("name", "Bob");
ResultActions result = getMockMvc().perform(post);
// no redirect as we have errors
result.andExpect(view().name("info/register"));
}
#Configuration
#Import(DispatcherServletConfig.class)
static class Config extends WebMvcConfigurerAdapter
{
#Override
public Validator getValidator ( )
{
return new LocalValidatorFactoryBeanMock();
}
#Bean
RegisterController registerController ( )
{
return new RegisterController();
}
}
}

Categories

Resources