Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring表单提交和minum模板_Java_Spring_Spring Mvc_Boilerplate_Spring Web - Fatal编程技术网

Java Spring表单提交和minum模板

Java Spring表单提交和minum模板,java,spring,spring-mvc,boilerplate,spring-web,Java,Spring,Spring Mvc,Boilerplate,Spring Web,我一直在试图弄清楚spring表单提交的最佳实践是什么,以及实现这一点的最小样板是什么 我认为以下是最佳实践特征 验证失败时启用验证并保留表单值 禁用表单重新提交F5(即使用重定向) 防止在重定向之间的URL中显示模型值(model.clear()) 到目前为止,我已经想到了这个 @Controller @RequestMapping("/") public class MyModelController { @ModelAttribute("myModel") publi

我一直在试图弄清楚spring表单提交的最佳实践是什么,以及实现这一点的最小样板是什么

我认为以下是最佳实践特征

  • 验证失败时启用验证并保留表单值
  • 禁用表单重新提交F5(即使用重定向)
  • 防止在重定向之间的URL中显示模型值(
    model.clear()
到目前为止,我已经想到了这个

@Controller
@RequestMapping("/")
public class MyModelController {

    @ModelAttribute("myModel")
    public MyModel myModel() {
        return new MyModel();
    }

    @GetMapping
    public String showPage() {
        return "thepage";
    }

    @PostMapping
    public String doAction(
            @Valid @ModelAttribute("myModel") MyModel myModel,
            BindingResult bindingResult,
            Map<String, Object> model,
            RedirectAttributes redirectAttrs) throws Exception {
        model.clear();
        if (bindingResult.hasErrors()) {
            redirectAttrs.addFlashAttribute("org.springframework.validation.BindingResult.myModel", bindingResult);
            redirectAttrs.addFlashAttribute("myModel", myModel);
        } else {
            // service logic
        }
        return "redirect:/thepage";
    }
}
@控制器
@请求映射(“/”)
公共类MyModelController{
@ModelAttribute(“myModel”)
公共MyModel MyModel(){
返回新的MyModel();
}
@GetMapping
公共字符串showPage(){
返回“页面”;
}
@邮戳
公共字符串doAction(
@有效的@ModelAttribute(“myModel”)myModel myModel,
BindingResult BindingResult,
地图模型,
RedirectAttributes(重定向属性)引发异常{
model.clear();
if(bindingResult.hasErrors()){
redirectAttrs.addFlashAttribute(“org.springframework.validation.BindingResult.myModel”,BindingResult);
redirectAttrs.addFlashAttribute(“myModel”,myModel);
}否则{
//服务逻辑
}
返回“重定向:/thepage”;
}
}

有没有一种方法可以用更少的样板代码来实现这一点,或者这是实现这一点所需的最少代码量?

一种可能的方法是对Web表单使用原型,而不是创建简单的项目,您可以选择从Web表单的现有原型创建项目。它将为您提供足够的肉鸡板代码。你也可以制作自己的原型。 看看这个链接,可以更深入地了解原型。

首先,我不会违反该模式,这意味着我只会在表单成功发布时重定向

其次,我将彻底摆脱
BindingResult
样式。对于简单的情况来说,这很好,但是一旦您需要更复杂的通知来从服务/域/业务逻辑到达用户,事情就会变得棘手。此外,您的服务不太可重用

我要做的是将绑定的DTO直接传递给服务,服务将验证DTO并在出现错误/警告时发出通知。通过这种方式,您可以将业务逻辑验证与JSR303:Bean验证结合起来。 为此,您可以在服务中使用

按照通知模式,您需要一个通用通知包装器:

public class Notification<T> {
    private List<String> errors = new ArrayList<>();
    private T model; // model for which the notifications apply

    public Notification<T> pushError(String message) {
        this.errors.add(message);
        return this;
    }

    public boolean hasErrors() {
        return !this.errors.isEmpty();
    }

    public void clearErrors() {
        this.errors.clear();
    }

    public String getFirstError() {
        if (!hasErrors()) {
            return "";
        }
        return errors.get(0);
    }

    public List<String> getAllErrors() {
        return this.errors;
    }

    public T getModel() {
        return model;
    }

    public void setModel(T model) {
        this.model = model;
    }
}
公共类通知{
私有列表错误=新建ArrayList();
private T model;//应用通知的模型
公共通知pushError(字符串消息){
this.errors.add(消息);
归还这个;
}
公共布尔hasErrors(){
return!this.errors.isEmpty();
}
公共无效clearErrors(){
this.errors.clear();
}
公共字符串getFirstError(){
如果(!hasErrors()){
返回“”;
}
返回错误。获取(0);
}
公共列表getAllErrors(){
返回此项。错误;
}
公共T getModel(){
收益模型;
}
公共模型(T模型){
this.model=模型;
}
}
您的服务将类似于:

public Notification<MyModel> addMyModel(MyModelDTO myModelDTO){
    Notification<MyModel> notification = new Notification();
    //if(JSR 303 bean validation errors) -> notification.pushError(...); return notification;
    //if(business logic violations) -> notification.pushError(...); return notification;
    return notification;
}
Notification<MyModel> addAction = service.addMyModel(myModelDTO);
if (addAction.hasErrors()) {
    model.addAttribute("myModel", addAction.getModel());
    model.addAttribute("notifications", addAction.getAllErrors());
    return "myModelView"; // no redirect if errors
} 
redirectAttrs.addFlashAttribute("success", "My Model was added successfully");
return "redirect:/thepage";
public Notification addMyModel(MyModelDTO MyModelDTO){
通知通知=新通知();
//if(JSR303bean验证错误)->notification.pushError(…);返回通知;
//if(业务逻辑冲突)->notification.pushError(…);返回通知;
退货通知;
}
然后你的控制器会是这样的:

public Notification<MyModel> addMyModel(MyModelDTO myModelDTO){
    Notification<MyModel> notification = new Notification();
    //if(JSR 303 bean validation errors) -> notification.pushError(...); return notification;
    //if(business logic violations) -> notification.pushError(...); return notification;
    return notification;
}
Notification<MyModel> addAction = service.addMyModel(myModelDTO);
if (addAction.hasErrors()) {
    model.addAttribute("myModel", addAction.getModel());
    model.addAttribute("notifications", addAction.getAllErrors());
    return "myModelView"; // no redirect if errors
} 
redirectAttrs.addFlashAttribute("success", "My Model was added successfully");
return "redirect:/thepage";
Notification addAction=service.addMyModel(myModelDTO);
if(addAction.hasErrors()){
addAttribute(“myModel”,addAction.getModel());
model.addAttribute(“通知”,addAction.getAllErrors());
返回“myModelView”;//如果出现错误,则不重定向
} 
redirectAttrs.addFlashAttribute(“成功”,“我的模型添加成功”);
返回“重定向:/thepage”;
尽管仍存在
hasrerrors()
检查,但此解决方案更具可扩展性,因为您的服务可以通过新的业务规则通知继续发展

另一种方法是从您的服务中抛出一个自定义的
RuntimeException
,这个自定义的
RuntimeException
可以包含必要的消息/模型,并使用
@ControllerAdvice
捕获这个通用异常,从异常中提取模型和消息,并将它们放入模型中。这样,您的控制器只会将绑定的数据转发到服务。

根据答案,如果只有在成功验证后才发生重定向,则代码可以简化为:

@Controller
@RequestMapping("/")
public class MyModelController {

    @ModelAttribute("myModel")
    public MyModel myModel() {
        return new MyModel();
    }

    @GetMapping
    public String showPage() {
        return "thepage";
    }

    @PostMapping
    public String doAction(
            @Valid @ModelAttribute("myModel") MyModel myModel,
            BindingResult bindingResult,
            RedirectAttributes redirectAttrs) throws Exception {
        if (bindingResult.hasErrors()) {
            return "thepage";
        }
        // service logic
        redirectAttrs.addFlashAttribute("success", "My Model was added successfully");
        return "redirect:/thepage";
    }
}

这不属于吗?您可以使用AOP(面向方面编程)来减少样板代码。如果原型解决了您的问题,请告诉我,我可以帮助您制作自定义原型。谢谢您的建议。我不是要生成样板,而是要找到重复使用案例所需的最少样板。我喜欢不破坏Post/Redirect/Get(PRG)的建议。然而,我个人并不喜欢在服务层进行验证,让服务抛出异常似乎打破了这一前提。这是第二种方法……我也喜欢第一种方法(通知模式不使用异常)。我知道控制流反模式的异常用法,但是,在这种情况下,我会考虑,因为利弊大于弊。对我来说,在控制器中没有样板文件,并且为这个“ValidationException”有一个集中的处理程序似乎是第二个最好的选择。关于服务层中的验证,我提到了可重用性/业务规则演化,如果您没有它,您的服务很难重用。