Java Spring表单提交和minum模板
我一直在试图弄清楚spring表单提交的最佳实践是什么,以及实现这一点的最小样板是什么 我认为以下是最佳实践特征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
- 验证失败时启用验证并保留表单值
- 禁用表单重新提交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”有一个集中的处理程序似乎是第二个最好的选择。关于服务层中的验证,我提到了可重用性/业务规则演化,如果您没有它,您的服务很难重用。