Tomcat の PUTメソッドが息をしていない問題

| コメントをどうぞ

あけましておめでとうございます。
本年も有為無為に関わらず書き散らかしていく所存にございます。
生暖かい眼差しでお付き合いいただければと思います。
どうぞよろしくお願いいたします。
DiceK Mikamiです。

さて突然なのですが、アプリケーションサーバにTomcatを利用していると、たまにとんでもない大穴にはまったりします。
今回はそんなお話です。

 

概要

RESTfulなサービスを作っていたりした場合、様々なHTTPメソッドを利用します。

GETはリソース取得。
POSTはリソースの保存。
PUTはリソースの更新。
DELETEはリソースの削除。

などと言う具合です。
普通ですね。

ですが、これをそのまま実装しようとするとこけます。
特に、APサーバをTomcatなんてのにした日には、頭を抱えます。
GETとDELETE、POSTとPUTは非常に似ています。
前者はリクエストボディを持たず、後者はリクエストボディを持ちます。
つまり、パラメータを格納しておく場所が異なるわけです。

今回はPUTメソッドを使って更新処理を行おうとしたのですが、何ということでしょう。
リクエストボディが空になってしまうのです。
全俺が泣くわけです。

素直にPOSTメソッドを使えば良いのですが、「そんなのゆるせない!」ということで、無理矢理PUTメソッドを使おうと思います。
ちなみに、このPUTメソッドのリクエストボディが消えるのは、Tomcatのバグです。
(Tomcat8では直ってるかもしれません。未検証)
Jettyなどでは起きません。
やりやがったな!ドラ猫め!
 

環境

  • Tomcat 7.0.56
  • JDK 1.7.0
     

実装

いきなりですが、やらかした場合のコードを載せておきます。

public void doPut(HttpServletRequest req, HttpServletResponse resp)
              throws ServletException, java.io.IOException {
    String paramVal1 = req.getParameter("param1");
    String paramVal2 = req.getParameter("param2");
}

POSTメソッドと同じようにgetParameter()を使うと、nullが返ってきます。
ですので、やり方を変えます。

public void doPut(HttpServletRequest req, HttpServletResponse resp)
              throws ServletException, java.io.IOException {

    BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
    String queryString = br.readLine();
    Map map = parseQuery(queryString);

    String paramVal1 = map.get("param1");
    String paramVal2 = map.get("param2");
}

private Map parseQuery(String query) {
    String[] params = query.split("&");

    Map map = new HashMap();
    for(String param : params) {
        String[] val = param.split("=");

        map.put(URLDecoder.decode(val[0], "utf-8"), URLDecoder.decode(val[1], "utf-8"));
    }

    return map;
}

泥臭いですが、この方法しかありません。
リクエストのやり方にもよりますが、例の場合ではリクエストボディにはparam1=val1&param2=val2のように入っている場合です。
JSON形式ならもっと別のやり方ができるでしょう。
大切なことはInputStreamから無理矢理引っこ抜くしかないと言うことですね。
ちなみに、何らかのフレームワークを使って開発している場合、フレームワーク側が対処している場合があります。
例えば、Spring framework(v3.1以降)の場合ですと、、、


    httpPutFormFilter
    org.springframework.web.filter.HttpPutFormContentFilter



    httpPutFormFilter
    Spring MVC Dispatcher Servlet



    Spring MVC Dispatcher Servlet
    org.springframework.web.servlet.DispatcherServlet
    1

こんな感じでweb.xmlにフィルターを追加するだけで、getParameter()を使うことができます。
なんだかなぁ〜と言う感じの結末でした。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>