Spring MVC - Basic RequestMapping question - java

I'm new to Spring MVC. I'm getting errors on the following (not sure yet what;s the full scope of info requierd to assist me):
Working fine:
#RequestMapping(value = "startpage.do")
public ModelAndView startpage(HttpServletRequest req, HttpServletResponse res) {
.
.
ModelAndView mv = new ModelAndView("startpage");
mv.getModelMap().addAttribute("loginPage", loginPage);
return mv;
But failing:
#RequestMapping(value = "somecontroller.do")
public ModelAndView ftcontroller(HttpServletRequest req, HttpServletResponse res, ModelAndView mav) {.. ...
ModelAndView mv = new ModelAndView("startpage");
mv.getModelMap().addAttribute("loginPage", loginPage);
return mav;
As you can see, same code, different request mapping. Could it be that this is consuing the MVC somehow to get confused?
The error I'm getting is:
java.lang.NullPointerException
at jsp_servlet._web_45_inf._jsp.__somecontroller._jspService(__ftcontroller.java:103)
at weblogic.servlet.jsp.JspBase.service(JspBase.java:34)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
Truncated. see log file for complete stacktrace
The biz logic is a login screen "startpage.do" when Login is submitted, it can fail (in which case, startup.do should be retuend once again, in other case, a differn page should be returned.
if there is better way to implement this, I'll be happy to hear that.
(It should be noted that I'm trying to plug-in Spring MVC into an existing project, so I'm trying to make as little as possible changes, and hence not using spring:form etc..)
More info:
The JSP startpage.jsp (resolved from "startpage") has a form with target="somecontroller.do".
Full controller code:
class
{
public static void main(String[] args)
{
#RequestMapping(value = "startpage.do")
public ModelAndView startpage(HttpServletRequest req, HttpServletResponse res) {
System.out.println(">>>>>>HomeController: Passing through (Get Type)...");
LoginPage loginPage = new LoginPage();
ModelAndView mv = new ModelAndView("startpage");
mv.getModelMap().addAttribute("loginPage", loginPage);
return mv;
}
#RequestMapping(value = "somecontroller.do")
public ModelAndView ftcontroller(HttpServletRequest req, HttpServletResponse res)
throws Exception {
// Parsing for login request;
String sUsername = req.getParameter(USER_ID);
String sUserPassword = req.getParameter(PASSWORD);
AbstractResponseDataComponent returnedResponse = new LoginCommand().login(sUsername, sUserPassword);
String returnedView = GlobalConstants.EMPTY_STRING;
JstlView view = new JstlView();
Map model = new HashMap();
if (returnedResponse.isSuccessful())
{
view.setUrl("somecontroller");
model.put("loginResponse", (LoginResponse) returnedResponse);
} else
{
view.setUrl("startpage");
model.put("loginPage", (LoginPage) returnedResponse);
}
return new ModelAndView(view, model);
}
}
}

BTW: when using Spring 3.0 make your method signature more clean:
instead of
public ModelAndView ftcontroller(HttpServletRequest req, HttpServletResponse res)
throws Exception {
String sUsername = req.getParameter(USER_ID);
String sUserPassword = req.getParameter(PASSWORD);
...
do it in the spring 3.0 way:
public ModelAndView ftcontroller(
#RequestParam(USER_ID) String sUsername,
#RequestParam(PASSWORD) String sUserPassword)
throws Exception {
...

For your second question:
if there is better way to implement
this, I'll be happy to hear that.
Are you looking for something like this:
#RequestMapping(value = "startpage.do")
public String startpage() {
...
if (loginFailed) {
return "redirect:startpage.do");
} else {
return "redirect:somecontroller.do");
}
}
(I prefere redirects, because I assume the that login methods has some sideeffects.)

I belive the cause of your exception is the JstlView.
Try not to use the JstlView direct, instad pass the view name as String.
final String viewName;
Map model = new HashMap();
if (returnedResponse.isSuccessful())
{
viewName = "somecontroller";
model.put("loginResponse", (LoginResponse) returnedResponse);
} else
{
viewName = "startpage";
model.put("loginPage", (LoginPage) returnedResponse);
}
return new ModelAndView(viewName, model);

Related

How to access a spring-mvc flash redirectAttribute in the filter chain before the DispatcherServlet is invoked?

I have the following controller:
#Controller
#RequestMapping("/my-account")
public class AccountController {
#RequestMapping(value = "/foo/post",
method = RequestMethod.POST)
public String doPost(final RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("flashAttribute", "flashAttributeValue");
return "redirect:/my-account/foo/get";
}
#RequestMapping(value = "/foo/get",
method = RequestMethod.GET)
public void doGet(final HttpServletRequest request, final Model model) {
System.out.println("in request: " + RequestContextUtils.getInputFlashMap(request).get("flashAttribute"));
System.out.println("in model: " + model.asMap().get("flashAttribute"));
}
}
I would also like to access the flash attribute flashAttribute during the invocation of a filter in the filter chain that finally invokes springs default DispatcherServlet which in turn invokes AccountController.
public class FlashAttributeBasedFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain)
throws ServletException, IOException {
String flashAttribute = // how to access the redirectAttribute flashAttribute here?
// do something with flashAttribute ...
filterChain.doFilter(request, response);
}
The DispatcherServlet uses a org.springframework.web.servlet.FlashMapManager that handles these flash attributes, but it doesn't provide read-only access so I think I would be messing something up if I would use it in the filter. And also the FlashMapManager instance is kept in the dispatcher servlet privately.
Does anybody have an idea how I can make the redirect attribute accessible in the filter chain for the GET request succeeding the POST?
Considering that all these methods return null into my filter (I don't understand why):
RequestContextUtils.getFlashMapManager(httpRequest)
RequestContextUtils.getInputFlashMap(httpRequest)
RequestContextUtils.getOutputFlashMap(httpRequest)
I used a drastic solution: read directly the into the session (where flash attributes are stored).
CopyOnWriteArrayList<FlashMap> what = (CopyOnWriteArrayList<FlashMap>) httpRequest.getSession().getAttribute("org.springframework.web.servlet.support.SessionFlashMapManager.FLASH_MAPS");
if (what != null) {
FlashMap flashMap = what.get(0);
[read flashMap as you read a HashMap]
}
I know, this code is super ugly but at the moment I don't find another solution.
Had the same problem, following works for me.
FlashMap flashMap = new SessionFlashMapManager().retrieveAndUpdate(request, null);
flashMap.get("parameter");

Java/Spring MaxUploadSizeExceededException post parameters

I have a form with a three text fields and a file upload field.
When I reach the MaxUploadSizeExceededException exception I can handle with a class that implements HandlerExceptionResolver.
I have my custom handler class with
resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception){ ... }
My problem is that I need a way to pass some variables to Exception handler (the values of other fields in the form) so I can return a ModelAndView that contains these variables. I don't want to redirect to an error page, I want to return to my Form, without losing inserted values.
I've also a "Validator" that validates other fields and it works, but I don't know how to integrate it with MaxUploadSizeExceededException exception.
My controller implements HandlerExceptionResolver
#Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception)
{
Map<String, Object> model = new HashMap<String, Object>();
if (exception instanceof MaxUploadSizeExceededException)
{
// this is empty!
Map<String,String[]> paramMap = request.getParameterMap();
// model.put("ticketForm", new TicketForm());
// ticketForm.setId();
model.put("err", exception.getMessage());
return new ModelAndView(inserisciticket", model);
} else
{
model.put("err", "Unexpected error: " + exception.getMessage());
return new ModelAndView("error", model);
}
}
This is the function that is called from the form:
#RequestMapping(value = "/inseriscinuovoticket", method = RequestMethod.POST)
#ResponseBody
public String inseriscinuovoticket(
#RequestParam(value = "idArgomento", required = true, defaultValue = "") String idArgomento,
#RequestParam(value = "oggetto", required = true, defaultValue = "") String oggetto,
#RequestParam(value = "descrizione", required = true, defaultValue = "") String descrizione,
#RequestParam(value = "fileuploaded", required = false) MultipartFile fileuploaded,
#ModelAttribute("ticket") TicketForm ticketForm, BindingResult result, Model model, HttpServletRequest request,
Locale locale) throws IOException { .... }
Can you help me?
------------- EDIT 2 --------------------
I tried the method suggested here
public class MultipartExceptionHandler extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (MaxUploadSizeExceededException e) {
handle(request, response, e);
} catch (ServletException e) {
if(e.getRootCause() instanceof MaxUploadSizeExceededException) {
handle(request, response, (MaxUploadSizeExceededException) e.getRootCause());
} else {
throw e;
}
}
}
private void handle(HttpServletRequest request,
HttpServletResponse response, MaxUploadSizeExceededException e) throws ServletException, IOException {
// null
TicketForm t = (TicketForm)request.getAttribute("ticket");
// null
String idArgomento = (String)request.getAttribute("idArgomento");
response.sendRedirect("inserisciticket");
}
}
But also in the handle and in the filter I CAN'T read form parameters (post data).
How can I do???
Thank you.
The following changes resolved the fileUpload size issues(MaxUploadSizeExceededException) in my application.
Do the following changes in "application.yml" to fix this issue. Setting -1 means, unlimited size allowed.
Spring:
servlet:
multipart:
max-file-size: -1
Spring:
servlet:
multipart:
max-request-size: -1

