2014-03-03 64 views
11

我正在開發一個使用SpringMVC的REST服務,其中我在類和方法級別具有@RequestMapping。Spring MVC REST通過返回JSON處理Bad Url(404)

此應用程序當前配置爲返回web.xml中配置的錯誤頁面jsp。

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

但是我想返回自定義JSON而不是這個錯誤頁面。

我能夠處理異常並返回json的其他異常,通過寫在控制器中,但不知道如何編寫邏輯返回JSON時根本不存在的URL。

@ExceptionHandler(TypeMismatchException.class) 
     @ResponseStatus(value=HttpStatus.NOT_FOUND) 
     @ResponseBody 
     public ResponseEntity<String> handleTypeMismatchException(HttpServletRequest req, TypeMismatchException ex) { 

      HttpHeaders headers = new HttpHeaders(); 
      headers.add("Content-Type", "application/json; charset=utf-8"); 
      Locale locale = LocaleContextHolder.getLocale(); 
      String errorMessage = messageSource.getMessage("error.patient.bad.request", null, locale); 

      errorMessage += ex.getValue(); 
      String errorURL = req.getRequestURL().toString(); 

      ErrorInfo errorInfo = new ErrorInfo(errorURL, errorMessage); 
      return new ResponseEntity<String>(errorInfo.toJson(), headers, HttpStatus.BAD_REQUEST); 

     } 

我試過@ControllerAdvice,它適用於其他異常的情況,而不是在映射不avaialble,

@ControllerAdvice 
public class RestExceptionProcessor { 

    @Autowired 
    private MessageSource messageSource; 

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class) 
    @ResponseStatus(value=HttpStatus.NOT_FOUND) 
    @ResponseBody 
    public ResponseEntity<String> requestMethodNotSupported(HttpServletRequest req, HttpRequestMethodNotSupportedException ex) { 
     Locale locale = LocaleContextHolder.getLocale(); 
     String errorMessage = messageSource.getMessage("error.patient.bad.id", null, locale); 

     String errorURL = req.getRequestURL().toString(); 

     ErrorInfo errorInfo = new ErrorInfo(errorURL, errorMessage); 
     return new ResponseEntity<String>(errorInfo.toJson(), HttpStatus.BAD_REQUEST); 
    } 

    @ExceptionHandler(NoSuchRequestHandlingMethodException.class) 
    @ResponseStatus(value=HttpStatus.NOT_FOUND) 
    @ResponseBody 
    public ResponseEntity<String> requestHandlingMethodNotSupported(HttpServletRequest req, NoSuchRequestHandlingMethodException ex) { 
     Locale locale = LocaleContextHolder.getLocale(); 
     String errorMessage = messageSource.getMessage("error.patient.bad.id", null, locale); 

     String errorURL = req.getRequestURL().toString(); 

     ErrorInfo errorInfo = new ErrorInfo(errorURL, errorMessage); 
     return new ResponseEntity<String>(errorInfo.toJson(), HttpStatus.BAD_REQUEST); 
    } 


} 

回答

22

在挖掘DispatcherServlet和HttpServletBean的.init()在springframework的我看到它有可能在春季4

org.springframework.web.servlet.DispatcherServlet

/** Throw a NoHandlerFoundException if no Handler was found to process this request? **/ 
private boolean throwExceptionIfNoHandlerFound = false; 

protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { 
    if (pageNotFoundLogger.isWarnEnabled()) { 
     String requestUri = urlPathHelper.getRequestUri(request); 
     pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + requestUri + 
       "] in DispatcherServlet with name '" + getServletName() + "'"); 
    } 
    if(throwExceptionIfNoHandlerFound) { 
     ServletServerHttpRequest req = new ServletServerHttpRequest(request); 
     throw new NoHandlerFoundException(req.getMethod().name(), 
       req.getServletRequest().getRequestURI(),req.getHeaders()); 
    } else { 
     response.sendError(HttpServletResponse.SC_NOT_FOUND); 
    } 
} 

throwExceptionIfNoHandlerFound是默認和W假Ë應該能夠在web.xml中

<servlet> 
    <servlet-name>appServlet</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
     <init-param> 
      <param-name>throwExceptionIfNoHandlerFound</param-name> 
      <param-value>true</param-value> 
     </init-param> 
    <load-on-startup>1</load-on-startup> 
    <async-supported>true</async-supported> 
