今日はServlet例外とエラーハンドリングについて見ていきます。以前、Javaでの例外処理についての記事を書きましたが、Webアプリケーションの場合は通常のJavaの例外処理以上のものが必要です。
Servlet例外
doGet()メソッドとdoPost()メソッドがjavax.servlet.ServletException
とIOException
をスローしていることに気付くと思いますが、アプリケーションからこれらの例外をスローした場合に何が起こるか見てみましょう。Servlet例外をスローする単純なサーブレットを作成します。
package com.journaldev.servlet.exception;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/MyExceptionServlet")
public class MyExceptionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
throw new ServletException("GET method is not supported.");
}
}
これをGETメソッドでブラウザから呼び出すと、以下のような応答が得られます。ブラウザはHTMLのみを理解するため、アプリケーションが例外をスローすると、サーブレットコンテナが例外を処理し、HTMLの応答を生成します。このロジックはサーブレットコンテナに固有のものです。私はTomcatを使用しており、このエラーページが表示されます。他のサーバー(JBossやGlassfishなど)を使用する場合は、異なるエラーHTML応答が表示される可能性があります。この応答の問題は、ユーザーにとって何の価値もないことです。また、アプリケーションのクラスやサーバーの詳細がユーザーに表示され、セキュリティ上の観点からも良くありません。
サーブレットエラー
I am sure you must have seen 404 error when you are trying to hit a URL that doesn’t exists. Let’s see how our servlet container responds to 404 error. If we send request for an invalid URL, we get response HTML like below image. Again it’s a generic HTML generated by server on our application behalf and hold little to no value to the user.
サーブレットの例外とエラー処理
サーブレットAPIは、デプロイメント記述子で設定できるカスタムの例外処理およびエラーハンドラーサーブレットのサポートを提供します。これらのサーブレットの目的は、アプリケーションで発生した例外またはエラーを処理し、ユーザーに有用なHTML応答を送信することです。アプリケーションのホームページへのリンクや、何が間違っているかをユーザーに知らせるための詳細などを提供することができます。したがって、まずカスタムの例外とエラーハンドラーサーブレットを作成する必要があります。アプリケーションには複数の例外とエラーハンドラーサーブレットを持つことができますが、シンプルにするために、単一のサーブレットを作成し、例外とエラーの両方に使用します。AppExceptionHandler.java
package com.journaldev.servlet.exception;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/AppExceptionHandler")
public class AppExceptionHandler extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processError(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processError(request, response);
}
private void processError(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// サーブレット例外の解析
Throwable throwable = (Throwable) request
.getAttribute("javax.servlet.error.exception");
Integer statusCode = (Integer) request
.getAttribute("javax.servlet.error.status_code");
String servletName = (String) request
.getAttribute("javax.servlet.error.servlet_name");
if (servletName == null) {
servletName = "Unknown";
}
String requestUri = (String) request
.getAttribute("javax.servlet.error.request_uri");
if (requestUri == null) {
requestUri = "Unknown";
}
// レスポンスのコンテンツタイプを設定
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.write("Exception/Error Details ");
if(statusCode != 500){
out.write("Error Details
");
out.write("Status Code:"+statusCode+"
");
out.write("Requested URI:"+requestUri);
}else{
out.write("Exception Details
");
out.write("- Servlet Name:"+servletName+"
");
out.write("- Exception Name:"+throwable.getClass().getName()+"
");
out.write("- Requested URI:"+requestUri+"
");
out.write("- Exception Message:"+throwable.getMessage()+"
");
out.write("
");
}
out.write("
");
out.write("Home Page");
out.write("");
}
}
これをデプロイメント記述子でどのように設定できるかを見てみましょう。その後、実装方法と動作について理解します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>ServletExceptionHandling</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/AppExceptionHandler</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/AppExceptionHandler</location>
</error-page>
</web-app>
error-page要素を使用して、アプリケーションに例外ハンドラーのサーブレットを指定するのは非常に簡単です。各error-page要素は、error-code要素またはexception-type要素を持つ必要があります。私たちはlocation要素で例外ハンドラーサーブレットを定義します。上記の設定に基づいて、アプリケーションが404エラーまたはServletExceptionをスローする場合、AppExceptionHandlerサーブレットで処理されます。このような例外とエラーシナリオが発生すると、サーブレットコンテナは例外ハンドラーサーブレットの対応するHTTPメソッドを呼び出し、リクエストとレスポンスオブジェクトを渡します。例外を処理するためにサーブレットコンテナがサーブレットを呼び出す前に、リクエストにいくつかの属性を設定して例外に関する有用な情報を取得します。そのうちのいくつかはjavax.servlet.error.exception、javax.servlet.error.status_code、javax.servlet.error.servlet_name、およびjavax.servlet.error.request_uriです。例外の場合、ステータスコードは常に「500 Internal Server Error」に対応しており、他の種類のエラーでは404、403などの異なるエラーコードを取得します。ステータスコードを使用して、私たちの実装はユーザーに異なるタイプのHTMLレスポンスを表示します。また、アプリケーションのホームページへのハイパーリンクも提供します。今、ServletExceptionをスローしているサーブレットをアクセスすると、以下の画像のようなレスポンスが得られます。 404レスポンスを引き起こす無効なURLにアクセスしようとすると、以下の画像のようなレスポンスが得られます。
これは見栄えが良く、ユーザーが何が起こったのかを簡単に理解し、正しい場所に移動する方法を提供しています。また、アプリケーションの機密情報をユーザーに送信しないようにもしています。常にウェブアプリケーションに例外ハンドラーを設置するべきです。すべてのランタイム例外とその他の例外を単一の例外ハンドラーで処理したい場合は、exception-typeとしてThrowableを指定することができます。
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/AppExceptionHandler</location>
</error-page>
複数のエラーページエントリがある場合、Throwable用とIOException用の2つがあるとし、アプリケーションがFileNotFoundExceptionをスローした場合、IOExceptionのエラーハンドラによって処理されます。また、例外ハンドラとしてJSPページを使用することもできます。ただし、サーブレットのマッピングではなく、JSPファイルの場所を指定してください。これにて、ウェブアプリケーションにおけるサーブレット例外のハンドリングは終了です。お気に召しましたでしょうか。
Servlet例外ハンドリングのサンプルプロジェクトをダウンロードする
このシリーズの他の記事をご覧ください: