spring-mvc - requestmapping - spring mvc netbeans
Spring MVC: Validación, Post-redirección-Get, actualizaciones parciales, concurrencia optimista, seguridad de campo (1)
[Esta es una lista de preguntas comunes que veo sobre Spring MVC, que se resuelven de manera similar. Los he publicado aquí, así que puedo consultarlos fácilmente desde otras preguntas]
¿Cómo actualizo solo unos pocos campos de una entidad modelo con formularios?
¿Cómo uso el patrón Post-Redirect-Get con Spring MVC, especialmente con la validación de formularios?
¿Cómo aseguro ciertos campos en mis entidades?
¿Cómo implemento el Control de concurrencia optimista ?
-
Para actualizar parcialmente una entidad, debe usar
@SessionAttributes
para almacenar el modelo en sesión entre solicitudes. Puede usar campos de formulario ocultos, pero la sesión es más segura. -
Para usar P / R / G con validación, use
flashAttributes
-
Para proteger los campos, use
webDataBinder.setAllowedFields("field1","field2",...)
o cree una clase específica para el formulario y luego copie los valores en su entidad. Las entidades no requieren establecedores para id y versión (si se usa Hibernate). -
Para usar el Control de concurrencia optimista, use la anotación
@Version
en su Entidad y use@SessionAttributes
en su controlador.
Código de ejemplo:
@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 (compatible con 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 {
}