简体   繁体   中英

How to pass an object from one controller to another controller

I want to pass the object (that is placed in the model of a function in the first controller) to be received by a function of the second controller and place the received object in the function's model.

I understand that HTTP is stateless but is there a way to pass an object from one controller to another without using sessions in Spring MVC? Thanks.

Please see the sample code that I have given below.

FirstController.java

@RequestMapping(value="search-user",method=RequestMethod.POST)
public ModelAndView searchUser (HttpServletRequest request) {
    //Retrieve the search query by request.getParameter
    String searchQuery = request.getParameter("searchQuery");

    //Search for the user (this is the object that I want to pass)
    User user = userDao.searchUser(searchQuery);

    ModelAndView mav = new ModelAndView(new RedirectView("display-searched-user"));
    mav.addObject("user",user);

    return mav;
}

SecondController.java

@RequestMapping(value="display-searched-user",method={RequestMethod.GET,RequestMethod.POST})
public ModelAndView displayResultUser (HttpServletRequest request) {
    ModelAndView mav = new ModelAndView();
    mav.setViewName("result");

    //I want to receive the object from the FirstController and set that object in this function's model.

    return mav;
}

You will need to to send 2 calls from the client and send "User" to the second controller (which you should modify to accept a user). So the first call to "/search-user" returns an object which includes a user. The client extracts the user and sends it to "/display-searched-user".

A different approach could be, that the request in the second controller also accepts the parameter "searchQuery". In this case just modify your second controller to look like this:

@RequestMapping(value="display-searched-user",method={RequestMethod.GET,RequestMethod.POST})
public ModelAndView displayResultUser (HttpServletRequest request) {
    ModelAndView mav = new ModelAndView();
    mav.setViewName("result");

    FirstController fc = new FirstController();
    return fc.searchUser(request);
}

EDIT:

I just read CrazySabbath's proposal to create a transportation class. Assuming both controllers have access to it, I'd implement the transportation class like this:

public class UserTransporter {

    private static boolean userAvailable = false;
    private static User user;

    public static boolean isUserAvailable() {
        return userAvailable;
    }

    public static void setUser(User user) {
        UserTransporter.user = user;
        userAvailable = true;
    }

    public static User getUser() {
        userAvailable = false;
        return user;
    }
}

Just to be clear: I added the boolean variable, because I wanted to make it impossible to get null or to get a user, which was already obtained by a call before. If you do not wish to check for that, just delete the boolean and any lines in which I used it.

The first controller would need to be changed to this:

@RequestMapping(value="search-user",method=RequestMethod.POST)
public ModelAndView searchUser (HttpServletRequest request) {
    //Retrieve the search query by request.getParameter
    String searchQuery = request.getParameter("searchQuery");

    //Search for the user (this is the object that I want to pass)
    User user = userDao.searchUser(searchQuery);

    ModelAndView mav = new ModelAndView(new RedirectView("display-searched-user"));
    mav.addObject("user",user);

    UserTransporter.setUser(user);

    return mav;
}

The second controller would need to be changed to this:

@RequestMapping(value="display-searched-user",method={RequestMethod.GET,RequestMethod.POST})
public ModelAndView displayResultUser (HttpServletRequest request) {
    ModelAndView mav = new ModelAndView();
    mav.setViewName("result");

    User user;
    if(UserTransporter.isUserAvailable()) user = UserTransporter.getUser();
    else return "ERROR, no user available to display";

    //do something with the obtained user object

    return mav;
}

You can achieve this with RedirectAttributes and ModelAttribute .

Normally I'd say just use flash attributes but since those are stored in the session and you wanted to do this without a session you'll have to do this with regular attributes.

Actually , after reading your code again I think what you really want to do is to use a session and flash attributes. Not using one is less secure (trusting the client to carry the user object) and/or error-prone.

Redirect attributes work by adding the redirect attributes as parameters on the redirect URL and to send anything more complex than simple strings, ints, doubles, etc. we need to serialize it first. Here I do this by turning the object into JSON and then Base64-encoding it.

Here's a complete, working example:

@Controller
@SpringBootApplication
public class RedirectController {

    @Autowired
    private ObjectMapper objectMapper;

    // Bean for converting from TestThing to base64 encoded string
    @Bean
    public Converter<TestThing, String> testThingToStringConverter() {
        return new Converter<TestThing, String>() {
            public String convert(TestThing thing) {
                try {
                    return Base64.getUrlEncoder().encodeToString(
                            objectMapper.writeValueAsString(thing)
                                    .getBytes(StandardCharsets.UTF_8));
                } catch (IOException e){
                    throw new RuntimeException(e);
                }
            }
        };
    }

    // Bean for converting from base64 encoded string to TestThing
    @Bean
    public Converter<String, TestThing> stringToTestThingConverter() {
        return new Converter<String, TestThing>() {
            public TestThing convert(String thing) {
                try {
                    return objectMapper.readValue(Base64.getUrlDecoder().decode(thing), TestThing.class);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
    }

    public static class TestThing implements Serializable {

        private String firstString;
        private String secondString;

        public String getFirstString() {
            return firstString;
        }

        public void setFirstString(String firstString) {
            this.firstString = firstString;
        }

        public String getSecondString() {
            return secondString;
        }

        public void setSecondString(String secondString) {
            this.secondString = secondString;
        }
    }


    @GetMapping("/test")
    public String testValidation(@RequestParam String firstString,
                                 @RequestParam String secondString,
                                 RedirectAttributes redirectAttributes) {
        TestThing redirectObject = new TestThing();
        redirectObject.firstString = firstString;
        redirectObject.secondString = secondString;

        redirectAttributes.addAttribute("redirectObject", redirectObject);

        return "redirect:/redirected";
    }

    @ResponseBody
    @GetMapping("/redirected")
    public TestThing redirected(@ModelAttribute("redirectObject") TestThing thing) {
        return thing;
    }
    public static void main(String[] args) {
        SpringApplication.run(RedirectController.class, args);
    }
}

If we talk to our controller with Curl we can see that it works:

# -L follows redirects
$ curl -L "localhost:8080/test?firstString=first&secondString=second
{"firstString":"first","secondString":"second"}% 

# Now let's do it manually
curl -v "localhost:8080/test?firstString=first&secondString=second"
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /test?firstString=first&secondString=second HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.57.0
> Accept: */*
> 
< HTTP/1.1 302 
< Location: http://localhost:8080/redirected?redirectObject=eyJmaXJzdFN0cmluZyI6ImZpcnN0Iiwic2Vjb25kU3RyaW5nIjoic2Vjb25kIn0%3D
< Content-Language: en-GB
< Content-Length: 0
< Date: Thu, 07 Dec 2017 15:38:02 GMT
< 
* Connection #0 to host localhost left intact

$ curl http://localhost:8080/redirected\?redirectObject\=eyJmaXJzdFN0cmluZyI6ImZpcnN0Iiwic2Vjb25kU3RyaW5nIjoic2Vjb25kIn0%3D
{"firstString":"first","secondString":"second"}

If you use flash attributes instead, the example is the same but you don't need the two Converter s and you use addFlashAttribute instead of addAttribute .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM