2017年12月26日 星期二

Java Spring - Carrying data across redirect requests

  • redirect
    • 處理完POST後,通常都會執redirect。這樣能夠防止點擊refresh或back時,client重新執行危險的POST
    • 通常使用redirect: prefix能夠重新定位
    • return "redirect:/spitter/" + spitter.getUsername();
      
    • 問: 如何發送data給redirect的目標method?
    • 答: 當一個controller method完成之後,這個method所指定的model data將會複製到request中,並作為request的property,request會forward到view上。因為controller method和view處理的是同一個request,所以在forward過程中,request property也能夠保存。但當controller是redirect時,原始的request就消失了,並且會發起一個新的GET request。原始request中所帶有的model data也跟著request消失了,在新的request上,沒有任何的model data,這個request必須要自己計算data
    • 有一些方法能夠使用redirect但又能夠將data傳遞給redirect的method中
      • 使用URL template 以path variable或或query parameter的形式
      • 透過flash attribute發送data
  • 透過URL template進行redirect
    • 使用String做連結,但有點危險
      • return "redirect:/spitter/{username}";
        
    • Spring還提供model的方式來定義redirect URL
      • @RequestMapping(value = "/register", method = POST)
        public String processRegistration(
         Spitter spitter, Model model) {
         spitterRepository.save(spitter);
         model.addAttribute("username", spitter.getUsername());
         return "redirect:/spitter/{username}";
        }
        
    • 除此之外,model中任何primitive values都可以加到URL中作為query parameters
    • 下例表示,假設還需要使用到id
    • @RequestMapping(value = "/register", method = POST)
      public String processRegistration(Spitter spitter, Model model) {
       spitterRepository.save(spitter);
       model.addAttribute("username", spitter.getUsername());
       model.addAttribute("spitterId", spitter.getId());
       return "redirect:/spitter/{username}";
      }
      
    • return的String沒有太大變化,但是因為model中的spitterId attribute沒有redirect URL中的任何placeholder,所以會自動以query parameter的形式附加到redirect URL
      • 例如username如果是habuma,且spitterId是42,則redirect URL path: /spitter/habuma?spitterId=42
    • 使用path variable及query parameter的形式只能拿來發送簡單的值,像是String或數字,並沒有辦法發送更複雜的值,但這是flash能夠提供幫助的領域
  • Flash attribute
    • 假設這次不發送ID及username,而是要發送Spitter object。
    • 如果只發送ID,則需要到db找Spitter object,但是在redirect之前其實已經得到Spitter object
    • Spitter object只能設定為model中的attribute,但model會因為redirect而lost,因此必須將Spitter object放在一個位置,能夠在redirect的過程中存活
    • 有個方法是,將Spitter放到session中,session能夠長期存在,且可以跨多個session,所以在redirect之後可以從session取出attribute
    • 但Spring認為並不需要管理這些data,相反的,Spring提供了將data發送到flash attribute的功能,按照定義,flash attribute會一直攜帶這些data直到下一次的request,然後才會消失
    • Spring提供一種透過RedirectAttributes設定flash attribute的method,這是Spring3.1引入的sub-interface of Model。RedirectAttributes提供了model的所有功能,還有幾個method可以設定flash attribute
    • 可以使用addFlashAttribute()將Spitter加到model
    • @RequestMapping(value="/register", method=POST)
      public String processRegistration(Spitter spitter, RedirectAttributes model) {
        spitterRepository.save(spitter);
        model.addAttribute("username", spitter.getUsername());
        model.addFlashAttribute("spitter", spitter); // spitter object加到model中 ,將spitter當作key
        return "redirect:/spitter/{username}";
      }
      
      • 也可以直接寫成,讓key自行從type決定
      • model.addFlashAttribute(spitter);
        
    • 在redirect之前,所有的flash attribute都會複製到session中。而redirect之後,存在session中的flash attribute就會被取出,並從session轉移到model中,這樣一來redirect method就能從model中取得Spitter object,就像取得其他的model object依樣
    • from Spring in action 4
    • 下面黃色部份,在從資料庫找user之前,會先確認model裡面是否有spitter attribute,如果有的話,則什麼都不用做,直接會將spitter object傳遞到view
    • @RequestMapping(value = "/{username}", method = GET)
      public String showSpitterProfile(
       @PathVariable String username, Model model) {
       if (!model.containsAttribute("spitter")) { 
        model.addAttribute(
         spitterRepository.findByUsername(username));
       }
       return "profile";
      }

沒有留言:

張貼留言