簡體   English   中英

處理從自定義轉換器拋出的Spring Boot REST中的異常

[英]Handling exception in Spring Boot REST thrown from custom converter

我是Spring Boot的新手,但經過幾個小時的閱讀關於Spring Boot REST中異常handlig的帖子和博客,沒有人寫任何關於處理從自定義Converter拋出的異常的事情,我決定寫這里。

我開發了基於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 = "/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));
    }
}

因為guidlang都是字符串,我希望這些信息是從同一個開始的強類型,我創建了GUIDLanguage類型的簡單轉換器並在Application類中注冊它:

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);
   }
}

使用@ControllerAdvice @ResponseStatus@ControllerAdvice @ExpectationHandler@ExpectationHandler所有類型的處理異常我無法在Controller中捕獲轉換器的異常以重寫(或更好地映射)json錯誤的“status”,“error”,“exception”和“message”將原始字段響應給我的值。 可能是因為在調用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字段的最后位置,但當然應該與我的自定義消息交換。自定義異常應該在exception字段中,現在是Spring異常。 這當然是目標,不知道如何在這種背景下實現它。

請解決我的問題,捕獲從轉換器拋出的異常並映射它們的方式,因為可以使用@ControllerAdvice和從控制器拋出的異常。 Thx提前。

你是對的 - @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" ) )
   }

}

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

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

映射路徑(在上面的示例中為“/ errors / 404”)映射到普通控制器,這很好,因為它允許您執行任何錯誤記錄/處理/電子郵件等您可能想要對給定錯誤執行的操作 - 缺點這是因為它是一個處理你的錯誤的標准控制器,你可能有一個暴露的端點,如/ errors / 404 - 這是不理想的(有一些選項 - 模糊URL,因此不太可能被發現,或使用像apache這樣的東西,以防止直接訪問這些端點等)

在這里簡要介紹了它 - 包括在傳統的tomcat服務器中將應用程序作為WAR托管時的工作原理

當轉換器拋出異常時, @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()
    //...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM