Created
May 13, 2012 11:22
-
-
Save horte/2687913 to your computer and use it in GitHub Desktop.
Partial Update in Spring 3.1 Controller using @RequestBody with Validation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ContentPatternConverter implements Converter<String, ContentPattern> { | |
private PatternService patternService; | |
@Autowired | |
public ContentPatternConverter(PatternService patternService) { | |
this.patternService = patternService; | |
} | |
@Override | |
public ContentPattern convert(String source) { | |
return patternService.findOneByPatternId(Long.valueOf(source)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static void merge(Object obj, Object update) { | |
if (!obj.getClass().isAssignableFrom(update.getClass())) { | |
return; | |
} | |
Method[] methods = obj.getClass().getMethods(); | |
for (Method fromMethod : methods) { | |
if (fromMethod.getDeclaringClass().equals(obj.getClass()) && fromMethod.getName().startsWith("get")) { | |
String fromName = fromMethod.getName(); | |
String toName = fromName.replace("get", "set"); | |
try { | |
Method toMetod = obj.getClass().getMethod(toName, fromMethod.getReturnType()); | |
Object value = fromMethod.invoke(update, (Object[]) null); | |
if (value != null) { | |
toMetod.invoke(obj, value); | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Annotation to indicate a method parameter should be bound to a URL template, and retrieved as a domain entity. | |
* Used for annotated methods. | |
*/ | |
@Target(ElementType.PARAMETER) | |
@Retention(RetentionPolicy.RUNTIME) | |
@Documented | |
public @interface RequestBodyPathVariable { | |
/** | |
* The URI template variable to bind to. | |
* @return | |
*/ | |
String value(); | |
/** | |
* If validation should be made on the final entity. | |
* @return | |
*/ | |
String validation(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Annotation that binds a method parameter to a URI template variable and loads the | |
* entity based on the path attribute value using a custom {@link Converter} and | |
* merges the loaded entity with the provided request body. Finally does | |
* validation on the merged entity. Not compatible with {@link Valid} annotation, | |
* for validation of the final merged entity set | |
* {@link RequestBodyPathVariable#validation()} to true. | |
* | |
*/ | |
public class RequestBodyPathVariableMethodArgumentResolver implements HandlerMethodArgumentResolver { | |
public RequestBodyPathVariableMethodArgumentResolver() { | |
} | |
@Autowired | |
private RequestMappingHandlerAdapter requestMappingHandlerAdapter; | |
private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null; | |
private RequestResponseBodyMethodProcessor getRequestResponseBodyMethodProcessor() { | |
if (requestResponseBodyMethodProcessor == null) { | |
List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters(); | |
requestResponseBodyMethodProcessor = new RequestResponseBodyMethodProcessor(messageConverters); | |
} | |
return requestResponseBodyMethodProcessor; | |
} | |
/** | |
* A conversion service to convert primary key values. | |
*/ | |
@Autowired | |
private ConversionService conversionService; | |
public boolean supportsParameter(MethodParameter parameter) { | |
return parameter.hasParameterAnnotation(RequestBodyPathVariable.class); | |
} | |
@Override | |
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) | |
throws Exception { | |
// Get attribute name from URL | |
String attribute = parameter.getParameterAnnotation(RequestBodyPathVariable.class).value(); | |
// Get value for the URL attribute | |
String attributeValue = getRequestValueForAttribute(attribute, webRequest); | |
// Get existing resource | |
Object target = conversionService.convert(attributeValue, parameter.getParameterType()); | |
// Get the request body | |
Object arg = getRequestResponseBodyMethodProcessor().resolveArgument(parameter, mavContainer, webRequest, binderFactory); | |
// Merge the request body into the existing resource | |
Util.merge(target, arg); | |
// Run validation if validation=true (not compatible with @Valid | |
// annotation) | |
Annotation[] annotations = parameter.getParameterAnnotations(); | |
for (Annotation annot : annotations) { | |
if (annot.annotationType().getSimpleName().equalsIgnoreCase("RequestBodyPathVariable") && getValidationValue(parameter).equalsIgnoreCase("true")) { | |
String name = Conventions.getVariableNameForParameter(parameter); | |
WebDataBinder binder = binderFactory.createBinder(webRequest, target, name); | |
Object hints = AnnotationUtils.getValue(annot); | |
binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] { hints }); | |
BindingResult bindingResult = binder.getBindingResult(); | |
if (bindingResult.hasErrors()) { | |
throw new MethodArgumentNotValidException(parameter, bindingResult); | |
} | |
} | |
} | |
return target; | |
} | |
protected String getValidationValue(MethodParameter parameter) { | |
return parameter.getParameterAnnotation(RequestBodyPathVariable.class).validation(); | |
} | |
public boolean supportsReturnType(MethodParameter returnType) { | |
return false; | |
} | |
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) | |
throws Exception { | |
} | |
/** | |
* Copied from {@see org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#getRequestValueForAttribute) | |
* @param attributeName | |
* @param request | |
* @return | |
*/ | |
private String getRequestValueForAttribute(String attributeName, NativeWebRequest request) { | |
Map<String, String> variables = getUriTemplateVariables(request); | |
if (StringUtils.hasText(variables.get(attributeName))) { | |
return variables.get(attributeName); | |
} else if (StringUtils.hasText(request.getParameter(attributeName))) { | |
return request.getParameter(attributeName); | |
} else { | |
return null; | |
} | |
} | |
/** | |
* Copied from {@see org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor#getRequestValueForAttribute) | |
* @param attributeName | |
* @param request | |
* @return | |
*/ | |
@SuppressWarnings("unchecked") | |
private final Map<String, String> getUriTemplateVariables(NativeWebRequest request) { | |
Map<String, String> variables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, | |
RequestAttributes.SCOPE_REQUEST); | |
return (variables != null) ? variables : Collections.<String, String> emptyMap(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Register Custom Converter | |
<bean id="conversionService" | |
class="org.springframework.context.support.ConversionServiceFactoryBean"> | |
<property name="converters"> | |
<list> | |
<bean class="my.package.spring.ContentPatternConverter" /> | |
</list> | |
</property> | |
</bean> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment