1 package pl.matsuo.core.web.mvc;
2
3 import com.google.gson.Gson;
4 import com.google.gson.reflect.TypeToken;
5 import org.apache.commons.io.IOUtils;
6 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.core.Conventions;
8 import org.springframework.core.MethodParameter;
9 import org.springframework.core.annotation.AnnotationUtils;
10 import org.springframework.stereotype.Component;
11 import org.springframework.validation.BindingResult;
12 import org.springframework.validation.Errors;
13 import org.springframework.web.bind.MethodArgumentNotValidException;
14 import org.springframework.web.bind.WebDataBinder;
15 import org.springframework.web.bind.annotation.RequestBody;
16 import org.springframework.web.bind.support.WebDataBinderFactory;
17 import org.springframework.web.context.request.NativeWebRequest;
18 import org.springframework.web.method.support.HandlerMethodArgumentResolver;
19 import org.springframework.web.method.support.ModelAndViewContainer;
20 import pl.matsuo.core.IRequestParams;
21 import pl.matsuo.core.service.facade.FacadeBuilder;
22 import pl.matsuo.core.service.parameterprovider.IParameterProvider;
23 import pl.matsuo.core.service.parameterprovider.MapParameterProvider;
24
25 import javax.servlet.http.HttpServletRequest;
26 import java.lang.annotation.Annotation;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31
32 import static java.util.Arrays.*;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 @Component
52 public class FacadeBuilderHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
53
54
55 @Autowired
56 FacadeBuilder facadeBuilder;
57 protected Gson gson = new Gson();
58
59
60 @Override
61 public boolean supportsParameter(MethodParameter parameter) {
62 return IRequestParams.class.isAssignableFrom(parameter.getParameterType())
63 || IParameterProvider.class.equals(parameter.getParameterType());
64 }
65
66
67 @Override
68 public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
69 NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
70 boolean returnFacade = IRequestParams.class.isAssignableFrom(parameter.getParameterType());
71 Object facade;
72 if (parameter.hasParameterAnnotation(RequestBody.class)) {
73 String body = IOUtils.toString(webRequest.getNativeRequest(HttpServletRequest.class).getInputStream());
74 if (returnFacade) {
75 facade = facadeBuilder.createFacade(
76 gson.fromJson(body, new TypeToken<Map<String, Object>>(){}.getType()),
77 parameter.getParameterType());
78 } else {
79 facade = facadeBuilder.createParameterProvider(
80 gson.fromJson(body, new TypeToken<Map<String, Object>>(){}.getType()));
81 }
82 } else {
83 Map<String, String[]> parameterMap = webRequest.getParameterMap();
84 Map<String, List<String>> params = new HashMap<>();
85 for (String key : parameterMap.keySet()) {
86 String[] values = parameterMap.get(key);
87 params.put(key, new ArrayList<>(asList(values)));
88 }
89
90 IParameterProvider<?> parameterProvider = new MapParameterProvider(params) {
91 @Override
92 public Object internalGet(String key, Class<?> expectedClass) {
93 if (expectedClass.equals(List.class)) {
94 return super.internalGet(key, expectedClass);
95 } else {
96 Object list = super.internalGet(key, Object.class);
97 if (list == null) {
98 return null;
99 } else if (List.class.isAssignableFrom(list.getClass())) {
100 return ((List) list).get(0);
101 } else {
102 return list;
103 }
104 }
105 }
106 };
107
108 if (returnFacade) {
109 facade = facadeBuilder.createFacade(parameterProvider, parameter.getParameterType());
110 } else {
111 facade = parameterProvider;
112 }
113 }
114
115 String name = Conventions.getVariableNameForParameter(parameter);
116 WebDataBinder binder = binderFactory.createBinder(webRequest, facade, name);
117 validate(binder, parameter);
118
119 mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
120
121 return facade;
122 }
123
124
125 private void validate(WebDataBinder binder, MethodParameter parameter) throws Exception {
126
127 Annotation[] annotations = parameter.getParameterAnnotations();
128 for (Annotation annot : annotations) {
129 if (annot.annotationType().getSimpleName().startsWith("Valid")) {
130 Object hints = AnnotationUtils.getValue(annot);
131 binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
132 BindingResult bindingResult = binder.getBindingResult();
133 if (bindingResult.hasErrors()) {
134 if (isBindExceptionRequired(binder, parameter)) {
135 throw new MethodArgumentNotValidException(parameter, bindingResult);
136 }
137 }
138 break;
139 }
140 }
141 }
142
143
144
145
146
147
148
149
150 private boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
151 int i = parameter.getParameterIndex();
152 Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
153 boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
154
155 return !hasBindingResult;
156 }
157 }
158