RedirectAttributes in Spring MVC 3.2.8 - spring

I have a project based in Spring Web model-view-controller (MVC) framework. The version of the Spring Web model-view-controller (MVC) framework is 3.2.8.
I have this method
#RequestMapping(value = { "/newdesign/manage/device/award",
"/newdesign/manage/device/award/"}, method = {RequestMethod.POST})
public String awardDeviceProduct(
#ModelAttribute("deviceForm") DeviceForm deviceForm,
HttpServletRequest request,
Model model,
final RedirectAttributes redirectAttributes) throws Exception {
checkUser (request, UserRole.MARKETING);
Device device = manageLicenseService.getDeviceById(deviceForm.getDevice().getId());
if (deviceForm.getDevice().getIos()==null) {
model.addAttribute ("errorMessage", "Licence Number cannot be null !");
redirectAttributes.addFlashAttribute("errorMessage", "Licence Number cannot be null !");
} else if (deviceForm.getSelectedItems()!=null &&
!deviceForm.getSelectedItems().isEmpty()) {
// check LICENCE DUPLICATED
manageLicenseService.applyStatusChange (device, deviceForm.getSelectedItems(), Status.AWARDED );
} else {
model.addAttribute ("errorMessage", "no Items selected !");
model.addAttribute ("productGroup", getNotExpiredProductGroups (request));
}
return "redirect:/newdesign/manage/device/" + deviceForm.getDevice().getId();
}
But in the JSP I can't find the attribute "errorMessage" !!!! when (deviceForm.getDevice().getIos()==null)

The problem with your code relies in the difference between "redirect" and "forward".
If you return with a redirect statement, the response will first return to the browser, and then it will request the new url. The problem with this approach is, that the redirected new request will have a completely new context, and will not have access to the Model, set in your previous response.
The forward response however is processed by the server side itself, transferring the request to the new URL. It is faster and the context can be maintained.
You can find more details here

Related

Spring Controller does nothing

Greeting my dear fellows
I would appreciate some help since I've been 2 days googling to find out why my code is not working. My webapp is Spring running in a Weblogic Server under Eclipse. Btw, apologies for my spelling (I am not native English speaker)
Straight from my webapp, the following controller works flawless
#RequestMapping(value = "/sendFile")
public ModelAndView vistaEnvioFicheros() throws myCustomException {
ModelAndView model = null;
try {
getLog().debug("Setting model for sending a file");
model = new ModelAndView("/content/sendFileWeb");
} catch (Exception ex) {
getLog().error("Shxx happens: ", ex);
throw new myCustomException(ex.getMessage(), ex);
}
return model;
}
This controller loads a jsp file with a file browser button and a file upload button which works great too.
So when I click on the upload button the following controller triggers:
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
public Object subirFichero(#RequestParam(value = "file") MultipartFile file) throws myCustomException {
ModelAndView model = null;
if (file.isEmpty()){
try {
getLog().debug("File is empty");
model = new ModelAndView("/content/errorWeb");
} catch (Exception ex) {
getLog().error("Shxx happens again ", ex);
throw new myCustomException(ex.getMessage(), ex);
}
return model;
}
...
}
Problem is: when I upload empty file, errorWeb jsp file should be shown in my web browser, however nothing happens. If I debbug the controlles I can see the code runs propperly until return model sentence nonetheless errorWeb jsp file is not loaded
Could anyone give me a hint about why first controller loads it jsp view but second controller doesn't. Also I don't get a single error message in any console or whatever
Thank you very much
Ok, I got it solved! This was just a newbie thing (I am a newbie web developper).
The problem was in the client side, where my webapp was sending the request thay triggers the second controller, not by an standard href o after a submit (or whatever), but by a custom javascript function.
This custom javascript function firstly validates the file and then uploads it to the server. After that client side code stops listening the response from the server. That's why the new ModelAndView is ignored.
Thank you for your time to all of you who read the post

How to redirect from spring ajax controller?

