001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.web.json; 017 018import com.alibaba.fastjson.JSON; 019import com.alibaba.fastjson.JSONArray; 020import com.alibaba.fastjson.JSONObject; 021import com.jfinal.aop.Interceptor; 022import com.jfinal.aop.Invocation; 023import com.jfinal.core.ActionException; 024import com.jfinal.core.Controller; 025import com.jfinal.kit.LogKit; 026import com.jfinal.render.RenderManager; 027import io.jboot.aop.InterceptorBuilder; 028import io.jboot.aop.Interceptors; 029import io.jboot.aop.annotation.AutoLoad; 030import io.jboot.utils.ClassUtil; 031import io.jboot.utils.ObjectUtil; 032import io.jboot.utils.StrUtil; 033import io.jboot.web.controller.JbootController; 034 035import java.lang.reflect.*; 036import java.util.Collection; 037import java.util.HashSet; 038import java.util.Map; 039import java.util.Set; 040 041@AutoLoad 042public class JsonBodyParseInterceptor implements Interceptor, InterceptorBuilder { 043 044 private static final String startOfArray = "["; 045 private static final String endOfArray = "]"; 046 047 @Override 048 public void intercept(Invocation inv) { 049 050 Controller controller = inv.getController(); 051 Method method = inv.getMethod(); 052 Parameter[] parameters = method.getParameters(); 053 Type[] paraTypes = method.getGenericParameterTypes(); 054 055 Object jsonObjectOrArray = StrUtil.isBlank(controller.getRawData()) ? null : JSON.parse(controller.getRawData()); 056 057 for (int index = 0; index < parameters.length; index++) { 058 JsonBody jsonBody = parameters[index].getAnnotation(JsonBody.class); 059 if (jsonBody != null) { 060 Class<?> paraClass = parameters[index].getType(); 061 Object result = null; 062 try { 063 Type paraType = paraTypes[index]; 064 if (paraType instanceof TypeVariable) { 065 Type variableRawType = getTypeVariableRawType(controller.getClass(), ((TypeVariable<?>) paraType)); 066 if (variableRawType != null) { 067 paraClass = (Class<?>) variableRawType; 068 paraType = variableRawType; 069 } 070 } 071 result = parseJsonBody(jsonObjectOrArray, paraClass, paraType, jsonBody.value()); 072 } catch (Exception e) { 073 String message = "Can not parse \"" + parameters[index].getType() 074 + "\" in method " + ClassUtil.buildMethodString(method) + ", Cause: " + e.getMessage(); 075 if (jsonBody.skipConvertError()) { 076 LogKit.error(message); 077 } else { 078 throw new ActionException(400, RenderManager.me().getRenderFactory().getErrorRender(400), message); 079 } 080 } 081 082 inv.setArg(index, result); 083 } 084 } 085 086 inv.invoke(); 087 } 088 089 090 /** 091 * 获取方法里的泛型参数 T 对于的真实的 Class 类 092 * 093 * @param defClass 094 * @param typeVariable 095 * @return 096 */ 097 private static Type getTypeVariableRawType(Class<?> defClass, TypeVariable<?> typeVariable) { 098 Type type = defClass.getGenericSuperclass(); 099 if (type instanceof ParameterizedType) { 100 Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments(); 101 if (typeArguments.length == 1) { 102 return typeArguments[0]; 103 } else if (typeArguments.length > 1) { 104 TypeVariable<?>[] typeVariables = typeVariable.getGenericDeclaration().getTypeParameters(); 105 for (int i = 0; i < typeVariables.length; i++) { 106 if (typeVariable.getName().equals(typeVariables[i].getName())) { 107 return typeArguments[i]; 108 } 109 } 110 } 111 } 112 return null; 113 } 114 115 116 public static Object parseJsonBody(Object jsonObjectOrArray, Class<?> paraClass, Type paraType, String jsonKey) throws InstantiationException, IllegalAccessException { 117 if (jsonObjectOrArray == null) { 118 return paraClass.isPrimitive() ? ObjectUtil.getPrimitiveDefaultValue(paraClass) : null; 119 } 120 if (Collection.class.isAssignableFrom(paraClass) || paraClass.isArray()) { 121 return parseArray(jsonObjectOrArray, paraClass, paraType, jsonKey); 122 } else { 123 return parseObject((JSONObject) jsonObjectOrArray, paraClass, paraType, jsonKey); 124 } 125 } 126 127 128 private static Object parseObject(JSONObject rawObject, Class<?> paraClass, Type paraType, String jsonKey) throws IllegalAccessException, InstantiationException { 129 if (StrUtil.isBlank(jsonKey)) { 130 return toJavaObject(rawObject, paraClass, paraType); 131 } 132 133 Object result = null; 134 String[] keys = jsonKey.split("\\."); 135 for (int i = 0; i < keys.length; i++) { 136 if (rawObject != null && !rawObject.isEmpty()) { 137 String key = keys[i].trim(); 138 if (StrUtil.isNotBlank(key)) { 139 //the last 140 if (i == keys.length - 1) { 141 if (key.endsWith(endOfArray) && key.contains(startOfArray)) { 142 String realKey = key.substring(0, key.indexOf(startOfArray)); 143 JSONArray jarray = rawObject.getJSONArray(realKey.trim()); 144 if (jarray != null && jarray.size() > 0) { 145 String arrayString = key.substring(key.indexOf(startOfArray) + 1, key.length() - 1); 146 int arrayIndex = StrUtil.isBlank(arrayString) ? 0 : Integer.parseInt(arrayString.trim()); 147 result = arrayIndex >= jarray.size() ? null : jarray.get(arrayIndex); 148 } 149 } else { 150 result = rawObject.get(key); 151 } 152 } 153 //not last 154 else { 155 rawObject = getJSONObjectByKey(rawObject, key); 156 } 157 } 158 } 159 } 160 161 if (result == null || StrUtil.EMPTY.equals(result)) { 162 return paraClass.isPrimitive() ? ObjectUtil.getPrimitiveDefaultValue(paraClass) : null; 163 } 164 165 if (paraClass == String.class && paraClass == paraType) { 166 return result.toString(); 167 } 168 169 // JSONObject 类型 170 if (result instanceof JSONObject) { 171 return toJavaObject((JSONObject) result, paraClass, paraType); 172 } 173 174 return ObjectUtil.convert(result, paraClass); 175 } 176 177 178 private static Object parseArray(Object rawJsonObjectOrArray, Class<?> typeClass, Type type, String jsonKey) { 179 JSONArray jsonArray = null; 180 if (StrUtil.isBlank(jsonKey)) { 181 if (rawJsonObjectOrArray instanceof JSONArray) { 182 jsonArray = (JSONArray) rawJsonObjectOrArray; 183 } 184 } else { 185 if (rawJsonObjectOrArray instanceof JSONObject) { 186 JSONObject rawObject = (JSONObject) rawJsonObjectOrArray; 187 String[] keys = jsonKey.split("\\."); 188 for (int i = 0; i < keys.length; i++) { 189 if (rawObject == null || rawObject.isEmpty()) { 190 break; 191 } 192 String key = keys[i].trim(); 193 if (StrUtil.isNotBlank(key)) { 194 //the last 195 if (i == keys.length - 1) { 196 if (key.endsWith(endOfArray) && key.contains(startOfArray)) { 197 String realKey = key.substring(0, key.indexOf(startOfArray)); 198 JSONArray jarray = rawObject.getJSONArray(realKey.trim()); 199 if (jarray == null || jarray.isEmpty()) { 200 return null; 201 } 202 String subKey = key.substring(key.indexOf(startOfArray) + 1, key.length() - 1).trim(); 203 if (StrUtil.isBlank(subKey)) { 204 throw new IllegalStateException("Sub key can not empty: " + jsonKey); 205 } 206 207 JSONArray newJsonArray = new JSONArray(); 208 for (int j = 0; j < jarray.size(); j++) { 209 Object value = jarray.getJSONObject(j).get(subKey); 210 if (value != null) { 211 newJsonArray.add(value); 212 } 213 } 214 jsonArray = newJsonArray; 215 } else { 216 jsonArray = rawObject.getJSONArray(key); 217 } 218 } 219 //not last 220 else { 221 rawObject = getJSONObjectByKey(rawObject, key); 222 } 223 } 224 } 225 } 226 } 227 228 if (jsonArray == null || jsonArray.isEmpty()) { 229 return null; 230 } 231 232 //非泛型 set 233 if ((typeClass == Set.class || typeClass == HashSet.class) && typeClass == type) { 234 return new HashSet<>(jsonArray); 235 } 236 237 //直接获取 JsonArray 238 if (typeClass == type && typeClass == JSONArray.class) { 239 return jsonArray; 240 } 241 242 return jsonArray.toJavaObject(type); 243 } 244 245 246 private static JSONObject getJSONObjectByKey(JSONObject jsonObject, String key) { 247 if (key.endsWith(endOfArray) && key.contains(startOfArray)) { 248 String realKey = key.substring(0, key.indexOf(startOfArray)); 249 JSONArray jarray = jsonObject.getJSONArray(realKey.trim()); 250 if (jarray == null || jarray.isEmpty()) { 251 return null; 252 } 253 String arrayString = key.substring(key.indexOf(startOfArray) + 1, key.length() - 1); 254 int arrayIndex = StrUtil.isBlank(arrayString) ? 0 : Integer.parseInt(arrayString.trim()); 255 return arrayIndex >= jarray.size() ? null : jarray.getJSONObject(arrayIndex); 256 } else { 257 return jsonObject.getJSONObject(key); 258 } 259 } 260 261 262 private static Object toJavaObject(JSONObject rawObject, Class<?> paraClass, Type paraType) throws IllegalAccessException, InstantiationException { 263 if (rawObject.isEmpty()) { 264 return paraClass.isPrimitive() ? ObjectUtil.getPrimitiveDefaultValue(paraClass) : null; 265 } 266 267 //非泛型 的 map 268 if ((paraClass == Map.class || paraClass == JSONObject.class) && paraClass == paraType) { 269 return rawObject; 270 } 271 272 //非泛型 的 map 273 if (Map.class.isAssignableFrom(paraClass) && paraClass == paraType && canNewInstance(paraClass)) { 274 Map map = (Map) paraClass.newInstance(); 275 map.putAll(rawObject); 276 return map; 277 } 278 279 return rawObject.toJavaObject(paraType); 280 } 281 282 283 private static boolean canNewInstance(Class<?> clazz) { 284 int modifiers = clazz.getModifiers(); 285 return !Modifier.isAbstract(modifiers) && !Modifier.isInterface(modifiers); 286 } 287 288 289 @Override 290 public void build(Class<?> targetClass, Method method, Interceptors interceptors) { 291 if (Util.isController(targetClass)) { 292 Parameter[] parameters = method.getParameters(); 293 if (parameters != null && parameters.length > 0) { 294 for (Parameter p : parameters) { 295 if (p.getAnnotation(JsonBody.class) != null) { 296 Class<?> typeClass = p.getType(); 297 if ((Map.class.isAssignableFrom(typeClass) || Collection.class.isAssignableFrom(typeClass) || typeClass.isArray()) 298 && !JbootController.class.isAssignableFrom(targetClass)) { 299 throw new IllegalArgumentException("Can not use @JsonBody for Map/List(Collection)/Array type if your controller not extends JbootController, method: " + ClassUtil.buildMethodString(method)); 300 } 301 302 interceptors.addIfNotExist(this); 303 return; 304 } 305 } 306 } 307 } 308 } 309}