spring-mvc - Spring MVC : Validation, Post-Redirect-Get、部分更新、乐观并发、字段安全

标签 spring-mvc optimistic-locking post-redirect-get optimistic-concurrency

[这是我看到的有关 Spring MVC 的常见问题列表,它们以类似的方式解决。我已经把它们贴在这里,所以我可以很容易地从其他问题中引用它们]

如何使用表单仅更新模型实体的几个字段?

我如何使用 Post-Redirect-Get模式与 Spring MVC,尤其是表单验证?

如何保护实体中的某些字段?

我如何实现 Optimistic Concurrency Control ?

最佳答案

  • 要部分更新实体,您应该使用 @SessionAttributes将模型存储在请求之间的 session 中。您可以使用隐藏的表单字段,但 session 更安全。
  • 要将 P/R/G 与验证一起使用,请使用 flashAttributes
  • 要保护字段,请使用 webDataBinder.setAllowedFields("field1","field2",...)或创建特定于表单的类,然后将值复制到您的实体。实体不需要 id 和 version 的 setter(如果使用 Hibernate)。
  • 要使用乐观并发控制,请使用 @Version在您的实体中添加注释并使用 @SessionAttributes在您的 Controller 上。

  • 示例代码:
    @Controller
    @RequestMapping("/foo/edit/{id}")
    @SessionAttributes({FooEditController.ATTRIBUTE_NAME})
    public class FooEditController {
    
        static final String ATTRIBUTE_NAME = "foo";
        static final String BINDING_RESULT_NAME = "org.springframework.validation.BindingResult." + ATTRIBUTE_NAME;
    
        @Autowired
        private FooRepository fooRepository;
    
        /*
         Without this, user can set any Foo fields they want with a custom HTTP POST
         setAllowedFields disallows all other fields. 
         You don't even need setters for id and version, as Hibernate sets them using reflection
        */
        @InitBinder
        void allowFields(WebDataBinder webDataBinder){
            webDataBinder.setAllowedFields("name"); 
        }
    
        /*
         Get the edit form, or get the edit form with validation errors
        */
        @RequestMapping(method = RequestMethod.GET)
        String getForm(@PathVariable("id") long id, Model model) {
    
            /* if "fresh" GET (ie, not redirect w validation errors): */
            if(!model.containsAttribute(BINDING_RESULT_NAME)) {
                Foo foo = fooRepository.findOne(id);
                if(foo == null) throw new ResourceNotFoundException();
                model.addAttribute(ATTRIBUTE_NAME, foo);
            }
    
            return "foo/edit-form";
        }
    
        /*
         @Validated is better than @Valid as it can handle http://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html
         @ModelAttribute will load Foo from session but also set values from the form post
         BindingResult contains validation errors
         RedirectAttribute.addFlashAttribute() lets you put stuff in session for ONE request
         SessionStatus lets you clear your SessionAttributes
        */
        @RequestMapping(method = RequestMethod.POST)
        String saveForm(
           @Validated @ModelAttribute(ATTRIBUTE_NAME) Foo foo,
           BindingResult bindingResult, 
           RedirectAttributes redirectAttributes, 
           HttpServletRequest request, 
           SessionStatus sessionStatus
        ) {
    
            if(!bindingResult.hasErrors()) {
                try {
                    fooRepository.save(foo);
                } catch (JpaOptimisticLockingFailureException exp){
                    bindingResult.reject("", "This record was modified by another user. Try refreshing the page.");
                }
            }
    
            if(bindingResult.hasErrors()) {
    
                //put the validation errors in Flash session and redirect to self
                redirectAttributes.addFlashAttribute(BINDING_RESULT_NAME, bindingResult);
                return "redirect:" + request.getRequestURI();
            }
    
            sessionStatus.setComplete(); //remove Foo from session
    
            redirectAttributes.addFlashAttribute("message", "Success. The record was saved");
            return "redirect:" + request.getRequestURI();
        }
    }
    

    Foo.java:
    @Entity
    public class Foo {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Version //for optimistic concurrency control
        private int version;
    
        @NotBlank
        private String name;
    
        public Long getId() {
            return id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    

    edit-form.jsp(与 Twitter Bootstrap 兼容):
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
    <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
    
    <form:form modelAttribute="foo">
    
        <spring:hasBindErrors name="foo">
            <c:if test="${errors.globalErrorCount > 0}">
                <div class="alert alert-danger" role="alert"><form:errors/></div>
            </c:if>
        </spring:hasBindErrors>
    
        <c:if test="${not empty message}">
          <div class="alert alert-success"><c:out value="${message}"/></div>
        </c:if>
    
        <div class="panel panel-default">
            <div class="panel-heading">
                <button class="btn btn-primary" name="btnSave">Save</button>
            </div>
    
            <div class="panel-body">
    
                <spring:bind path="name">
                    <div class="form-group${status.error?' has-error':''}">
                        <form:label path="name" class="control-label">Name <form:errors path="name"/></form:label>
                        <form:input path="name" class="form-control" />
                    </div>
                </spring:bind>
    
            </div>
        </div>
    
    </form:form>
    

    ResourceNotFoundException.java:
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public class ResourceNotFoundException extends RuntimeException {
    }
    

    关于spring-mvc - Spring MVC : Validation, Post-Redirect-Get、部分更新、乐观并发、字段安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29039116/

    相关文章:

    java - Spring MVC 抽象类绑定(bind)

    java - Spring Data JPA 乐观锁定与旧版 DB2/400

    java - JPA toplink 中是否可以实现列级乐观锁定?

    c# - ASP.NET MVC 中的发布/重定向/获取模式

    spring - Thymeleaf 中的有条件关闭标签

    java - 使用 jar 库自动部署应用程序

    java - Spring 4.0 : Resolve Type of GenericDao

    oracle - Oracle 数据库的 NHibernate <timestamp> 映射导致 StaleStateException

    php - post/redirect/get 并在 SESSION 中保存数据库查询

    jsf - JSF 2和发布/重定向/获取?