Spring MVC:验证、重定向后获取、部分更新、乐观并发、字段安全 [英] Spring MVC: Validation, Post-Redirect-Get, Partial Updates, Optimistic Concurrency, Field Security

查看:35
本文介绍了Spring MVC:验证、重定向后获取、部分更新、乐观并发、字段安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

如何只更新带有表单的模型实体的几个字段?

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

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

如何实现乐观并发控制?

解决方案

  1. 要部分更新实体,您应该使用 @SessionAttributes 在请求之间的会话中存储模型.您可以使用隐藏的表单字段,但会话更安全.

  2. 要使用 P/R/G 进行验证,请使用 flashAttributes

  3. 要保护字段,请使用 webDataBinder.setAllowedFields("field1","field2",...) 或创建特定于表单的类,然后将值复制到您的实体.实体不需要 id 和 version 的 setter(如果使用 Hibernate).

  4. 要使用乐观并发控制,请在您的实体中使用 @Version 注释,并在您的控制器上使用 @SessionAttributes.

示例代码:

@Controller@RequestMapping("/foo/edit/{id}")@SessionAttributes({FooEditController.ATTRIBUTE_NAME})公共类 FooEditController {静态最终字符串 ATTRIBUTE_NAME = "foo";static final String BINDING_RESULT_NAME = "org.springframework.validation.BindingResult."+ ATTRIBUTE_NAME;@自动连线私人 FooRepository fooRepository;/*没有这个,用户可以使用自定义 HTTP POST 设置他们想要的任何 Foo 字段setAllowedFields 禁止所有其他字段.您甚至不需要 id 和 version 的设置器,因为 Hibernate 使用反射设置它们*/@InitBindervoid allowFields(WebDataBinder webDataBinder){webDataBinder.setAllowedFields("name");}/*获取编辑表单,或获取带有验证错误的编辑表单*/@RequestMapping(method = RequestMethod.GET)String getForm(@PathVariable("id") long id, Model model) {/* 如果新鲜" GET(即,不重定向 w 验证错误):*/if(!model.containsAttribute(BINDING_RESULT_NAME)) {foo foo = fooRepository.findOne(id);if(foo == null) throw new ResourceNotFoundException();model.addAttribute(ATTRIBUTE_NAME, foo);}返回foo/edit-form";}/*@Validated 比 @Valid 更好,因为它可以处理 http://docs.jboss.org/hibernate/validator/5.1/reference/en-US/html/chapter-groups.html@ModelAttribute 将从会话加载 Foo 但也会从表单帖子中设置值BindingResult 包含验证错误RedirectAttribute.addFlashAttribute() 允许您将内容放入会话中以用于一个请求SessionStatus 可让您清除 SessionAttributes*/@RequestMapping(method = RequestMethod.POST)字符串保存表格(@Validated @ModelAttribute(ATTRIBUTE_NAME) Foo foo,BindingResult bindingResult,RedirectAttributes 重定向属性,HttpServletRequest 请求,会话状态会话状态){if(!bindingResult.hasErrors()) {尝试 {fooRepository.save(foo);} catch (JpaOptimisticLockingFailureException exp){bindingResult.reject("", "此记录已被其他用户修改.尝试刷新页面.");}}如果(bindingResult.hasErrors()){//将验证错误放在Flash会话中并重定向到selfredirectAttributes.addFlashAttribute(BINDING_RESULT_NAME, bindingResult);返回重定向:"+ request.getRequestURI();}sessionStatus.setComplete();//从会话中删除FooredirectAttributes.addFlashAttribute("message", "成功.记录已保存");返回重定向:"+ request.getRequestURI();}}

Foo.java:

@Entity公共类 Foo {@ID@GeneratedValue(策略 = GenerationType.IDENTITY)私人长ID;@Version//用于乐观并发控制私有 int 版本;@NotBlank私人字符串名称;公共长 getId() {返回标识;}公共字符串 getName() {返回名称;}公共无效集名称(字符串名称){this.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">保存</button>

<div class="panel-body"><spring:bind path="name"><div class="form-group${status.error?'有错误':''}"><form:label path="name" class="control-label">Name <form:errors path="name"/></form:label><form:input path="name" class="form-control"/>

</spring:bind>

</form:form>

ResourceNotFoundException.java:

@ResponseStatus(HttpStatus.NOT_FOUND)公共类 ResourceNotFoundException 扩展 RuntimeException {}

[This is a list of common questions I see about Spring MVC, which are solved in similar ways. I've posted them here, so I can easily refer to them from other questions]

How do I update only a few fields of a model entity with forms?

How do I use the Post-Redirect-Get pattern with Spring MVC, especially with form validation?

How do I secure certain fields in my entities?

How do I implement Optimistic Concurrency Control?

解决方案

  1. To partially update an entity, you should use @SessionAttributes to store the model in session between requests. You could use hidden form fields, but session is more secure.

  2. To use P/R/G with validation, use flashAttributes

  3. To secure fields use webDataBinder.setAllowedFields("field1","field2",...) or create a class specific to the form then copy values to your entity. Entities don't require setters for id and version (if using Hibernate).

  4. To use Optimistic Concurrency Control use the @Version annotation in your Entity and use @SessionAttributes on your controller.

Example code:

@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 compatible):

<%@ 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:验证、重定向后获取、部分更新、乐观并发、字段安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