2016-03-23 3 views
2

Я новичок в Spring Boot, но после нескольких часов чтения сообщений и блогов об исключении handlig в Spring Boot REST, где никто ничего не писал о обработке такого исключения, выброшенного из пользовательского конвертера, I решил написать здесь.Обработка исключений в Spring Boot REST, отброшенном из пользовательского конвертера

Я разрабатываю небольшое приложение REST на основе Spring Boot, которое просто создается из 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)); 
    } 
} 

Поскольку оба guid и lang являются строками, и я хотел это фрагменты информации, были сильны типизированными из тех же начала, я создал просто преобразователь для GUID и Language типов и зарегистрировал его в классе 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 return 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 error к моим значениям. Вероятно, потому что исключения вызывают перед вызовом моего метода 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() в вашем приложении.

Я предпочитаю иметь конкретный файл конфигурации ошибок, для удобства чтения, но вы можете определить это как просто нормальный компонент в вашей конфигурации, если вы хотите:

@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 в моей конфигурации ошибок, а затем, если где угодно код, который я хочу, для exaple, 404 запрос я могу только throw new PageNotFoundException() (но это личное предпочтение).

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

Отображение пути (в приведенном выше примере «/ ошибки/404») отображает на нормальный контроллер, который хорош, как это позволяет делать какие-либо протоколирования ошибок/обработки/отправки по электронной почте и т.д., которые вы можете сделать при данной ошибке - недостатком этого является то, что, поскольку он является стандартным контроллером, обрабатывающим ваши ошибки, у вас потенциально есть открытая конечная точка, например/errors/404, что не является идеальным (существует несколько вариантов - затенение URL так меньше вероятно, будет обнаружен или использовать что-то вроде apache для предотвращения прямого доступа к этим конечным точкам и т. д.)

Я кратко написал об этом here - в том числе сведения о том, как это работает при размещении вашего приложения в качестве WAR в традиционном томе cat 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

Bad Request: [Сообщение об исключении]

Вы можете настроить реакцию, как вам нравится и вернуть JSON, если вы предпочитаете.

Чтобы получить path, а также вы можете придать HttpServletRequest в ваш метод обработчика ошибок

@ExceptionHandler(MethodArgumentTypeMismatchException.class) 
public ResponseEntity<String> 
handleConverterErrors(HttpServletRequest req, MethodArgumentTypeMismatchException exception) { 
    String path = req.getPathInfo() 
    //... 
} 
Смежные вопросы