7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Springで直接Responseデータを書き込むには

Posted at

HttpServletResponseに直接書き込みたい、という要望があったので、その方法を調べました。

やり方

Controllerの引数にHttpServletResponseを定義し、そこに書き込むだけです。

HelloController.java
@Controller
public class HelloController {

  @RequestMapping(value="/", method=RequestMethod.GET)
  public void hello(HttpServletResponse response) throws Exception {
    response.getWriter().write("hogehoge");
  }
}

レスポンスを書き込むパターンとView名を返却するパターンを分岐したい

戻り値をStringにします。
レスポンスに直接書き込んだ場合はnullを返却します。

HelloController.java
@Controller
public class HelloController {

  @RequestMapping(value="/{param}", method=RequestMethod.GET)
  public String hello(HttpServletResponse response, @PathVariable String param) throws Exception {
    if(Objects.equals("param", param)){
      response.getWriter().write("hogehoge");
      return null;
    }
    return "hello";
  }
}

nullを返却したらエラーが出たと文句を言われた

nullを返却したらエラーログが出たんだけど、と報告がありました。
ログを確認すると、JSPを探しに行ったけど見つからない、みたいな内容でした。

ということでソースコードを確認してみると以下のような感じになっていました。

HelloController.java
@Controller
public class HelloController {

  @Inject
  HttpServletResponse response

  @RequestMapping(value="/{param}", method=RequestMethod.GET)
  public String hello(@PathVariable String param) throws Exception {
    if(Objects.equals("param", param)){
      response.getWriter().write("hogehoge");
      return null;
    }
    return "hello";
  }
}

確かにこのように実装するとJSPを探してエラーになるみたいでした。つまり、Viewのrender処理が実行されている。
ちなみに、HttpServletResponseってInjectできるんだー、と思いましたが話の本筋ではないのでスルー。

フレームワークの中身を確認してみる

render処理が実行されないための条件は?

DispatcherServletを確認すると、HandlerAdapterを実行してModelAndViewを取得しています。
このとき、戻り値がnullであればViewによるrender処理は実行されません。Responseを直接書き込んでいる場合は、この挙動になることを想定しています。
(doDispatchやprocessDispatchResultなど)

では、HandlerAdapterの戻り値がnullになる条件は?

RequestMappingHandlerAdapterの処理を見てみると、ModelAndViewContainerのisRequestHandledがtrueになった場合にnullを返却しています。
(getModelAndViewなど)

では、これがtrueになるための条件は?

いろんな条件があるのですが、今回のケースに関して言うと、Controllerメソッドの引数にHttpServletResponseがあること、Controllerからの戻り値がnullであることが条件となります。
エラーが発生していたソースでは、フィールドに定義されたHttpServletResponseを利用しているため、HandlerAdapterの戻り値がnullにならなかった、ということです。

参考までに、View名がnullのときの動作

DispatcherServletのapplyDefaultViewNameからの処理を追えば、リクエストされたURLをView名として適用していることがわかります。

解決方法

引数にHttpServletResponseを追加しましょう。
そんなことできないよ~、という方は以下のようにControllerの戻り値の型をModelAndViewにしましょう。

HelloController.java
@Controller
public class HelloController {

  @Inject
  HttpServletResponse response

  @RequestMapping(value="/{param}", method=RequestMethod.GET)
  public ModelAndView hello(@PathVariable String param) throws Exception {
    if(Objects.equals("param", param)){
      response.getWriter().write("hogehoge");
      return null;
    }
    return new ModelAndView("hello");
  }
}

なぜこれで上手くいくのかは、ModelAndViewMethodReturnValueHandlerを読めばきっとわかります。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?