I'm running into a situation where my model attribute is losing values between pages.
I've got two controller methods that handle GET and POST requests respectively.
GET Method
#RequestMapping(value = "/checkout/billing", method = RequestMethod.GET)
public String getBillingPage(Model model, final HttpServletRequest request) throws CMSItemNotFoundException {
// other code
checkoutForm.setCustomFieldsForm(customFieldsForm);
model.addAttribute("checkoutForm", checkoutForm);
// other code
}
Debug View After GET Method completes
POST Method
#RequestMapping(value = "/checkout/billing", method = RequestMethod.POST)
public String submitPayment(
#Valid #ModelAttribute("checkoutForm") final CheckoutForm checkoutForm,
final BindingResult bindingResult,
Model model,
final HttpServletRequest request,
final HttpServletResponse response) throws CMSItemNotFoundException
{}
Debug View When POST Method is Invoked
The 1234 comes from the user entering that data in the form field. The other values should still be there and not null though.
What could be happening here?
Your model is not stored in the session. Each request creates a new model object. That's why it's empty.
You can add your model as a session attribute. Please find the documentation here https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-ann-sessionattrib
Related
I have a resource that supports both GET and POST requests. Here a sample code for a sample resource:
#RequestMapping(value = "/books", method = RequestMethod.GET)
public ModelAndView listBooks(#ModelAttribute("booksFilter") BooksFilter filter, two #RequestParam parameters, HttpServletRequest request)
throws ParseException {
LONG CODE
}
#RequestMapping(value = "/books", method = RequestMethod.POST)
public ModelAndView listBooksPOST(#ModelAttribute("booksFilter") BooksFilter filter, BindingResult result)
throws ParseException {
SAME LONG CODE with a minor difference
}
The code in the two methods is practically the same, except for lets say a variable definition. The two methods can be easily combined using method = {RequestMethod.POST, RequestMethod.GET}, and a simple if inside. I tried, but it doesn't work, because the two methods have a different parameter at the end, i.e. HttpServletRequest and BindingResult (the #RequestParam's are not required and therefore not needed in the POST request). Any ideas how to combine the two methods?
#RequestMapping(value = "/testonly", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView listBooksPOST(#ModelAttribute("booksFilter") BooksFilter filter,
#RequestParam(required = false) String parameter1,
#RequestParam(required = false) String parameter2,
BindingResult result, HttpServletRequest request)
throws ParseException {
LONG CODE and SAME LONG CODE with a minor difference
}
if #RequestParam(required = true) then you must pass parameter1,parameter2
Use BindingResult and request them based on your conditions.
The Other way
#RequestMapping(value = "/books", method = RequestMethod.GET)
public ModelAndView listBooks(#ModelAttribute("booksFilter") BooksFilter filter,
two #RequestParam parameters, HttpServletRequest request) throws ParseException {
myMethod();
}
#RequestMapping(value = "/books", method = RequestMethod.POST)
public ModelAndView listBooksPOST(#ModelAttribute("booksFilter") BooksFilter filter,
BindingResult result) throws ParseException {
myMethod();
do here your minor difference
}
private returntype myMethod(){
LONG CODE
}
Below is one of the way by which you can achieve that, may not be an ideal way to do.
Have one method accepting both types of request, then check what type of request you received, is it of type "GET" or "POST", once you come to know that, do respective actions and the call one method which does common task for both request Methods ie GET and POST.
#RequestMapping(value = "/books")
public ModelAndView listBooks(HttpServletRequest request){
//handle both get and post request here
// first check request type and do respective actions needed for get and post.
if(GET REQUEST){
//WORK RELATED TO GET
}else if(POST REQUEST){
//WORK RELATED TO POST
}
commonMethod(param1, param2....);
}
#RequestMapping(value = "/books", method = { RequestMethod.GET,
RequestMethod.POST })
public ModelAndView listBooks(#ModelAttribute("booksFilter") BooksFilter filter,
HttpServletRequest request)
throws ParseException {
//your code
}
This will works for both GET and POST.
For GET if your pojo(BooksFilter) have to contain the attribute which you're using in request parameter
like below
public class BooksFilter{
private String parameter1;
private String parameter2;
//getters and setters
URl should be like below
/books?parameter1=blah
Like this way u can use it for both GET and POST
Using Spring Boot, I have a couple methods in my RegisterController which handles new user registration.
The createNewUser method is responsible for saving the new user to the database and sending a confirmation e-mail containing a link that has a unique token.
The confirmUser method handles processing the GET request for the confirmation link.
Is there a way for the createNewUser method to get the #RequestMapping value assigned to confirmUser? I'd like to use this value to generate the confirmation link instead of hard coding it.
// Process form input data
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ModelAndView createNewUser(#Valid User user, BindingResult bindingResult) {
}
// Process confirmation link
// Link in confirmation e-mail will be /registerConfirmation?token=UUID
#RequestMapping(value="/registerConfirmation", method = RequestMethod.GET)
public ModelAndView confirmUser( #RequestParam("token") String token) {
}
I don't know of a way to get it from the #RequestMapping value but you have a couple of different options.
Option 1: Create a constant for the mapping and use that which allows you to reference it in both methods.
private final static String REGISTER_CONF_VAL = "/registerConfirmation";
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ModelAndView createNewUser(#Valid User user, BindingResult bindingResult) {
}
// Process confirmation link
// Link in confirmation e-mail will be /registerConfirmation?token=UUID
#RequestMapping(value=REGISTER_CONF_VAL, method = RequestMethod.GET)
public ModelAndView confirmUser( #RequestParam("token") String token) {
}
Option 2: Less ideal, but if you add registerConfirmation to your config file, you can access it like:
#RequestMapping(value="${register.conf.val}", method = RequestMethod.GET)
public ModelAndView confirmUser( #RequestParam("token") String token) {
}
The reason this isn't ideal is because you probably don't want it to be different from environment to environment. That said, it would work.
If you need to generate the link based on user request, you can use the Path Variable in the controller. U can get the path variable and use some mechanism to validate the path as well.
Replace registerConfirmation with {registerConfirmation} and in the method, use #PathVariable annotation to get the path. Use the variable to check if the path is valid.
#RequestMapping(value = "/connect{accountDeviceId}", method = RequestMethod.GET)
public #ResponseBody
String showCompleteAuthorizat(HttpServletRequest request,
HttpServletResponse response,
#RequestParam("accountDeviceId") int accountDeviceId) throws Exception {
I'm getting this error
org.springframework.web.bind.MissingServletRequestParameterException: Required int parameter 'accountDeviceId' is not present
and this is the url that calls the above api
http://localhost:8080/gch-restful/fitbit/connect29?oauth_token=b1e939450e745664ce4bbbc194b4ed47f&oauth_verifier=9dc1045654dc775d2347ae2963d5ae878c
I'm new to spring and basically learning it, please tell me what am I doing wrong here.
Regards
You should use #PathVariable(value="accountDeviceId") instead of #RequestParam("accountDeviceId") in order to get a variable from url.
I have the following property that I need mapped to a post parameter in Spring. Is there an attribute I can use? It accepts application/x-www-form-urlencoded for string-based payloads, multipart/form-data for binary payloads. Other properties are mapping fine without underscores.
String deliveryAttemptId;
mapped to the post parameter
DELIVERY-ATTEMPT-ID
Controller
#Controller
#RequestMapping("/notifications")
public class NotificationController {
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public void grade(EventNotificationRequest request, HttpServletResponse response) throws Exception {
}
Model
public class EventNotificationRequest {
String deliveryAttemptId;
I just made a work around for this Spring limitation. This also fixes case sensitivity issues with parameters. Sorry I am used to .NET and how easy binding is so it's frustrating to run into these Spring issues.
HttpServletRequest parameter lowecase
#RequestMapping(method = RequestMethod.POST, value = "/grade")
#ResponseBody
public void grade(HttpServletRequest request, HttpServletResponse response) throws Exception {
EventNotificationRequest notificationRequest = new LearningStudioEventNotificationRequest();
notificationRequest.setDeliveryAttemptId(getCaseInsensitiveParameter(request, "DELIVERY-ATTEMPT-ID"));
I have defined a #ModelAttribute("mymodel")
#ModelAttribute("mymodel")
MyModel mymodel() {
MyModel mymodel = new MyModel();
return mymodel;
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public final void save(#ModelAttribute("mymodel") MyModel mymodel,
final BindingResult binding,
final HttpServletRequest request,
final ModelMap modelMap) throws Exception {
modelService.save(mymodel);
// try to reset the model --> doesn't work!!!
myModel = new MyModel();
}
The problem is, even though I reset the model in the save method, if I reload the page after a save operation and save a second time, the model contains all of the values of the previous myModel.
How do I reset it after it has been processed?
Unless I miss my guess, this is because
myModel = new MyModel();
is only going to reset the reference within the method, in the same way that getting a MyModel from a List<MyModel> and then calling myModel = new MyModel(); would not change the element in the List, only your local reference.
You most likely need to put the new MyModel() into the model or modelMap.
The redirect after post pattern may also be useful to you here. Have your POST method
return "redirect:originalpage.htm"
This should reload the original page fresh, and will also mean that if you hit refresh you won't resubmit the POST, saving your object twice.
I wouldn't do it like this.
Try setting your model in the get request like :
#RequestMapping(value = "/save", method = RequestMethod.GET)
public ModelAndView getSaveForm(ModelMap model) {
model.addAttribute("mymodel", new MyModel());
return new ModelAndView("newView", model);
}
I was running into a similar same issue while messing with the BookCatalog from Portlets in Action. My solution was to manually reset it using Model.addAttribute(). For example:
#RequestMapping(value = "/save", method = RequestMethod.POST)
public final void save(#ModelAttribute("mymodel") MyModel mymodel,
final BindingResult binding,
Model model
final HttpServletRequest request,
final ModelMap modelMap) throws Exception
{
modelService.save(mymodel);
model.addAttrubute("mymodel", new MyModel());
}
Below sample method works when we want to add the response header once the controller has finished its processing. #ModelAttribute("mymodel") is invoked before controller method processing so we can not put any values based on processing results.
//Add a parameter "HttpServletResponse"in the request method
#RequestMapping(method = RequestMethod.POST, value = APIConstants.Request_URL, headers = APIConstants.Request_HEADER, produces = APIConstants.PRODUCES)
public #ResponseBody PojoRS autheticateByPassword(#RequestBody() PojoRQ apiRequest, HttpServletResponse response)
throws Exception {
try
{
// All code related to processing
}
catch(Exception e)
{
throw e;
}
//Just before sending back the response add below line
response.setHeader("Response_Key","Response_Header_value");
return PojoRS;
}