Как программно заменить Spring NumberFormatException на удобный текст?

Я работаю над веб-приложением Spring, и у меня есть объект, который имеет свойство Integer, которое пользователь может заполнить при создании нового объекта с использованием формы JSP. Метод контроллера, вызываемый этой формой, приведен ниже:

@RequestMapping(value = {"/newNursingUnit"}, method = RequestMethod.POST)
public String saveNursingUnit(@Valid NursingUnit nursingUnit, BindingResult result, ModelMap model) 
{
    boolean hasCustomErrors = validate(result, nursingUnit);
    if ((hasCustomErrors) || (result.hasErrors()))
    {
        List<Facility> facilities = facilityService.findAll();
        model.addAttribute("facilities", facilities);

        setPermissions(model);

        return "nursingUnitDataAccess";
    }

    nursingUnitService.save(nursingUnit);
    session.setAttribute("successMessage", "Successfully added nursing unit \"" + nursingUnit.getName() + "\"!");
    return "redirect:/nursingUnits/list";
}

Метод validate просто проверяет, существует ли имя в базе данных, поэтому я не включил его. Моя проблема в том, что, когда я намеренно вводил текст в поле, я хотел бы иметь приятное сообщение, такое как "Время автоматического разряда должно быть числом!". Вместо этого Spring возвращает эту абсолютно ужасную ошибку:

Failed to convert property value of type [java.lang.String] to required type [java.lang.Integer] for property autoDCTime; nested exception is java.lang.NumberFormatException: For input string: "sdf"

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

РЕДАКТИРОВАТЬ

Как и было предложено, я построил этот метод в своем контроллере, но я все еще получаю сообщение "Не удалось преобразовать значение свойства...":

@ExceptionHandler({NumberFormatException.class})
private String numberError()
{
   return "The auto-discharge time must be a number!";
}

OTHER EDIT

Вот код для моего класса сущности:

@Entity
@Table(name="tblNursingUnit")
public class NursingUnit implements Serializable 
{
private Integer id;
private String name;
private Integer autoDCTime;
private Facility facility;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() 
{
    return id;
}

public void setId(Integer id) 
{
    this.id = id;
}

@Size(min = 1, max = 15, message = "Name must be between 1 and 15 characters long")
@Column(nullable = false, unique = true, length = 15)
public String getName() 
{
    return name;
}

public void setName(String name) 
{
    this.name = name;
}

@NotNull(message = "The auto-discharge time is required!")
@Column(nullable = false)
public Integer getAutoDCTime() 
{
    return autoDCTime;
}

public void setAutoDCTime(Integer autoDCTime) 
{
    this.autoDCTime = autoDCTime;
}

@ManyToOne (fetch=FetchType.EAGER)
@NotNull(message = "The facility is required")
@JoinColumn(name = "id_facility", nullable = false)
public Facility getFacility()
{
    return facility;
}

public void setFacility(Facility facility)
{
    this.facility = facility;
}

@Override
public boolean equals(Object obj) 
{
    if (obj instanceof NursingUnit)
    {
        NursingUnit nursingUnit = (NursingUnit)obj;
        if (Objects.equals(id, nursingUnit.getId()))
        {
            return true;
        }
    }
    return false;
}

@Override
public int hashCode() 
{
    int hash = 3;
    hash = 29 * hash + Objects.hashCode(this.id);
    hash = 29 * hash + Objects.hashCode(this.name);
    hash = 29 * hash + Objects.hashCode(this.autoDCTime);
    hash = 29 * hash + Objects.hashCode(this.facility);
    return hash;
}

@Override
public String toString()
{
    return name + " (" + facility.getCode() + ")";
}
}

ДАЙТЕ ДРУГОЙ РЕДАКТИРОВАНИЕ

Я могу сделать эту работу, используя файл message.properties в pathpath, содержащий это:

typeMismatch.java.lang.Integer={0} must be a number!

И следующее объявление bean в файле конфигурации:

@Bean
public ResourceBundleMessageSource messageSource() 
{
    ResourceBundleMessageSource resource = new ResourceBundleMessageSource();
    resource.setBasename("message");
    return resource;
}

Это дает мне правильное сообщение об ошибке вместо Spring generic TypeMismatchException/NumberFormatException, с которым я могу жить, но все же, я хочу делать все программно, где это возможно, и я ищу альтернативу.

Спасибо за помощь!

Ответы

Ответ 2

Вы можете аннотировать метод с помощью:

@ExceptionHandler({NumberFormatException.class})
public String handleError(){
   //example
   return "Uncorrectly formatted number!";
}

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

Чтобы сделать глобальную обработку ошибок, вы можете использовать @ControllerAdvice следующим образом:

@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

   @ExceptionHandler({NumberFormatException.class})
    public String handleError(){
       //example
       return "Uncorrectly formatted number!";
    }
} 

Ответ 3

@Martin, я спросил вас о версии, потому что @ControllerAdvice доступен начиная с версии 3.2.

Я бы рекомендовал вам использовать @ControllerAdvice, который представляет собой аннотацию, которая позволяет вам писать код, который разделяется между контроллерами (аннотируется с помощью @Controller и @RestController), но он также может применяться только к контроллерам в определенных пакетах или конкретных классах.

ControllerAdvice предназначен для использования с @ExceptionHandler, @InitBinder или @ModelAttribute.

Вы устанавливаете целевые классы следующим образом: @ControllerAdvice(assignableTypes = {YourController.class,...}).

@ControllerAdvice(assignableTypes = {YourController.class, YourOtherController.class})
public class YourExceptionHandler{
    //Example with default message
    @ExceptionHandler({NumberFormatException.class})
    private String numberError(){
        return "The auto-discharge time must be a number!";
    }

    //Example with exception handling
    @ExceptionHandler({WhateverException.class})
    private String whateverError(WhateverException exception){
        //do stuff with the exception
        return "Whatever exception message!";
    }

    @ExceptionHandler({ OtherException.class })
    protected String otherException(RuntimeException e, WebRequest request) {
        //do stuff with the exception and the webRequest
        return "Other exception message!";
    }
} 

Что нужно иметь в виду, так это то, что если вы не установите цель и вы определите несколько обработчиков исключений для тех же исключений в разных классах @ControllerAdvice, Spring применит первый обработчик, который он найдет. Если в одном классе @ControllerAdvice присутствует несколько обработчиков исключений, будет @ControllerAdvice ошибка.

Ответ 4

Обработать NumberFormatException.

try {
 boolean hasCustomErrors = validate(result, nursingUnit);
}catch (NumberFormatException nEx){
 // do whatever you want
 // for example : throw custom Exception with the custom message.
}