处理从自定义转换器抛出的Spring Boot REST中的异常

Mar*_*ski 8 java rest exception-handling converter spring-boot

我是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));
    }
}
Run Code Online (Sandbox Code Playgroud)

因为这两个guidlang是字符串,我想这条信息来自同一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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
}
...
Run Code Online (Sandbox Code Playgroud)

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);
   }
}
Run Code Online (Sandbox Code Playgroud)

使用所有类型的处理异常带@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"
Run Code Online (Sandbox Code Playgroud)

}

自定义异常仅在message字段的最后位置,但当然应该与我的自定义消息交换.自定义排除应该在exception现在是Spring异常的字段中.这当然是目标,不知道如何在这种背景下实现它.

请解决我的问题,捕获从转换器抛出的异常,并按照可以完成的方式映射它们,@ControllerAdvice并从控制器抛出异常.Thx提前.

rhi*_*nds 5

你是对的 - @ControllerAdviceetc 只捕获源自控制器方法内部的异常 - 老实说,我发现需要一些思考才能让你的头脑理解所有错误处理并整合一致的错误处理方法(而不必在不同的地方)。

因为在我的应用程序中,我不可避免地需要捕获控制器外部的此类错误(或自定义 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" ) )
   }

}
Run Code Online (Sandbox Code Playgroud)

您可以根据特定的异常类型以及 Http 响应代码映射错误页面,因此它非常灵活 - 但我通常做的是定义自定义异常并将 Http 响应代码附加到异常 - 这样我就可以映射错误中的HttpStatus代码配置,然后如果代码中的任何位置我想要,例如 404 请求,我就可以throw new PageNotFoundException()(但那是个人喜好)。

@ResponseStatus(value = HttpStatus.NOT_FOUND)
class PageNotFoundException extends Exception { }
Run Code Online (Sandbox Code Playgroud)

映射路径(在上面的示例中为“/errors/404”)映射到普通控制器,这很好,因为它可以让您对给定错误执行您可能想要执行的任何错误日志记录/处理/电子邮件等操作 - 缺点其中之一是,由于它是处理错误的标准控制器,因此您可能有一个暴露的端点,例如 /errors/404 - 这并不理想(有几个选项 - 隐藏 URL,以便不太可能被发现,或者使用像 apache 这样的东西可以防止直接访问这些端点等)

我在这里简要介绍了它- 包括有关在传统 tomcat 服务器中将应用程序作为 WAR 托管时它如何工作的详细信息


Edd*_*Edd 5

当您的转换器引发异常时,类(或其他类型的异常处理程序)将拾取的实际异常@ControllerAdviceMethodArgumentTypeMismatchException.

如果您将转换器配置为选择 a,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() + "]");
    }
}
Run Code Online (Sandbox Code Playgroud)

在上面的情况下,如果抛出我的自定义异常,响应将如下所示:

状态:401

您的会话已过期

否则对于其他例外情况将是

状态:400

错误请求:[异常消息]

您可以根据需要自定义响应并返回 JSON(如果您愿意)。

为了获得path同样的结果,你可以将 a 注入HttpServletRequest到你的错误处理方法中

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