I have a controller with #ResponseBody annotation. What I want to do is if this user doesn't exists process user's Id and return a json object. If exists redirect to user page with userInfo. Below code gives ajax error. Is there any way to redirect to user page with userInfo?
#RequestMapping(value = "/user/userInfo", method = {RequestMethod.GET})
#ResponseBody
public String getUserInfo(HttpServletRequest request, HttpServletResponse response, ModelMap modelMap) {
if(...){
.....//ajax return
}else{
modelMap.addAttribute("userInfo", userInfoFromDB);
return "user/user.jsp";
}
}
Well, this method is annotated with #ResponseBody. That means that the String return value will be the body of the response. So here you are just returning "user/user.jsp" to caller.
As you have full access to the response, you can always explicitely do a redirect with response.sendRedirect(...);. It is even possible to explicitely ask Spring to pass userInfoFromDB as a RedirectAttribute through the flash. You can see more details on that in this other answer from me (this latter is for an interceptor, but can be used the same from a controller). You would have to return null to tell spring that the controller code have fully processed the response. Here it will be:
...
}else{
Map<String, Object> flash = RequestContextUtils.getOutputFlashMap(request);
flash.put("userInfo", userInfoFromDB);
response.redirect(request.getContextPath() + "/user/user.jsp");
return null;
}
...
The problem is that the client side expects a string response that will not arrive and must be prepared to that. If it is not, you will get an error client side. The alternative would then be not to redirect but pass a special string containing the next URL:
...
}else{
Map<String, Object> flash = RequestContextUtils.getOutputFlashMap(request);
flash.put("userInfo", userInfoFromDB); // prepare the redirect attribute
return "SPECIAL_STRING_FOR_REDIRECT:" + request.getContextPath() + "/user/user.jsp");
}
and let the javascript client code to process that response and ask for the next page.

calling a spring controller from xhrPost doesnot redirect

I am calling a spring controller method from dojo xhrPost and I need to redirect to a new html from the controller method. But controller returns back to the dojo in javascript instead of moving to a new page.
my javascript:
var xhrArgs={
url:"/tradeIn/submitTradeIn",
postData:dojo.toJson(tradeInDetails),
handleAs:"text",
headers:{"Content-Type":"application/json"}
/*load:function(data){
console.log(data);
label=data;
if(data =="fail"){
location.reload(true);
window.location="/Fail";
}
else{
window.location="/success";
}
}*/
}
var deferred=dojo.xhrPost(xhrArgs);
my spring controller:
#RequestMapping(value="/tradeIn/submitTradeIn", method = {RequestMethod.POST})
public String submitTradeIn(#RequestBody TradeInDetails tradeDetails) throws UnsupportedEncodingException{
List<byte[]> labelList=new ArrayList<byte[]>();
ShippingInfo shippingInfo=new ShippingInfo();
shippingInfo.setAddress1(tradeDetails.getCustomerDetails().get(0).getAddress1());
shippingInfo.setAddress2(tradeDetails.getCustomerDetails().get(0).getAddress1());
shippingInfo.setCity(tradeDetails.getCustomerDetails().get(0).getCity());
shippingInfo.setCompany(tradeDetails.getCustomerDetails().get(0).getCompany());
shippingInfo.setDayPhone(tradeDetails.getCustomerDetails().get(0).getDayPhone());
shippingInfo.setEmail(tradeDetails.getCustomerDetails().get(0).getEmail());
shippingInfo.setEvePhone(tradeDetails.getCustomerDetails().get(0).getEvePhone());
shippingInfo.setFirstName(tradeDetails.getCustomerDetails().get(0).getFirstName());
shippingInfo.setLastName(tradeDetails.getCustomerDetails().get(0).getLastName());
shippingInfo.setState(tradeDetails.getCustomerDetails().get(0).getState());
shippingInfo.setZip(tradeDetails.getCustomerDetails().get(0).getZip());
shippingInfo.setCountry(tradeDetails.getCustomerDetails().get(0).getCountry());
List<ReturnRequestLabel> label;
List<TradeInClubs> tradeInList1= new ArrayList<TradeInClubs>();
for(ClubDetails cl: tradeDetails.getClubDetails()){
TradeInClubs tradeInclubs1=new TradeInClubs();
tradeInclubs1.setClubMaterial(cl.getShaftType());
tradeInclubs1.setClubType(cl.getClubType());
tradeInclubs1.setManufacturer(cl.getClubManufacturer());
tradeInclubs1.setModel(cl.getClubModel());
tradeInclubs1.setTradeInValue(cl.getTradeInPrice());
tradeInList1.add(tradeInclubs1);
}
try{
ReturnFedexLabel returnFedexLabel = new ReturnFedexLabel();
label=returnFedexLabel.fetchFedexLabel(tradeInList1, shippingInfo);
byte[] labelImageData;
String fedexLabelNumber=null;
for(ReturnRequestLabel rl: label){
labelImageData=rl.fedexReturnLabel.imageData;
labelList.add(labelImageData);
fedexLabelNumber=rl.trackingNumber;
}
File f=new File("label.jpg");
BufferedImage img=ImageIO.read(new ByteArrayInputStream(labelList.get(0)));
ImageIO.write(img,"JPEG",f);
int id=tradeInDao.insertQuery(shippingInfo,tradeInList1,fedexLabelNumber);
byte[] pdfData=fedexLabelToPdf.printFedexLabel(labelList);
emailTradeIn.emailTradeInDetails(pdfData,tradeDetails.getCustomerDetails().get(0).getEmail(),tradeInList1,id);
System.out.println("here");
} catch (Exception e){
logger.error(e.getMessage());
return "fail";
}
return "success";//Base64.encodeBase64String(labelList.get(0));
}
it is not moving to success page. it just stays in the current page
Check and see what deferred is, it is probably "success".
From http://dojotoolkit.org/reference-guide/1.7/quickstart/ajax.html:
dojo.xhrPost xhrPost will create an Ajax request using the HTTP POST
method and is usually used to submit data to a service. It returns
data to a callback. The callback is defined as a member of the object
used to create the request (the property-bag), or by using the
dojo.Deferred.then() method.
For complete details and examples, see the dojo.xhrPost documentation.
if you need to change the page from the controller it shouldn't be an ajax request. If you can change it from the client side then you can continue with the ajax request and respond accordingly.

Design issue in Spring app: in which layer (web, service or repository) should I retrieve the currently logged in user?

I work on a CRUD Spring app. Let me explain a basic use case:
A user can save an Advertisement. As of now I retrieve the currently logged in member in the web/controller layer and then pass it on to the service layer so that it can be set on the advertisement (the currently logged in user is the owner of the Advertisement; it is retrieve using Spring Security and my custom annotation: #CurrentMember).
In controller layer:
#RequestMapping(value = "/family/new", method = RequestMethod.POST, produces = "text/html")
public String newFamilyAdvertisement(
#ModelAttribute("advertisementInfo") #Validated(value = ValidationGroups.AdvertisementCreation.class) FamilyAdvertisementInfo familyAdvertisementInfo,
BindingResult bindingResult, Model model, #CurrentMember Member member) {
if (bindingResult.hasErrors()) {
populateFamilyAdvertisementModel(model, familyAdvertisementInfo, member);
return "advertisement/family/new";
}
advertisementService.createAdvertisement(member, familyAdvertisementInfo.getAdvertisement(), familyAdvertisementInfo.getAddressReference());
return "redirect:/advertisement/family/new";
}
In service layer:
#Override
public void createAdvertisement(Member member, Advertisement advertisement, String addressReference) {
if (member == null || advertisement == null || addressReference == null || addressReference.isEmpty()) {
throw new IllegalArgumentException("One argument is null or empty");
}
Address address = geolocationService.retrieveAddressFromReference(addressReference);
advertisement.setAddress(address);
advertisement.setMember(member);//SET CURRENTLY LOGGED IN USER
advertisement.setValidated(Boolean.FALSE);
advertisement.setActive(Boolean.TRUE);
advertisement.setCreationDate(utils.now());
saveAdvertisement(advertisement);
}
Still in service layer(Roo ITD):
public void AdvertisementServiceImpl.saveAdvertisement(Advertisement advertisement) {
advertisementRepository.save(advertisement);
}
Now the interrogation I have is:
Should I retrieve the current user/member as early as possible (here in the web layer) and then pass it on until it is needed (here in the service layer)? OR
Should I retrieve the current user/member only when I need it (here in the service layer)?
Thats a matter of design and choices you need to made, usually you dont need to bother controller to pass member to service. It doesnt need any knowledge about user. You can easily load it in service so you api is shorter/cleaner.
But, in a case your api is used from some external project - then api should show what objects are needed to make it work.
To sum up, in your case I would load it in service.

how to prevent confirm alert In Spring MVC on F5

In Spring I usually did a redirect-after-submit(get method mapping) to prevent the user of submitting a form ,
but when i pressing F5 it will go to get method mapping again and display me this kind of confirm message. how could i prevent this message every time on F5.
Here is the code for controller -
ScheduleDetail objScheduleDetail = new ScheduleDetail();
HttpSession session = request.getSession(true);
String condition = "";
try{
int careProfessionalIDF = (Integer) session.getAttribute("careProfessionalIDF");
condition = "CareProfessionalIDF = "+careProfessionalIDF;
objScheduleDetail.setCareProfessionalIDF(careProfessionalIDF);
}catch (Exception e) {
int careProviderIDF = (Integer) session.getAttribute("careProviderIDF");
condition = "CareProviderIDF = "+careProviderIDF;
objScheduleDetail.setCareProviderIDF(careProviderIDF);
}
List<ScheduleDetail> ScheduleDetailList = objScheduleDetailManager.getAllScheduleDetail(condition+" ORDER BY ScheduleDetailIDP DESC");
model.addObject("List_of_ScheduleDetail",ScheduleDetailList);
model.addAttribute("ScheduleDetail", objScheduleDetail);
return "hospital/scheduleDetail";//jsp page
edited code
#RequestMapping("/editAddressType.html")
public String editAddressType(ModelMap model,HttpServletRequest request)
{
int addressTypeIDP = Integer.parseInt(request.getParameter("AddressTypeIDP"));
AddressType objAddressType = new AddressType();
objAddressType = objAddressTypeManager.getByID(addressTypeIDP);
model.addAttribute("AddressType", objAddressType);
return "jsp/addressType";
it open addressType.jsp with data tht we bind with `model.addAttribute`. now if i press F5 it show alert message as above image.
**get method**
#RequestMapping(value="/getAddressType.html", method=RequestMethod.GET)
public String getAddressType(ModelMap model, HttpServletRequest request) throws RemoteException
{
AddressType objAddressType = new AddressType();
model.addAttribute("AddressType", objAddressType);
return "hospital/addressType";
}
If you implement the POST - REDIRECT - GET pattern correctly, you will not see the warning from the browser that you mentioned. The warning is shown when the Page being viewed is in response to a POST request. The traditional pattern, FORM - POST - SUCCESS page.
The code you provided in the question is not enough to reach the root cause of the problem. I'm listing key points of the implementation here, please compare with your code and you'll understand what the mistake is.
To show the user the form, where they are supposed to enter data for submission. (Just the starting point, could be any page in your application.)
#RequestMapping(value = "/checkout/{cartId}.htm", method = RequestMethod.GET)
public ModelAndView showCheckoutForm(...) {
......
return new ModelAndView("/WEB-INF/jsp/form.jsp")
}
The form POSTs to a handler method, which issues a redirect to the user, pointing to a URL that will show the details of the resource created as a result of the POST.
#RequestMapping(value = "/checkout/{cartId}.htm", method = RequestMethod.POST)
public View submitCheckoutForm(....) {
return new RedirectView("/checkout/confirmation/" + cartId + ".htm");
}
The details of the created resource will be shown by a handler method like the following. Note that at this point, if your implementation worked properly, the URL in the user's browser will change to the path redirected by the POST handler. And a fresh GET request will be issued to the server.
#RequestMapping(value = "/checkout/confirmation/{cartId}.htm", method = RequestMethod.GET)
public ModelAndView showCheckoutConfirmation(....) {
return new ModelAndView("viewThatShowsCheckoutConfirmation");
}
If your implementation is similar, the confirmation page is a result of a GET request, so browsers won't issue a warning for re-POSTing data if you refresh the page.
My suspicion is that your POST handler is not issuing a redirect to the user, it is instead rendering the confirmation page itself.
Hope this helps. Please let me know if this does not solve your problem.

Resources