【问题标题】:Spring - Construct new modelAndView from existing BindingResult and ModelSpring - 从现有的 BindingResult 和 Model 构造新的 modelAndView
【发布时间】:2018-12-15 00:35:53
【问题描述】:

我正在尝试改进我的 Spring MVC 应用程序以使用全局异常处理程序来捕获所有控制器中的各种持久性异常。例如,这是当用户尝试保存新的 StrengthUnit 对象时运行的控制器代码。当 PersistenceException 被抛出时,所有的验证工作都很好,并且表单正确返回,并在 name 字段下方显示一条错误消息。结果页面也正确地包含了strengthUnit属性,并且能够将字段(这个实体只有一个名称字段)绑定回表单:

@RequestMapping(value = {"/newStrengthUnit"}, method = RequestMethod.POST)
public String saveStrengthUnit(@Valid StrengthUnit strengthUnit, BindingResult result, ModelMap model) throws Exception
{
    try
    {
        setPermissions(model);

        if (result.hasErrors())
        {
            return "strengthUnitDataAccess";
        }
        strengthUnitService.save(strengthUnit);

        session.setAttribute("successMessage", "Successfully added strength unit \"" + strengthUnit.getName() + "\"!");
    }
    catch (PersistenceException ex)
    {
        FieldError error = new FieldError("strengthUnit", "name", strengthUnit.getName(), false, null, null, 
                "Strength unit \"" + strengthUnit.getName() + "\" already exists!");
        result.addError(error);

        return "strengthUnitDataAccess";
    }

    return "redirect:/strengthUnits/list";
}

我试图以此为起点来合并我构建的全局异常处理程序,但我不明白如何调用该处理程序并返回具有相同模型和绑定结果的相同页面。我尝试了一些非常丑陋的自定义异常,只是为了尝试理解机制并让处理程序返回与以前相同的页面,但我无法让它工作。

这是我构建的自定义异常:

public class EntityAlreadyPersistedException extends Exception
{
    private final Object entity;
    private final FieldError error;
    private final String returnView;
    private final ModelMap model;
    private final BindingResult result;

    public EntityAlreadyPersistedException(String message, Object entity, FieldError error, String returnView, ModelMap model, BindingResult result) 
    {
        super(message);

        this.entity = entity;
        this.error = error;
        this.returnView = returnView;
        this.model = model;
        this.result = result;
    }

    public Object getEntity() 
    {
        return entity;
    }

    public FieldError getError() 
    {
        return error;
    }

    public String getReturnView() 
    {
        return returnView;
    }

    public ModelMap getModel()
    {
        return model;
    }

    public BindingResult getResult() 
    {
        return result;
    }
}

这是我在控制器的 saveStrengthUnit 方法中修改后的 catch 块:

catch (PersistenceException ex)
    {
        FieldError error = new FieldError("strengthUnit", "name", strengthUnit.getName(), false, null, null, 
                "Strength unit \"" + strengthUnit.getName() + "\" already exists!");
        result.addError(error);

        throw new EntityAlreadyPersistedException("Strength unit \"" + strengthUnit.getName() + "\" already exists!", strengthUnit, error, 
                "strengthUnitDataAccess", model, result);
    }

最后是全局异常处理程序的捕获方法:

@ExceptionHandler(EntityAlreadyPersistedException.class)
public ModelAndView handleDataIntegrityViolationException(HttpServletRequest request, Exception ex)
{
    EntityAlreadyPersistedException actualException;

    actualException = ((EntityAlreadyPersistedException)ex);

    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName(actualException.getReturnView());
    modelAndView.addObject(BindingResult.MODEL_KEY_PREFIX + "strengthUnitForm", actualException.getResult());

    if (actualException.getEntity() instanceof StrengthUnit)
    {
        modelAndView.addObject("strengthUnit", (StrengthUnit)actualException.getEntity());
    }
    return modelAndView;
}

对于经验丰富的 Spring 开发人员来说,这非常丑陋并且可能非常愚蠢,但我还不是一个(还)。这可行,但绑定结果丢失并且不会出现验证错误。如何修改此代码使其行为与以前一样,同时仍使用全局异常处理程序来处理所有错误?

谢谢!

【问题讨论】:

  • 这很有趣,但我认为与 Spring MVC 背道而驰。我处理异常的方式是使用单个状态对象来容纳我的响应,无论遇到成功还是失败。这允许控制器做它正常的事情并总是返回预期的对象类型以及对客户端或消费者有用的信息。用扩展异常处理这个很有趣,但我认为这里不合适。
  • 嘿,我想我遇到了类似的事情。发现这个可能有帮助的错误报告:jira.spring.io/browse/…。对不起,如果它没有用,不能完全理解你的所有代码(对 spring 很新)

标签: java spring exception model-view-controller controller


【解决方案1】:

如果您试图捕获在使用@Valid 时抛出的有效异常。并且您希望同一个处理程序处理该异常,则在@ExceptionHandler 注释中再添加一个异常类

医生怎么说

@ExceptionHandler 的值可以设置为一个异常数组 类型。如果抛出异常匹配列表中的类型之一, 那么用匹配的@ExceptionHandler注解的方法将是 调用。如果未设置注释值,则异常类型 被列为方法参数。

@Valid 注解抛出的异常是 MethodArgumentNotValidException ,所以你可以在同一个处理方法上添加这个异常

希望对你有帮助

【讨论】:

  • 我不需要我的异常处理程序来捕获这些,因为我已经在控制器方法中返回带有 result.hasErrors() 的同一页面。我想要做的是让异常处理程序构造一个新的 modelAndView 对象以返回,但以某种方式将 BindingResult 设置为与我在控制器方法中的相同。换句话说,我正在寻找 ModelAndView 对象上没有的某种 setBindingResult() 方法。
【解决方案2】:

1.您将实体设置为strengthUnit.getName() 而不是strengthUnit

    throw new EntityAlreadyPersistedException("Strength unit \"" + strengthUnit.getName() + "\" already exists!", strengthUnit, error, 
                    "strengthUnitDataAccess", model, result);

2.但是你正在检查if (actualException.getEntity() instanceof StrengthUnit

希望有帮助,尝试将实体设置为strengthUnit。

【讨论】:

  • 如果您看起来正确,我正在正确设置实体,这与我的问题无关。异常逻辑工作正常,但我无法返回一个新的 ModelAndView 对象,其 BindingResult 与我控制器中以前的模型具有相同的 BindingResult。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-31
  • 1970-01-01
  • 1970-01-01
  • 2012-11-19
  • 1970-01-01
  • 2015-03-16
  • 2020-09-05
相关资源
最近更新 更多