1. 概要

このチュートリアルでは、 Spring でのリダイレクトの実装に焦点を当て、各戦略の背後にある理由について説明します。

2. なぜリダイレクトするのですか?

まず、Springアプリケーションでリダイレクトを実行する必要がある理由について考えてみましょう。

もちろん、考えられる例と理由はたくさんあります。 たとえば、フォームデータをPOSTしたり、二重送信の問題を回避したり、実行フローを別のコントローラーメソッドに委任したりする必要がある場合があります。

ここで簡単に説明します。通常の投稿/リダイレクト/取得パターンでは、二重送信の問題に適切に対処できません。また、最初の送信が完了する前にページを更新するなどの問題により、二重送信が発生する可能性があります。

3. RedirectViewでリダイレクトする

この単純なアプローチから始めて、例に直接進みましょう

@Controller
@RequestMapping("/")
public class RedirectController {
    
    @GetMapping("/redirectWithRedirectView")
    public RedirectView redirectWithUsingRedirectView(
      RedirectAttributes attributes) {
        attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectView");
        attributes.addAttribute("attribute", "redirectWithRedirectView");
        return new RedirectView("redirectedUrl");
    }
}

舞台裏では、 RedirectViewHttpServletResponse.sendRedirect()をトリガーし、実際のリダイレクトを実行します。

ここで、リダイレクト属性をメソッドに挿入していることに注目してください。フレームワークは手間のかかる作業を行い、これらの属性と対話できるようにします。

モデル属性attributeを追加します。これは、HTTPクエリパラメーターとして公開されます。 モデルには、オブジェクトのみが含まれている必要があります。通常は、文字列または文字列に変換できるオブジェクトです。

簡単なcurlコマンドを使用してリダイレクトをテストしてみましょう

curl -i http://localhost:8080/spring-rest/redirectWithRedirectView

そして、これが私たちの結果です:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: 
  http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView

4. プレフィックスを使用したリダイレクトredirect:

RedirectView を使用する以前のアプローチは、いくつかの理由で最適ではありません。

まず、コードで RedirectView を直接使用しているため、SpringAPIに結合されています。

次に、そのコントローラー操作を実装するときに、結果が常にリダイレクトになることを最初から知る必要がありますが、常にそうであるとは限りません。

より良いオプションは、プレフィックス redirect:を使用することです。 リダイレクトビュー名は、他の論理ビュー名と同様にコントローラーに挿入されます。 コントローラーはリダイレクトが発生していることさえ認識していません。

これは次のようになります。

@Controller
@RequestMapping("/")
public class RedirectController {
    
    @GetMapping("/redirectWithRedirectPrefix")
    public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) {
        model.addAttribute("attribute", "redirectWithRedirectPrefix");
        return new ModelAndView("redirect:/redirectedUrl", model);
    }
}

ビュー名がプレフィックスredirect:で返される場合、 UrlBasedViewResolver (およびそのすべてのサブクラス)は、これをリダイレクトが発生する必要があることを示す特別な指標として認識します。 ビュー名の残りの部分は、リダイレクトURLとして使用されます。

ここでこの論理ビュー名を使用する場合( redirect:/ redirectedUrl )、現在のサーブレットコンテキストに対してリダイレクトを実行していることに注意してください。

絶対URLにリダイレクトする必要がある場合は、 redirect:http:// localhost:8080 / spring-redirect-and-forward /redirectedUrlなどの名前を使用できます。

では、curlコマンドを実行すると

curl -i http://localhost:8080/spring-rest/redirectWithRedirectPrefix

すぐにリダイレクトされます:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: 
  http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectPrefix

5. プレフィックス付きで転送転送:

少し違うことをする方法を見てみましょう:フォワード。

コードの前に、フォワードとリダイレクトのセマンティクスの概要を簡単に説明します。

  • redirect は、302とLocationヘッダーの新しいURLで応答します。 次に、ブラウザ/クライアントは新しいURLに対して別のリクエストを行います。
  • forwardは完全にサーバー側で発生します。 サーブレットコンテナは同じリクエストをターゲットURLに転送します。 URLはブラウザで変更されません。

それでは、コードを見てみましょう。

@Controller
@RequestMapping("/")
public class RedirectController {
    
