2016-03-23 54 views
2

我是Spring Boot的新手,但在Spring Boot REST中閱讀關於異常handlig的帖子和博客幾個小時後,沒有人編寫任何有關處理自定義Converter引發的異常的問題,決定寫在這裏。在Spring Boot中處理異常REST從自定義轉換器拋出REST

我開發了基於Spring Boot的小型REST應用程序,該應用程序只是從IntelliJ生成的。典型的方法是這樣的

@RestController 
@RequestMapping("/resources") 
public class CVResourceService { 

    private final TechnologyRepository technologyRepository; 
    private final ProjectRepository projectRepository; 

    @Autowired 
    public CVResourceService(TechnologyRepository technologyRepository,  ProjectRepository projectRepository) { 
     this.technologyRepository = technologyRepository; 
     this.projectRepository = projectRepository; 
    } 

    @RequestMapping(value = "https://stackoverflow.com/users/{guid}/projects/langs/{lang}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseBody 
    public Collection getUserProjects(@PathVariable("guid") GUID userGUID,   @PathVariable("lang") Language language) { 
     return ProjectDTOAssembler.toDTOs(projectRepository.findOne(userGUID, language)); 
    } 
} 

因爲無論​​和lang是字符串,我想這條信息來自同一begining強類型,我創建了簡單的轉換器GUIDLanguage類型和應用類註冊吧:

public final class GUIDConverter implements Converter{ 

    @Override 
    public GUID convert(String source) { 
     return GUID.fromString(source); 
    } 
} 

public class LanguageConverter implements Converter{ 

    @Override 
    public Language convert(String source) { 
     Language language = Language.of(source); 
     if (language == null) { throw new WrongLanguagePathVariableException(); } 

     return language; 
    } 
} 

GUID從工廠方法拋出異常,

... 
public static GUID fromString(String string) { 
    String[] components = string.split("-"); 

    if (components.length != 5) 
     throw new IllegalArgumentException("Invalid GUID string: " + string); 

    return new GUID(string); 
} 
... 

Language返回null,然後我拋出轉換器的自定義異常。 中註冊申請:

@SpringBootApplication 
public class Application extends WebMvcConfigurerAdapter { 

    @Override 
    public void addFormatters(FormatterRegistry registry) { 
     registry.addConverter(new GUIDConverter()); 
     registry.addConverter(new LanguageConverter()); 
    } 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

使用所有類型的處理異常的有@ResponseStatus@ControllerAdvice@ExpectationHandler我沒聽轉換器的例外控制器重寫(或更好的地圖)‘狀態’,‘錯誤’,「 JSON錯誤響應原始字段的異常「和」消息「。可能是因爲在調用REST方法之前引發異常。我也試過ResponseEntityExceptionHandler的解決方案,但它根本不起作用。

對於請求http://localhost:8080/resources/users/620e643f-406f-4c69-3f4c-3f2c303f3f3f/projects/langs/end,其中正確的語言是en,響應與例外是:

{ 
    "timestamp": 1458812172976, 
    "status": 400, 
    "error": "Bad Request", 
    "exception": "org.springframework.web.method.annotation.MethodArgumentTypeMismatchException", 
    "message": "Failed to convert value of type [java.lang.String] to required type [com.cybercom.cvdataapi.domain.Language]; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.PathVariable com.cybercom.cvdataapi.domain.Language] for value 'end'; nested exception is com.cybercom.cvdataapi.interfaces.rest.converter.WrongLanguagePathVariableException", 
    "path": "/resources/users/620e643f-406f-4c69-3f4c-3f2c303f3f3f/projects/langs/end" 

}

其中自定義異常僅僅是在最後位置message領域,但當然應與交流我的自定義消息。自定義excetion應該在exception字段中,現在是Spring異常。這是目標,當然,不知道如何在這種情況下實現它。

請解決我的問題,即捕獲轉換器拋出的異常並將它們映射爲@ControllerAdvice和控制器拋出的異常。 Thx提前。

+0

請例外添加到您的問題 – msparer

回答

1

你是對的 - @ControllerAdvice等只捕獲控制器內部方法發生的異常 - 老實說,我發現需要一些思路才能理解所有的錯誤處理,並且提供一致的錯誤處理方法在不同的地方複製錯誤處理)。

因爲在我的應用程序中,我不可避免地需要捕獲控制器外部的這些(或自定義404等)錯誤,我只是爲了應用程序範圍的錯誤映射而去 - 我假設你已經定義了應用程序作爲JAR應用程序中的main()方法。

我的選擇是有一個特定的錯誤配置文件中,爲便於閱讀,但如果你願意,你可以在你的配置將此定義爲只是一個普通的bean:

@Configuration 
class ErrorConfiguration implements EmbeddedServletContainerCustomizer { 

    /** 
    * Set error pages for specific error response codes 
    */ 
    @Override public void customize(ConfigurableEmbeddedServletContainer container) { 
     container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/errors/404")) 
    } 

} 

您可以根據特定的地圖錯誤頁面異常類型以及Http響應代碼,所以它非常靈活 - 但我通常做的是定義自定義異常並將Http響應代碼附加到異常 - 這樣,我可以將HttpStatus代碼映射到我的錯誤配置中,然後在任何地方我想要的代碼,例如404請求我可以只是throw new PageNotFoundException()(但那是個人喜好)。

@ResponseStatus(value = HttpStatus.NOT_FOUND) 
class PageNotFoundException extends Exception { } 

映射路徑(在上面的例子中「/錯誤/ 404」)映射到一個正常的控制器,這是很好的,因爲它讓你做任何錯誤日誌記錄/處理/電子郵件等,你可能想要做在一個給定的錯誤 - 這樣做的缺點是,因爲它是一個標準的控制器處理你的錯誤,你可能有一個暴露的端點,如/錯誤/ 404 - 這是不理想的(有幾個選項 - 模糊URL很可能被發現,或者使用類似apache的東西來阻止直接訪問這些端點等)

我簡要地寫了關於它的內容here - 包括將應用程序作爲WAR託管在傳統tomcat server

0

當你的轉換器拋出異常時,@ControllerAdvice類(或其他類型的異常處理程序)將拾取的實際異常是MethodArgumentTypeMismatchException

如果您將轉換器配置爲選擇MethodArgumentTypeMismatchException,那麼它應該可以工作。

例如:

@ControllerAdvice 
public class ExceptionConfiguration extends ResponseEntityExceptionHandler { 

    @ExceptionHandler(MethodArgumentTypeMismatchException.class) 
    public ResponseEntity<String> handleConverterErrors(MethodArgumentTypeMismatchException exception) { 
     Throwable cause = exception.getCause() // First cause is a ConversionException 
            .getCause(); // Second Cause is your custom exception or some other exception e.g. NullPointerException 
     if(cause.getClass() == UnauthorizedException.class) { 
      return ResponseEntity.status(401).body("Your session has expired"); 
     } 
     return ResponseEntity.badRequest().body("Bad Request: [" + cause.getMessage() + "]"); 
    } 
} 

在上述情況下,如果我的自定義異常拋出的反應會是什麼樣子:

狀態:401

您的會話已過期

否則對於其他例外,它將是

狀態:400

錯誤的請求:[異常消息]

,你喜歡,你可以自定義的響應並返回JSON,如果你喜歡。

爲了得到path以及你可以注入一個HttpServletRequest到您的錯誤處理方法

@ExceptionHandler(MethodArgumentTypeMismatchException.class) 
public ResponseEntity<String> 
handleConverterErrors(HttpServletRequest req, MethodArgumentTypeMismatchException exception) { 
    String path = req.getPathInfo() 
    //... 
}