Tomcat8以降でJSPを動かす場合PUTとDELETEメソッドが使えないという問題があります。
この制限により、JSPに対してPUTまたはDELETEメソッドによるリクエストを実行すると405 Method Not Allowedが返ってきます。コンテナレベルで遮断しているため、サーブレットの処理や設定を変えても効果はありません。
この問題はHTTPメソッドオーバーライドで掻い潜ることができます。ただしPUT,DELETEを許可することでセキュリティの問題が生じる場合がありますので本手法の適用は自己責任で判断してください。
HTTPメソッドオーバーライド
やり方は簡単で、ヘッダにx-http-methodを付与し、POSTメソッドでアクセスするだけです。
PUTの発行例
curl -X POST -H "Content-Type:application/json" -H "x-http-method:PUT" -d "<データ>" "<APIのURL>"
ただしHTTPクライアントによってはヘッダを書き換える機能が無い場合があります。その時は次に紹介するサーブレット・フィルタをサーバ側に追加することで対応できます。
サーブレット・フィルタ
サーブレット・フィルタはサーブレットコンテナとサーブレットとの間に処理を噛ませることができる仕組みで、Webアプリケーションに対するリクエストの前処理やレスポンスの後処理を実行できます。
これに前述のHTTPメソッドオーバライドを実装します。これによりPUTやDELETEをPOSTとしてコンテナで受け付け、PUTやDELETEとしてサーブレットに渡すことができます。
サーブレット・フィルタの作成
フィルタのサンプルソース
以下にHTTPメソッドオーバライドを実行するフィルタのサンプルソースコードを載せます。
package test;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class MethodConvertingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
// do nothing
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(wrapRequest((HttpServletRequest) request), response);
}
@Override
public void destroy() {
// do nothing
}
private static HttpServletRequestWrapper wrapRequest(HttpServletRequest request) {
return new HttpServletRequestWrapper(request) {
@Override
public String getMethod() {
// リクエストメソッドがPUTまたはDELETEの場合に、このリクエストがPOSTであることを返す
if(request.getMethod().equalsIgnoreCase("PUT") || request.getMethod().equalsIgnoreCase("DELETE")) {
return "POST";
}
return request.getMethod();
}
@Override
public String getHeader(String name) {
// x-http-methodヘッダにオリジナルのメソッドを設定
if(name.equalsIgnoreCase("x-http-method")) {
return request.getMethod();
}
return super.getHeader(name);
}
@Override
public Enumeration getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
// x-http-methodヘッダを追加
names.add("x-http-method");
return Collections.enumeration(names);
}
};
}
}
フィルタクラスの配置
フィルタのソースをコンパイルし、生成されたクラスファイルをWebアプリケーションのWEB-INF/classesに配置してください。
フィルタ定義の追加
web.xmlに以下のフィルタ定義を追加します。複数のフィルタが定義されている場合、定義された順番に処理が適用されるため定義位置に注意してください。
<filter>
<filter-name>MethodConvertingFilter</filter-name>
<filter-class>test.MethodConvertingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MethodConvertingFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
これで実装は完了です。普通にPUTとDELETEが実行できるようになります。