异常后重新显示带有用户输入数据的表单的推荐方法是什么?

what is a recommended way to redisplay a form with user-entered data after an exception?

为了学习 Java 和网络开发,我正在编写一个个人应用程序,该应用程序具有用于输入包含成分、说明和各种其他字段的食谱的表单。主要工具集是:

完成表单可能需要 5-10 分钟,因此如果出现任何类型的 DataAccessException,我希望能够重新显示包含之前条目的表单。

SO 和其他地方的大多数 post 建议只显示 SQL 错误的错误页面,但他们没有提到用户不得不重新输入数据的不便再次表单(由于表单上使用的数据结构的特殊性,从错误页面点击后退按钮将不会显示所有输入的数据)。

一种有效的简单方法是在控制器 POST 方法中捕获错误,如下所示:

    try {
        recipeService.addRecipe(recipe);
    }
    catch (DataAccessException ex)
    {
        model.addAttribute("dataError", "DataAccessException caught");
        model.addAttribute("ingredientList", recipeService.getIngredients(recipe));         
        return "recipe/addRecipe";
    }

dataError 消息仅用于开发,稍后将替换为更有意义的消息。

我看到这种方法的问题是,该方法最终必须更深入地挖掘实际错误并确定重新显示表单是否有意义,例如,约束违反 = "fix this piece of data that caused an error" 与数据库连接丢失 = "sorry, can't talk to the database - try back later"。而且无论我多么彻底地尝试识别 "redisplay" 异常,我怀疑我是否会捕获所有异常(双关语意)。

如果我改为删除 try/catch 并显示错误页面,那么我不知道如何使用错误页面中输入的数据返回表单,因为 "recipe" 模型属性不适用于此方法:

    @ExceptionHandler(DataAccessException.class)
    public ModelAndView handleDataAccessException(DataAccessException ex) {
    ModelAndView view = new ModelAndView("/common/error");

    logger.info("Recipe: DataAccessException exception!");

    String errorMsg = ExceptionUtils.getRootCauseMessage(ex);

    view.addObject("errorMessage", errorMsg);
    return view;
}

我还没有尝试过,但是将表单数据序列化到服务器上的文件是否是一种可接受的方法?在错误页面上,我可以按照 "Click here to redisplay your recipe and try again" 行添加一条消息。必须有一种方法让重新显示方法知道要检索哪组序列化数据并将其发送到表单,这可能是不可能的(对序列化还不够熟悉)。

我发现这个 post 关于在客户端保存表单数据:What is the best way to locally store user-entered data on a web page?。它针对的上下文与我尝试做的不同,但它提到了 localStorage 和 IndexedDB 这可能是更好的解决方案?

对于这个 post 的长度,我提前表示歉意,如果这个问题已经在其他地方得到回答 - 我找不到任何解决这个特定问题的 SO 问题(或任何博客)。如有任何帮助,我们将不胜感激。

编辑:

我相信我可以像这样将 ModelAttribute 和 SessionAttribute 添加到控制器中:

@SessionAttribute("recipe")
public class RecipeController {

    @ModelAttribute
    public Recipe initializeRecipe () {
        return new Recipe();
    }

....

但是,如果我正确理解这些注释 (?),这只会在会话期间有效。如果输入的数据可以在以后的会话中检索,例如,一旦数据库备份,那就更好了。

最佳实践方法是如您在编辑中所建议的那样,结合 @SessionAttributes@ModelAttribute,第一次,将调用带有 @ModelAttribute 注释的方法。在所有后续调用中,该值将从会话中获取。它非常适合您的案例和 wizzard 之类的对话操作。确保在最后一步,如果一切正常,您在 SessionStatus 对象上调用 setComplete(只需在处理程序方法的参数列表中的 SessionStatus 中列出,它将被注入).这将从会话中清除 @SessionAttributes 下列出的变量。

如果用户在填写表单时已经登录,那么最好的办法是在每次尝试时都保存一个草稿。这是一个额外的努力,但非常好的可用性。

关于在客户端存储的问题,是的,我认为你有一个有效的案例,但你应该对此做出判断。在客户端存储的缺点是:

  1. 用户可以轻松更改数据
  2. 它们通常尺寸较小
  3. 您已绑定浏览器

如果 none 这让您感到困扰,我认为使用客户端存储是非常好的。使用 localeStorage 就足够了。 IndexDB 提供了我认为您不需要的更高级别的操作,但请检查 comparison 以更好地理解它