2013-02-05 13 views
12

コントローラの外部で発生する可能性があります(たとえば、HTTP 404のような)エラーを処理グローバルに、私は私のweb.xmlに次のようなエントリがあります:私のErrorControllerでSpring MVC 3.2 - エラーページのコンテンツネゴシエーション?

<error-page> 
    <error-code>404</error-code> 
    <location>/errors/404</location> 
</error-page> 

を私はに似た、対応するメソッドを持っています次

@Controller 
@RequestMapping("/errors") 
public class ErrorController { 

    @RequestMapping(value = "/404", method = RequestMethod.GET) 
    @ResponseBody 
    public ResponseEntity<ErrorResponse> error404() { 

     ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!"); 

     return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND); 
    } 
} 

私が直面してる問題は、私が設定したContentNegotiationManagerとメッセージコンバータはこの場合に使用されていないことです。私は、リクエストがエラーページにリダイレクトされているので、コンテンツネゴシエーションで使用された元のリクエストの属性が失われ、完全に別個のリクエストとして扱われると考えられます。 (/mycontroller/badresource.jsonの元のリクエスト - >/errors/404(ファイル拡張子なし))

このようなエラーハンドラには、適切なコンテンツタイプ元のリクエストで要求されたとおりに

答えて

3

私はこれについて少しのハックを思いついたが、うまくいくようだ。基本的には、元の要求のファイル拡張子を判断するために、エラー処理で余分な処理が必要になります。私のweb.xmlに

私は中間のアクションに転送エラーがあります。エラー応答を生成しますアクションに転送する前に、チェックがあったかどうかを確認するために行われ、

<error-page> 
    <error-code>404</error-code> 
    <location>/errors/redirect</location> 
</error-page> 

その後元の要求のファイル拡張子。存在する場合は、それがフォワードURIに追加されることを保証する。 HTTPヘッダーは自動的に転送されるため、設定したコンテンツネゴシエーションにはファイル拡張子またはHTTPヘッダーのみが含まれていると、「エラーページ」によって適切なコンテンツタイプのエラーが返されます。

@Controller 
@RequestMapping("/errors") 
public class ErrorController { 

    @RequestMapping(value = "/redirect", method = RequestMethod.GET) 
    public void errorRedirect(HttpServletRequest request, HttpServletResponse response) { 

     // Get original request URI 
     String uri = (String)request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE); 

     // Try to determine file extension 
     String filename = WebUtils.extractFullFilenameFromUrlPath(uri); 
     String extension = StringUtils.getFilenameExtension(filename); 
     extension = StringUtils.hasText(extension) ? "." + extension : ""; 

     // Forward request to appropriate handler with original request's file extension (i.e. /errors/404.json) 
     String forwardUri = "/errors/404" + extension); 
     request.getRequestDispatcher(forwardUri).forward(request, response); 
    } 

    @RequestMapping(value = "/404", method = RequestMethod.GET) 
    @ResponseBody 
    public ResponseEntity<ErrorResponse> error404() { 

     ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!"); 

     return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND); 
    } 
} 
2

はい、実際には、アプリケーション例外とHTTP応答エラーコードは2つの異なるものです。

requestUriにアクセスできるように、コードを次のように変更できます。 私はそれに基づいてコンテンツの種類を見つけることができると思います。私はその原油を知っているが、私たちは別の解決策があると思うしません:私は、あなたのアプリケーションは、RESTサービスであると推測、おそらくあなたはRESTフルサービスでthis link on how 404 is handledを参照してください可能性の例から

@RequestMapping(value = "/404", method = RequestMethod.GET) 
@ResponseBody 
public ResponseEntity<ErrorResponse> error404(HttpServletRequest request) { 

    ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!"); 

    String requestUri = request.getRequestURI(); 

    return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND); 
} 

を。

+0

感謝を - ここ

は、私はそれを設定する方法をです。私はその記事を見ましたが、何かが欠けていない限り、コントローラからの例外だけを処理し、一般的なサーブレットのエラーは処理しません。また、この文脈では 'request.getRequestURI()'はあなたにエラーページのURIを与えます。元のリクエストのURIを取得するには 'req.getAttribute(" javax.servlet.error.request_uri ")'を使用する必要があるようです。ContentNegotiationManagerが使用する情報をオーバーライドして、エラーページではなく元のリクエストの情報に設定する方法があるのだろうかと思います。 – WayneC

2

春MVC 3.2は今@ControllerAdviceという便利な注釈が含まれています。定義した例外をグローバルに処理するExceptionHandlerメソッドを追加できます。

私にとっては、application/jsonまたはtext/htmlというクライアントに返す可能性のある2つのコンテンツタイプしか気にしません。入力用

@ControllerAdvice 
public class ExceptionControllerAdvice { 

    private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); 
    private static final MediaType JSON_MEDIA_TYPE = new MediaType("application", "json", DEFAULT_CHARSET); 

    //I decided to handle all exceptions in this one method 
    @ExceptionHandler(Throwable.class) 
    public @ResponseBody String handleThrowable(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException { 

     ... 

     if(supportsJsonResponse(request.getHeader("Accept"))) { 

      //return response as JSON 
      response.setStatus(statusCode); 
      response.setContentType(JSON_MEDIA_TYPE.toString()); 

        //TODO serialize your error in a JSON format 
        //return ... 

     } else { 

      //return as HTML 
      response.setContentType("text/html"); 
      response.sendError(statusCode, exceptionMessage); 
      return null; 
     } 
    } 

    private boolean supportsJsonResponse(String acceptHeader) { 

     List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader); 

     for(MediaType mediaType : mediaTypes) { 
      if(JSON_MEDIA_TYPE.includes(mediaType)) { 
       return true; 
      } 
     } 

     return false; 
    } 

} 
+0

StringではなくResponseEntityを返す方が良いでしょうか。そして私はこのアプローチでweb.xmlに404エラーページを置くべきですか?私の質問を確認してください:http://stackoverflow.com/questions/17322855/how-to-handle-404-exception-invoked-by-ajax-spring-mvc-3-2-controlleradvice – Alex

関連する問題