I'm trying to send data to jsp but its not working
public class LoginPageController extends SimpleFormController
{
public ModelAndView onSubmit(HttpServletRequest request,
HttpServletResponse response, Object command, BindException errors)
throws ServletException
{
Loginpage lcmd=(Loginpage)command;
System.out.println("This is LOGIN PAGE");
System.out.println(lcmd.getUserName());
System.out.println(lcmd.getPassWord());
request.setAttribute("MSG","Thank u"); //This code not doing anything.
return new ModelAndView(new RedirectView("Login.jlc"));
}
}
You are changing the request object, that will indeed do nothing.
What you want is add a variable to the Model. So here's how you can do it:
Instead of:
request.setAttribute("MSG","Thank u"); //This code not doing anything.
return new ModelAndView(new RedirectView("Login.jlc"));
Try this:
Map<String, Object> model = new HashMap<String, Object>();
model.put("MSG", "Thank u");
return new ModelAndView(new RedirectView("Login.jlc"), model); // <-- notice this
This will enable you to access that "Thank u" value through the expression ${model.MSG} and others, if you add them to the model map.
Related
I have Spring Boot project on backend and Android app on frontend. Communication between the two happens via Retrofti2. On one of the endpoits, actually /login endpoint, is retruning a HashMap<String, Object>. That endpoint looks like this:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<?> login(#RequestBody AuthenticationRequest authenticationRequest) throws Exception {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("user", getUserObject());
map.put("token", getJwtToken());
return ResponseEntity.ok(map);
}
Interface for method call looks like this:
public interface LoginService {
#POST("login")
Call<HashMap<String, Object>> testLogin(#Body Login login); //login contains username and password strings
}
The reposnse in frontend look like this:
Call<HashMap<String, Object>> call = mLoginService.testLogin(new Login(username, password));
call.enqueue(new Callback<HashMap<String, Object>>() {
#Override
public void onResponse(Call<HashMap<String, Object>> call, Response<HashMap<String, Object>> response) {
if (!response.isSuccessful()){
Toast.makeText(getApplicationContext(), "Wrong credentials!", Toast.LENGTH_SHORT).show();
return;
}
//extract user & token
HashMap<String, Object> map = response.body();
Log.i("Test result", map.get("token").toString());
Log.i("Test result", map.get("user").toString());
User u = (User) map.get("user");
//Toast.makeText(getApplicationContext(), "Welcome " + u.getUsername() + "!", Toast.LENGTH_SHORT).show();
startActivity(goToEmailsIntent);
}
#Override
public void onFailure(Call<HashMap<String, Object>> call, Throwable t) {
Toast.makeText(getApplicationContext(), "Cannot login, look at the console", Toast.LENGTH_SHORT).show();
Log.i("ERRROOOOOOR during login", t.toString());
return;
}
});
When I look at the log statements in the console everything is okey, I get the data which I want. But when trying to cast to User object I get exception.
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.email.model.User
How to get around this? Is there any better way to send User object and a String from backend?
First method
When you can change your backend service then modify the backend service return DTO not HashMap.
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<Token> login(#RequestBody AuthenticationRequest authenticationRequest) throws Exception {
// create your response
Token token = new Token();
return ResponseEntity.ok(token);
}
Second method
Use json format to exchange data,in your onResponse method you can do like below:
HashMap<String, Object> map = response.body();
// map.get("user) may return a null value
String jsonString = new Gson().toJson(map.get("user));
User user = new Gson().fromJson(jsonString,User.class);
// do other thing as you want
A Spring MVC controller needs to conditionally either:
1.) forward the user to a very different URI, or
2.) return the preceding view so that the user can re-enter data in the same form
My research indicates that the redirect should be done by giving the controller method a return type of ResponseEntity. How can I convert the HttpHeaders from the HttpServletResponse that was passed into the controller method into the new ResponseEntity that gets created in the controller?
There is no HttpServletResponse.getHeaders() method, and there must be a more elegant method than (in pseudocode):
HttpHeaders responseHeaders = new HttpHeaders();
for(String headerName : HttpServletResponse.getHeaderNames()) {
responseHeaders.add(httpServletResponse.getHeader(headerName));
or
responseHeaders.add(httpServletResponse.getHeaders(headerName));.
}
add responseHeaders to new ResponseEntity;
This is confounded a bit by the additional requirement that the controller should alternatively be able to return the previous FreeMarker view from which the user called the controller, if the user did not provide the correct information in the preceding FreeMarker view's form.
Here is the code I have do far. What specific changes need to be made to it?
#RequestMapping("/handle")
public ResponseEntity<?> handle( HttpServletRequest req, HttpServletResponse resp) {
HttpHeaders responseHeaders = resp.getHeaders();//THIS IS NOT IN THE API
responseHeaders.set("MyResponseHeader", "MyValue");
boolean locationRedirect = true;
// Add some logic to determine whether to 1.) forward the user or 2.) return the previous view so they can re-enter information
if(locationRedirect){
try {
URI location = new URI("some_uri_to_send_user_to_if_they_entered_correct_value_in_form");
responseHeaders.setLocation(location);
} catch (URISyntaxException e) {e.printStackTrace();}
return new ResponseEntity<Void>(responseHeaders, HttpStatus.CREATED);
} else{// send the user back to the freeMarker view that would otherwise be at 'return "viewName";'
return new ResponseEntity<String>("viewName", responseHeaders, HttpStatus.CREATED);
}
}
LoginFormController-->Post is invoked after the form is submitted/posted. At the end, it invokes another Controller called LandingFormController-->loadForm.
Well, in the loadForm the values in the Model seems to be empty. Is there a way I can persist a Bean in session or request and get it in the loadForm method? Bonus points: if you could point to some documents to refer :)
Thanks
#Controller
#RequestMapping(value="/login")
public class LoginFormController {
#RequestMapping(method=RequestMethod.POST)
public ModelAndView post(#ModelAttribute User user, BindingResult result, SessionStatus status) {
logger.info("post");
new ReceiptUserValidator().validate(user, result);
if (result.hasErrors()) {
return new ModelAndView("login");
}
else {
logger.info("Email Id: " + user.getEmailId());
//status.setComplete();
Map<String, Object> model = new HashMap<String, Object>();
model.put("userId", user.getEmailId());
model.put("now", new Date().toString());
return new ModelAndView("redirect:/landing.htm", "model", model);
}
}
Controller B below that gets called
#Controller
#RequestMapping(value="/landing")
public class LandingFormController {
protected final Log logger = LogFactory.getLog(getClass());
#RequestMapping(method=RequestMethod.GET)
public String loadForm(Model model) {
logger.info("LandingFormController loadForm: " + model.asMap().keySet());
return "landing";
}
}
The code is performing a redirect which causes the properties placed in the model to be lost. Use flash attributes to pass the attributes to the next controller.
Flash attributes provide a way for one request to store attributes
intended for use in another. This is most commonly needed when
redirecting — for example, the Post/Redirect/Get pattern. Flash
attributes are saved temporarily before the redirect (typically in the
session) to be made available to the request after the redirect and
removed immediately.
LoginFormController
#Controller
#RequestMapping(value="/login")
public class LoginFormController {
#RequestMapping(method=RequestMethod.POST)
public ModelAndView post(#ModelAttribute User user, BindingResult result,
SessionStatus status, final RedirectAttributes redirectAttrs) {
logger.info("post");
new ReceiptUserValidator().validate(user, result);
if (result.hasErrors()) {
return new ModelAndView("login");
}
else {
logger.info("Email Id: " + user.getEmailId());
//status.setComplete();
redirectAttrs.addFlashAttribute("userId", user.getEmailId());
redirectAttrs.addFlashAttribute("now", new Date().toString());
return new ModelAndView("redirect:/landing.htm", "model", model);
}
}
Documentation
As an alternative solution you could simply not perform a redirect from the controller.
Appending retrieving solution.
Modify the loadForm
public String loadForm(#ModelAttribute("user") User user) {
logger.info("LandingFormController loadForm: " + user.getEmailId());
return "landing";
}
I have a code
#RenderMapping
public ModelAndView model(RenderRequest renderRequest, ModelMap map) {
map.put("form", form);
ModelAndView view = new ModelAndView("view", map);
PortletSession portletSession = renderRequest.getPortletSession(true);
if(portletSession != null) {
MappingJacksonJsonView v = new MappingJacksonJsonView();
view.setView(v);
view.addObject("dataListCustomer", portletSession.getAttribute("listCustomer"));
}
init(renderRequest, view);
return view;
}
I have an error:
java.lang.IllegalArgumentException: application/json is not a supported mime type
at com.liferay.portlet.MimeResponseImpl.setContentType(MimeResponseImpl.java:159)
Error is caused by view.setView(v);
How can I added listCustomer to JSON? In listCustomer I have a ModelMap
I have code:
#ResourceMapping(value="customer")
public ModelAndView customer(
ResourceRequest req,
ResourceResponse res) {
log.debug("List Customer Resource: " + context.getCustomer());
ModelAndView mav = new ModelAndView();
MappingJacksonJsonView v = new MappingJacksonJsonView();
v.setBeanName("ajaxResult");
mav.setView(v);
mav.addObject("customer", context.getCustomer());
return mav;
}
Customer is set in #ActionMapping function and it is OK.
In JSP I have:
<portlet:resourceURL escapeXml="false" id="customer" var="customer"/>
How can I call #ResourceMapping function? Because I don't see result of log.debug("List Customer Resource: " + context.getCustomer()); in logs.
For ajax (with portal) you cannot use RenderMapping (or ActionMapping), you must use ResourceMapping (and if needed ResourceRequest and ResourceResponse as method parameters).
For example, not using spring but change is trivial, see this SO answer.
what should handler return for the model not to be enriched with command object ?
ModelAndView - enriched, Model - enriched, Map - entriched ... everything is enriched with the ImplicitModel. Can I somehow stop the propagation of the implicit model to the ajaxResponse View ?
#ActionMapping(params = "javax.portlet.action=sample")
public void response(ActionRequest request, ActionResponse response, Bean bean) {
response.setRenderParameter("javax.portlet.action", "success");
List<MultipartFile> fileList = request.getFiles("file");
}
.....
#RequestMapping(params = "action=success")
public ModelAndView processSuccess(RenderRequest request, Model model) throws IOException {
Map map = new HashMap();
map.put("sucess", "sucess");
return new ModelAndView("ajaxResponse", map);
}
Then the parameters of the "model" argument (implicitModel) goes on to the next handler, because of this condition in Spring's AnnotationMethodHandlerAdapter.
if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}
The View class goes like this:
#Component("someView")
public class SomeView extends AbstractView {
private Logger logger = Logger.getLogger(SomeView.class);
#Override
protected void renderMergedOutputModel(Map map, HttpServletRequest request, HttpServletResponse response)
throws Exception {
logger.info("Resolving ajax request view - " + map);
JSONObject jsonObj = new JSONObject(map);
logger.info("content Type = " + getContentType());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(jsonObj.toString());
response.getWriter().flush();
}
}
It happens even if I remove the "Model model" attribute from processSuccess handler. Simply the implicitModel parameters are propagated into the ajaxResponse view, instead of just a new Map with the parameter I added there
How to stop this propagation ?
It relates to this question, in spring-portlet-mvc this is sometimes needed when request is forwarded to a handler based on some condition and hand it over some parameters that are to be rendered in View, but not the original CommandObject, which has been already processed.
CREATED A JIRA ISSUE - SPR-8267, PLEASE VOTE UP IF YOU HAVE THE SAME PROBLEM.
The answer is : clear ModelMap to prevent it from being stored as an ImplicitModel.
#RequestMapping
public String render(ModelMap modelMap, SessionStatus status, RenderRequest request, RenderResponse response) {
modelMap.clear();
...
}
Notice that if you are using #ModelAttribute at method level, modelMap gets populated with it after you dispatch to different handler within the same controller.
After a successful action method call, you may want to manually clear the model to prevent the action model data from being stored in the ImplicitModel.
First of all, the difference between spring-mvc and spring-portlet-mvc as to handling POST requests is that spring-mvc POST handler dispatches to VIEW directly whereas in spring-portlet-mvc the action phase (POST request handler) is always followed by render phase which is handled by another handler and the entire Model remains in the request (mainly command object and BindingResult) ... Post/Redirect/Get
Anyway after the request is dispatched to a VIEW, there is always a chance to filter the model in there... By declaring which parameters you want only or do not want in the model anymore...