【问题标题】:How to programmatically replace Spring's NumberFormatException with a user-friendly text?如何以用户友好的文本以编程方式替换 Spring 的 NumberFormatException?
【发布时间】:2018-02-06 18:59:47
【问题描述】:

我正在开发一个 Spring Web 应用程序,并且我有一个具有 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";
}

验证方法只是检查数据库中是否已经存在该名称,因此我没有包含它。我的问题是,当我故意在字段中输入文本时,我希望有一个很好的消息,例如“自动放电时间必须是数字!”。相反,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 的默认数字格式异常错误消息替换为我自己的。我知道可以用于此类事情的消息源,但我真的想直接在代码中实现这一点。

编辑

按照建议,我在控制器中构建了此方法,但仍然收到 Spring 的“无法转换属性值...”消息:

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

其他编辑

这是我的实体类的代码:

@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 文件来完成这项工作:

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

以及配置文件中的以下 bean 声明:

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

这给了我正确的错误消息,而不是我可以忍受的 Spring 泛型 TypeMismatchException / NumberFormatException,但我仍然想尽可能以编程方式完成所有事情,我正在寻找替代方案。

感谢您的帮助!

【问题讨论】:

  • 当然。我正在尝试使我的应用程序尽可能简单,因此当有人错误地(或在我的情况下故意)在“映射”到的字段中输入文本时,我试图使从服务器返回的错误更好一点一个整数值。
  • 好吧,我猜是这样。我建议的解决方案也在官方文档中进行了描述,它应该可以工作。我会用一个链接更新答案,也许它可以帮助你理解为什么它不起作用。
  • 谢谢,我会阅读这篇文章,看看我是否发现我做错了什么。
  • @Martin Hi Martin,你用的是什么版本的 spring?
  • 5.0.4 现在,虽然我想在不久的将来升级到最新版本。

标签: java spring replace message numberformatexception


【解决方案1】:

您可以通过提供类似于此处所做的 Spring DefaultBindingErrorProcessor 的实现来覆盖该消息: Custom Binding Error Message with Collections of Beans in Spring MVC

【讨论】:

  • 感谢您的回答!假设我的 message.properties 文件中有这个,并且目前一切正常: typeMismatch.nursingUnit.autoDCTime=自动放电时间必须是一个数字!我将如何复制返回此错误消息?不确定我是否理解,因为该方法返回一个字符串数组?
  • 在您的情况下,您只需要返回一个包含您想要的错误的字符串值。如果字段多次检查失败并且您需要多条消息,则它是一个数组。您可能需要向该字段添加注释,以便获得注释验证失败而不是转换失败。我不完全确定转换失败。我确定它会在注释验证失败时被调用。那里的消息格式看起来与注释的格式相同。在您的情况下,您可能只需检查该字段,如果它与 AutoDCTime 匹配,则返回您的错误消息。
  • 我构建了你建议的类,用 @Component 注释它并确保它在扫描包中。我在您提到的方法中添加了一个断点,当我尝试验证表单上的 autoDCTime 字段中的非数字时,它永远不会到达。
  • 在阅读更多内容时,看起来 DefaultmessageCodesResolver 仅适用于类型检查后发生的验证注释。我认为这将需要覆盖 DefaultBindingErrorProcessor。用你所拥有的想法更新答案,认为 message.properties 最终可能是最好的
【解决方案2】:

您可以使用以下方法注释方法:

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

并实现你想做的任何事情,以防引发该类型的异常。给定的代码将处理当前控制器中发生的异常。 如需进一步参考,请咨询this link

要进行全局错误处理,您可以通过以下方式使用@ControllerAdvice

@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler {

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

【讨论】:

  • 我确实记得尝试过类似的事情,但我无法弄清楚到底要放什么。当我生成自定义错误时,我会执行类似 FieldError error = new FieldError("nursingUnit", "name", shippingUnit.getName(), false, null, null, shippingUnit.getName() + " already exists!");我会在该方法中放入什么代码来告诉 Spring 用我的版本替换它的丑陋错误?
  • 我将此添加到我的控制器中,但我仍然收到 Spring 无法转换属性值消息:@ExceptionHandler({NumberFormatException.class}) private String numbererError() { return "自动放电时间必须是数字!"; }
  • autoDCTime 变量在哪里?我在你写的api中没有看到。
  • autoDCTime 属性附加到由我上面的 saveNursingUnit 方法验证的 NursingUnit 对象。该方法应该进入控制器还是实体类本身?
  • 请更新问题,包括 NursingUnit 的代码。
【解决方案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 类中存在多个异常处理程序,则会引发错误。

【讨论】:

  • @Martin,很抱歉发迟了,我昨天准备好了,但直到现在才发布。我还添加了一些小示例,说明如何在可能需要使用请求或异常本身的不同情况下扩展“ExceptionHandler”注释的使用。
  • 答案与我的相同(或非常相似)。
【解决方案4】:

处理 NumberFormatException。

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

【讨论】:

  • 验证方法是我自己的,它不会抛出任何异常。丑陋的错误是由 Spring 本身产生的,我正在寻找以某种方式替换它。
  • 你相信 NumberFormatException 是 Spring 抛出的吗?没有。
  • 它绝对不是由我编写的代码抛出的,我认为它确实是由 Spring 在尝试将用户输入的错误输入的字符串数据转换为我的实体的整数类型时抛出的类需要...
  • 太糟糕了,你的回答表明我完全不了解我所问的问题,你本来是可信的。我的 validate 方法不会抛出 NumberFormatException,如果它已经存在于数据库中,它只会将 FieldError 添加到 name 字段的模型中。
猜你喜欢
  • 2011-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-13
  • 2010-09-29
  • 2011-04-24
  • 2023-03-22
相关资源
最近更新 更多