</servlet> 

然後你就可以抓住它在使用這種方法@ControllerAdvice帶註釋的類。

@ExceptionHandler(NoHandlerFoundException.class) 
@ResponseStatus(value=HttpStatus.NOT_FOUND) 
@ResponseBody 
public ResponseEntity<String> requestHandlingNoHandlerFound(HttpServletRequest req, NoHandlerFoundException ex) { 
    Locale locale = LocaleContextHolder.getLocale(); 
    String errorMessage = messageSource.getMessage("error.bad.url", null, locale); 

    String errorURL = req.getRequestURL().toString(); 

    ErrorInfo errorInfo = new ErrorInfo(errorURL, errorMessage); 
    return new ResponseEntity<String>(errorInfo.toJson(), HttpStatus.BAD_REQUEST); 
} 

,讓我返回,而不是重定向到一個JSP頁面:)

{"message":"URL does not exist","url":"http://localhost:8080/service/patientssd"} 
+2

優秀的答案,值得注意的是,你不能使用[DefaultServletHandlerConfigurer](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/ DefaultServletHandlerConfigurer.html),因爲在獲取到DispatcherServlet之前會消耗404個郵箱 – muttonUp

+0

我試過這種方法,我希望實現HandlerExceptionResolver的解析器能夠捕獲此異常,但事實並非如此。 – user2807219

+0

對於基於java的配置,您需要覆蓋'AbstractAnnotationConfigDispatcherServletInitializer'的'customizeRegistration'。看[這個答案](http://stackoverflow.com/a/22751886/878514)。 – fracz

-1

如果你使用Spring 3.2或更高版本,你可以使用控制器建議(@ControllerAdvice)處理,其中包括映射錯誤(404)。你可以找到documentation here。看看17.11節。例如,您可以使用它來提供更詳細的日誌記錄,說明爲什麼您的請求綁定未針對特定的url進行匹配,或者只是返回比通用404更具體的響應。

+0

您使用的是什麼彈簧MVC版本? –

+0

我遵循你的建議,並使用HttpRequestMethodNotSupportedException和NoSuchRequestHandlingMethodException實現了@Controller建議,當我期待JSON時,我仍然被重定向到jsp。然而,控制器建議適用於其他場景。更新我的代碼。我使用的是Spring 3.2,我可以升級。 –

-2

您可以在下面的位置返回JSON爲沒有映射存在不良網址JSON響應,該/handle/404

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

你這個配置在web.xml中後,404錯誤將重定向到/手柄/ 404,你可以創建這個映射控制器和返回JSON結果。例如。

@RestController 
@RequestMapping(value = "handle") 
public class HttpErrorController { 
    @RequestMapping(value = "404") 
    public String handle404() { 
     return "404 error"; 
    } 
} 
0

如果你使用Spring的引導,同時設置了這兩個屬性:

spring.resources.add-mappings=false 
spring.mvc.throw-exception-if-no-handler-found=true 

現在你@ControllerAdvice註解類可以處理 「NoHandlerFoundException」,如下圖所示。

@ControllerAdvice 
@RequestMapping(produces = "application/json") 
@ResponseBody 
public class RestControllerAdvice { 

    @ExceptionHandler(NoHandlerFoundException.class) 
    public ResponseEntity<Map<String, Object>> unhandledPath(final NoHandlerFoundException e) { 
     Map<String, Object> errorInfo = new LinkedHashMap<>(); 
     errorInfo.put("timestamp", new Date()); 
     errorInfo.put("httpCode", HttpStatus.NOT_FOUND.value()); 
     errorInfo.put("httpStatus", HttpStatus.NOT_FOUND.getReasonPhrase()); 
     errorInfo.put("errorMessage", e.getMessage()); 
     return new ResponseEntity<Map<String, Object>>(errorInfo, HttpStatus.NOT_FOUND); 
    } 

} 

注意它是不夠的,只指定此屬性:

spring.mvc.throw-exception-if-no-handler-found=true 

,因爲默認情況下春天未知的URL映射到/ **,所以真的永遠是「沒有找到處理程序」。

要到/ **,你需要

spring.resources.add-mappings=false , 

這就是爲什麼這兩個屬性一起產生期望的行爲禁用未知的URL映射。