    @GetMapping("/forwardWithForwardPrefix")
    public ModelAndView redirectWithUsingForwardPrefix(ModelMap model) {
        model.addAttribute("attribute", "forwardWithForwardPrefix");
        return new ModelAndView("forward:/redirectedUrl", model);
    }
}

redirect:と同じように、 forward:プレフィックスはUrlBasedViewResolverとそのサブクラスによって解決されます。 内部的には、これにより InternalResourceView が作成され、新しいビューに対して RequestDispatcher.forward()が実行されます。

curl を使用してコマンドを実行すると、次のようになります。

curl -I http://localhost:8080/spring-rest/forwardWithForwardPrefix

HTTP 405を取得します(メソッドは許可されていません):

HTTP/1.1 405 Method Not Allowed
Server: Apache-Coyote/1.1
Allow: GET
Content-Type: text/html;charset=utf-8

まとめると、リダイレクトソリューションの場合の2つのリクエストと比較して、この場合、ブラウザ/クライアントからサーバー側に送信されるリクエストは1つだけです。 もちろん、以前にリダイレクトによって追加された属性もありません。

6. RedirectAttributesの属性

次に、リダイレクトで属性を渡す方法を詳しく見ていきましょう。リダイレクト属性でフレームワークを最大限に活用します。

@GetMapping("/redirectWithRedirectAttributes")
public RedirectView redirectWithRedirectAttributes(RedirectAttributes attributes) {
 
    attributes.addFlashAttribute("flashAttribute", "redirectWithRedirectAttributes");
    attributes.addAttribute("attribute", "redirectWithRedirectAttributes");
    return new RedirectView("redirectedUrl");
}

前に見たように、メソッドに属性オブジェクトを直接挿入できるため、このメカニズムは非常に使いやすくなっています。

フラッシュ属性も追加していることにも注意してください。これはURLに含まれない属性です。

この種の属性を使用すると、リダイレクトの最終ターゲットであるメソッドでのみ @ModelAttribute( “flashAttribute”)を使用して後でフラッシュ属性にアクセスできます。

@GetMapping("/redirectedUrl")
public ModelAndView redirection(
  ModelMap model, 
  @ModelAttribute("flashAttribute") Object flashAttribute) {
     
     model.addAttribute("redirectionAttribute", flashAttribute);
     return new ModelAndView("redirection", model);
 }

つまり、curl を使用して機能をテストすると、次のようになります。

curl -i http://localhost:8080/spring-rest/redirectWithRedirectAttributes

新しい場所にリダイレクトされます:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=4B70D8FADA2FD6C22E73312C2B57E381; Path=/spring-rest/; HttpOnly
Location: http://localhost:8080/spring-rest/redirectedUrl;
  jsessionid=4B70D8FADA2FD6C22E73312C2B57E381?attribute=redirectWithRedirectAttributes

このように、ModelMapの代わりにRedirectAttributesを使用すると、リダイレクト操作に関係する2つのメソッド間で一部の属性のみを共有することができます。

7. プレフィックスのない代替構成

次に、別の構成、つまりプレフィックスを使用しないリダイレクトについて見ていきましょう。

これを実現するには、org.springframework.web.servlet.view.XmlViewResolverを使用する必要があります。

<bean class="org.springframework.web.servlet.view.XmlViewResolver">
    <property name="location">
        <value>/WEB-INF/spring-views.xml</value>
    </property>
    <property name="order" value="0" />
</bean>

これは、前の構成で使用したorg.springframework.web.servlet.view.InternalResourceViewResolverの代わりです。

<bean 
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
</bean>

また、構成でRedirectViewBeanを定義する必要があります。

<bean id="RedirectedUrl" class="org.springframework.web.servlet.view.RedirectView">
    <property name="url" value="redirectedUrl" />
</bean>

これで、この新しいBeanをid で参照することにより、リダイレクトをトリガーできます。

@Controller
@RequestMapping("/")
public class RedirectController {
    
    @GetMapping("/redirectWithXMLConfig")
    public ModelAndView redirectWithUsingXMLConfig(ModelMap model) {
        model.addAttribute("attribute", "redirectWithXMLConfig");
        return new ModelAndView("RedirectedUrl", model);
    }
}