ExtJS how to get exception from spring conrolerAdvice handler

On the server I am handling exceptions globally using #ControllerAdvice
CODE:
#ControllerAdvice
#EnableWebMvc
public class GlobalExeptionHandler
{
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(value = {Exception.class,RuntimeException.class})
public ModelAndView defaultErrorHandler (HttpServletRequest request, Exception e)
{
ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
mav.addObject("datetime", new Date());
mav.addObject("exception", e);
mav.addObject("url", request.getRequestURL());
return mav;
}
#ExceptionHandler(value = {UnableToSaveException.class})
public ModelAndView saveExceptionHandler (HttpServletRequest request, UnableToSaveException e)
{
ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
mav.addObject("errCode", e.getErrCode());
mav.addObject("errMsg", e.getErrMsg());
return mav;
}
}
I know that using ExtJS on client I can catch exception using this code:
Ext.Ajax.on('requestexception', function(conn, response, options, eOpts)
{
...
}
I want to rise dialog with error Message which I want to get from ModelAndView which is returned in spring, but I do not know how to get it. Can someone tell me how, or say if I doing something wrong?

block a direct call to a controller

I'm working on a website using JAVA Spring mvc. I have a functionality that requires two controllers. First of all, the request is handled by controller1 who redirect it to controller2 using a return new ModelAndView ("redirect:controller2.htm"). All is working fine. However,I would like to block the direct access to the controller2 ( block a call from the url "controller2.htm") because the controller2's form needs data from the controller1.I want that the only case in which controller2 is used is the redirection from controller1. I would like a solution without annotations.Thanks in advance for your help.
Here is the code :
Controller1:
public class controller1 extends SimpleFormController implements Serializable {
private PersonManager pManager ;
#Override
public ModelAndView onSubmit(Object command) {
CommandPerson cmd = (CommandPerson) command;
Person p = null;
String viewName = "redirect:controller2.htm";
try {
p = pManager.getPersonbyID(cmd.getID());
} catch (EmptyResultDataAccessException ex) {
viewName="NosuchPerson";
}
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("ID",cmd.getID());
return mav;
}
controller2:
public class controller2 extends SimpleFormController implements Serializable {
private PersonManager pManager ;
#Override
public ModelAndView onSubmit (Object command) throws ServletException, IOException {
Person p = (Person) command;
Map<String,Object> model = new HashMap<String,Object>();
pManager.UpdatePerson(p);
model.put("person", p);
return new ModelAndView("SuccesfulUpdate","model",model);
}
protected Object formBackingObject(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("ID");
if(id==null) {
response.sendRedirect("controller1.htm");
return null;
} else{
Personne p = pManager.getPersonbyID(id);
return p;
}}
If the url "controller2.htm" is called directly the ID parameter will be null,and as the formBackingObject() is the first method executed when the request is being handled I thought I could make a redirection in it , but it didn't work as I'm redirected to the controller2's form being empty.
Your problem is very near to the Cross Site Request Forgery protection, so the same general solution should apply.
You just have to generate a random token in Controller1, put it in model under an arbitrary name (say "_csrf") and also store its value in session. Then in Controller2 you test that :
the parameter request _csrf is present in request
it is equal to the value stored in session
and immediately remove the _csrf value from the session.
If both requirements are met, it is highly probable that Controller2 was called via a redirect from Controller1, as nobody else should be able to guess the value
I finally found the solution. I overrided the showForm method . In this method , I could test if the parameter exists in the request . If it exists the formBackingObject method is called and the controller2's form is displayed else there is a redirection to controller1.
Controller1:
public class controller1 extends SimpleFormController implements Serializable {
private PersonManager pManager ;
#Override
public ModelAndView onSubmit(Object command) {
CommandPerson cmd = (CommandPerson) command;
Person p = null;
String viewName = "redirect:controller2.htm";
try {
p = pManager.getPersonbyID(cmd.getID());
} catch (EmptyResultDataAccessException ex) {
viewName="NosuchPerson";
}
ModelAndView mav = new ModelAndView(viewName);
mav.addObject("ID",cmd.getID());
return mav;
}
Controller2:
public class controller2 extends SimpleFormController implements Serializable {
private PersonManager pManager ;
#Override
public ModelAndView onSubmit (Object command) throws ServletException, IOException {
Person p = (Person) command;
Map<String,Object> model = new HashMap<String,Object>();
pManager.UpdatePerson(p);
model.put("person", p);
return new ModelAndView("SuccesfulUpdate","model",model);
}
protected Object formBackingObject(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("ID");
if(id==null) {
response.sendRedirect("controller1.htm");
return null;
} else{
Personne p = pManager.getPersonbyID(id);
return p;
}}
protected ModelAndView showForm(HttpServletRequest request, HttpServletResponse response, BindException errors)throws Exception {
String id = request.getParameter("ID");
if (id==null) return new ModelAndView("redirect:controller1.htm");
else{
Personn p = (Personne) formBackingObject(request, response);
return new ModelAndView("UpdatePersonForm","Person",p);
}}

How to return 404 response status in Spring Boot #ResponseBody - method return type is Response?

I'm using Spring Boot with #ResponseBody based approach like the following:
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public #ResponseBody Response getData(#PathVariable(ID_PARAMETER) long id, HttpServletResponse res) {
Video video = null;
Response response = null;
video = videos.get(id - 1);
if (video == null) {
// TODO how to return 404 status
}
serveSomeVideo(video, res);
VideoSvcApi client = new RestAdapter.Builder()
.setEndpoint("http://localhost:8080").build().create(VideoSvcApi.class);
response = client.getData(video.getId());
return response;
}
public void serveSomeVideo(Video v, HttpServletResponse response) throws IOException {
if (videoDataMgr == null) {
videoDataMgr = VideoFileManager.get();
}
response.addHeader("Content-Type", v.getContentType());
videoDataMgr.copyVideoData(v, response.getOutputStream());
response.setStatus(200);
response.addHeader("Content-Type", v.getContentType());
}
I tried some typical approaches as:
res.setStatus(HttpStatus.NOT_FOUND.value());
new ResponseEntity(HttpStatus.BAD_REQUEST);
but I need to return Response.
How to return here 404 status code if video is null?
This is very simply done by throwing org.springframework.web.server.ResponseStatusException:
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "entity not found"
);
It's compatible with #ResponseBody and with any return value. Requires Spring 5+
Create a NotFoundException class with an #ResponseStatus(HttpStatus.NOT_FOUND) annotation and throw it from your controller.
#ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "video not found")
public class VideoNotFoundException extends RuntimeException {
}
Your original method can return ResponseEntity (doesn't change your method behavior):
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(#PathVariable(ID_PARAMETER) long id, HttpServletResponse res{
...
}
and return the following:
return new ResponseEntity(HttpStatus.NOT_FOUND);
You can just set responseStatus on res like this:
#RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(#PathVariable(ID_PARAMETER) long id,
HttpServletResponse res) {
...
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
// or res.setStatus(404)
return null; // or build some response entity
...
}

Categories

Resources