そしてそれをテストするために、再びcurlコマンドを使用します

curl -i http://localhost:8080/spring-rest/redirectWithRedirectView

そして、これが私たちの結果です:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Location: 
  http://localhost:8080/spring-rest/redirectedUrl?attribute=redirectWithRedirectView

8. HTTPPOSTリクエストのリダイレクト

銀行支払いなどのユースケースでは、HTTPPOSTリクエストをリダイレクトする必要がある場合があります。 返されたHTTPステータスコードに応じて、POSTリクエストをHTTPGETまたはPOSTにリダイレクトできます。

HTTP 1.1プロトコルリファレンスに従って、ステータスコード301(永続的に移動)および302(検出)により、リクエストメソッドをPOSTからGETに変更できます。 この仕様では、対応する307(一時的なリダイレクト)および308(永続的なリダイレクト)のステータスコードも定義されており、要求メソッドをPOSTからGETに変更することはできません。

投稿リクエストを別の投稿リクエストにリダイレクトするためのコードを見てみましょう。

@PostMapping("/redirectPostToPost")
public ModelAndView redirectPostToPost(HttpServletRequest request) {
    request.setAttribute(
      View.RESPONSE_STATUS_ATTRIBUTE, HttpStatus.TEMPORARY_REDIRECT);
    return new ModelAndView("redirect:/redirectedPostToPost");
}
@PostMapping("/redirectedPostToPost")
public ModelAndView redirectedPostToPost() {
    return new ModelAndView("redirection");
}

次に、curlコマンドを使用してPOSTのリダイレクトをテストします

curl -L --verbose -X POST http://localhost:8080/spring-rest/redirectPostToPost

宛先の場所にリダイレクトされます:

> POST /redirectedPostToPost HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.49.0
> Accept: */*
> 
< HTTP/1.1 200 
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 08 Aug 2017 07:33:00 GMT

{"id":1,"content":"redirect completed"}

9. パラメータを使用して転送

次に、forwardプレフィックスを持つ別のRequestMappingにいくつかのパラメーターを送信するシナリオを考えてみましょう。

その場合、HttpServletRequestを使用して呼び出し間でパラメーターを渡すことができます。

param1param2を別のマッピングforwardedWithParamsに送信する必要があるメソッドforwardWithParamsは次のとおりです。

@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
public ModelAndView forwardWithParams(HttpServletRequest request) {
    request.setAttribute("param1", "one");
    request.setAttribute("param2", "two");
    return new ModelAndView("forward:/forwardedWithParams");
}

実際、マッピング forwardedWithParams は、まったく新しいコントローラーに存在する可能性があり、同じコントローラーに存在する必要はありません。

@RequestMapping(value="/forwardWithParams", method = RequestMethod.GET)
@Controller
@RequestMapping("/")
public class RedirectParamController {

    @RequestMapping(value = "/forwardedWithParams", method = RequestMethod.GET)
    public RedirectView forwardedWithParams(
      final RedirectAttributes redirectAttributes, HttpServletRequest request) {
        redirectAttributes.addAttribute("param1", request.getAttribute("param1"));
        redirectAttributes.addAttribute("param2", request.getAttribute("param2"));

        redirectAttributes.addAttribute("attribute", "forwardedWithParams");
        return new RedirectView("redirectedUrl");
    }
}

説明のために、このcurlコマンドを試してみましょう。

curl -i http://localhost:8080/spring-rest/forwardWithParams

結果は次のとおりです。

HTTP/1.1 302 Found
Date: Fri, 19 Feb 2021 05:37:14 GMT
Content-Language: en-IN
Location: http://localhost:8080/spring-rest/redirectedUrl?param1=one¶m2=two&attribute=forwardedWithParams
Content-Length: 0

ご覧のとおり、param1param2は最初のコントローラーから2番目のコントローラーに移動しました。 最後に、forwardedWithParamsが指すredirectedUrlという名前のリダイレクトに表示されました。

10. 結論

この記事では、 Spring でリダイレクトを実装するための3つの異なるアプローチ、これらのリダイレクトを行うときに属性を処理/渡す方法、およびHTTPPOSTリクエストのリダイレクトを処理する方法について説明しました。