1 请求方式
在定义一个Rest接口时通常会利用GET、POST、PUT、DELETE来实现数据的增删改查;这几种方式有的需要传递参数,后台开发人员必须对接收到的参数进行参数验证来确保程序的健壮性
1.1 GET
一般用于查询数据,采用明文进行传输,一般用来获取一些无关用户信息的数据
1.2 POST
一般用于插入数据
1.3 PUT
一般用于数据更新
1.4 DELETE
一般用于数据删除
技巧01:一般都是进行逻辑删除(即:仅仅改变记录的状态,而并非真正的删除数据)
2 参数获取注解
2.1 @PathVariable
路径参数,形如 url/{param} 时会用到,我们可以通过该注解来获取路径后面的参数来进行GET、DELETE、PUT操作
@GetMapping(value = "/{id}") public ResultViewModel findGirlById( @PathVariable("id") String id ) { // 01 参数去空格 id = StringUtils.trim(id); // 02 判断参数是否全为数字 Integer girlId = 0; if (JudgeUtil.allIsNumber(id)) { girlId = Integer.parseInt(id); } else { throw new GirlException(GirlEnum.PARAM_NOT_ALL_NUMBER); } // 03 调用服务层获取数据 return ResultViewModelUtil.success(girlService.findGirlById(girlId)); }
2.2 @RequestParam
用来获取多个参数,常用语POST、PUT操作
2.3 @RequestBody
利用一个对象去获取前端传过来的数据
@PutMapping public ResultViewModel updateOneGirl( @Valid @RequestBody GirlFormModel girlFormModel, BindingResult result ) { // 01 参数验证 if (result.hasErrors()) { // 0101 存储错误信息的字符串变量 StringBuffer msgBuffer = new StringBuffer(); // 0102 错误字段集合 List<FieldError> fieldErrors = result.getFieldErrors(); for (FieldError fieldError : fieldErrors) { // 0103 获取错误信息 msgBuffer.append(fieldError.getField() + ":" + fieldError.getDefaultMessage()); } // 0104 抛出错误信息 throw new GirlException(GirlEnum.PARAM_ERROR.getCode(), msgBuffer.toString()); } // 02 将表单对象转化成数据对象 GirlModel girlModel = new GirlModel(); BeanUtils.copyProperties(girlFormModel, girlModel); // 03 调用服务层进行更新操作 Boolean updateResult = girlService.updateGirl(girlModel); if (updateResult) { return ResultViewModelUtil.success(GirlEnum.UPDATE_SUCCESS.getMessage()); } else { return ResultViewModelUtil.error(GirlEnum.UPDATE_ERROR.getCode(), GirlEnum.UPDATE_ERROR.getMessage()); } }
3 参数验证注解
后台单独对接收到的参数进行验证时比较麻烦,springboot项目的web组件集成了hibernate-validator,开发者可以直接使用hibernate-validator提供的注解对数据进行校验,当有一些复杂的参数校验时我们也可以自定义校验注解
技巧01:接收到的参数默认都是字符串类型的
坑01:有的注解只能用在String类型的属性上
3.1 常用的验证注解有
技巧01:@JsonProperty可以实现前端的属性名和后台实体类的属性名不一致问题,例如
代码解释:前端传过来的属性名为nick,后台接收的属性名为nickname
3.2 使用方法
直接在表单实体类的属性上面添加相应的注解就可以啦
package cn.xiangxu.springboottest.model.dataFormModel; import cn.xiangxu.springboottest.commons.validators.GirlFormIdValidator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import org.hibernate.validator.constraints.NotBlank; import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.Min; @Data public class GirlFormModel { // @NotBlank(message = "目标girl的ID不能为空") // @NotEmpty(message = "目标girl的ID不能为空哟") // @GirlFormIdValidator private Integer girlId; @Min(value = 123) private Integer age; private String name; private String password; @NotEmpty(message = "昵称不能为空") @NotBlank(message = "昵称不能为空哟") @JsonProperty("nick") // 当前端属性为nick后台接收对象的属性为nickName时可以用@JsonProperty来保持一致 private String nickname; @NotEmpty(message = "地址不能为空") @NotBlank(message = "地址不能为空哟") private String address; }
3.3 如何使参数校验生效
3.3.1 在控制层方法的形参前面添加@Valid注解
3.3.2 利用BindingResult对象获取参数错误字段和参数错误信息
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.validation; import java.beans.PropertyEditor; import java.util.Map; import org.springframework.beans.PropertyEditorRegistry; public interface BindingResult extends Errors { String MODEL_KEY_PREFIX = BindingResult.class.getName() + "."; Object getTarget(); Map<String, Object> getModel(); Object getRawFieldValue(String var1); PropertyEditor findEditor(String var1, Class<?> var2); PropertyEditorRegistry getPropertyEditorRegistry(); void addError(ObjectError var1); String[] resolveMessageCodes(String var1); String[] resolveMessageCodes(String var1, String var2); void recordSuppressedField(String var1); String[] getSuppressedFields(); }
技巧01:利用BindingResult对象的hasErrors方法判断是否有参数错误
· 技巧02:利用BindingResult对象的getFieldErrors方法获取所有有参数错误的属性
技巧03:利用错误属性对象的getDefaultMessage去获取错误提示信息
4 自定义参数验证注解
4.1 定义一个注解接口
package cn.xiangxu.springboottest.commons.validators; import cn.xiangxu.springboottest.commons.validators.validatorClass.GirlFormValidatorClass; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD}) @Constraint(validatedBy = GirlFormValidatorClass.class) public @interface GirlFormIdValidator { String values(); String message() default "girl的ID必须为纯数字"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
4.2 定义一个注解接口实现类
技巧01:必须实现ConstraintValidator接口
package cn.xiangxu.springboottest.commons.validators.validatorClass; import cn.xiangxu.springboottest.commons.validators.GirlFormIdValidator; import cn.xiangxu.springboottest.utils.JudgeUtil; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class GirlFormValidatorClass implements ConstraintValidator<GirlFormIdValidator, Object> { private String values; @Override public void initialize(GirlFormIdValidator girlFormValidator) { this.values = girlFormValidator.values(); } @Override public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { if (JudgeUtil.allIsNumber((String)value)) { return true; } return false; } }
4.3 在实体类中使用自定义的参数校验注解
5 BeanUtils的使用
利用BeanUtils提供的copyProperties可以实现实体类数据的复制
坑01:只用属性名和属性类型一致的部分才可以进行复制操作,例如
BeanUtils.copyProperties(girlFormModel, girlModel);
代码解释:将girlFormModel对象复制给girlModel对象
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.beans; import java.beans.PropertyDescriptor; import java.beans.PropertyEditor; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.MethodParameter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; public abstract class BeanUtils { private static final Log logger = LogFactory.getLog(BeanUtils.class); private static final Set<Class<?>> unknownEditorTypes = Collections.newSetFromMap(new ConcurrentReferenceHashMap(64)); public BeanUtils() { } public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } else { try { return clazz.newInstance(); } catch (InstantiationException var2) { throw new BeanInstantiationException(clazz, "Is it an abstract class?", var2); } catch (IllegalAccessException var3) { throw new BeanInstantiationException(clazz, "Is the constructor accessible?", var3); } } } public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } else { try { return instantiateClass(clazz.getDeclaredConstructor()); } catch (NoSuchMethodException var2) { throw new BeanInstantiationException(clazz, "No default constructor found", var2); } } } public static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo) throws BeanInstantiationException { Assert.isAssignable(assignableTo, clazz); return instantiateClass(clazz); } public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException { Assert.notNull(ctor, "Constructor must not be null"); try { ReflectionUtils.makeAccessible(ctor); return ctor.newInstance(args); } catch (InstantiationException var3) { throw new BeanInstantiationException(ctor, "Is it an abstract class?", var3); } catch (IllegalAccessException var4) { throw new BeanInstantiationException(ctor, "Is the constructor accessible?", var4); } catch (IllegalArgumentException var5) { throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", var5); } catch (InvocationTargetException var6) { throw new BeanInstantiationException(ctor, "Constructor threw exception", var6.getTargetException()); } } public static Method findMethod(Class<?> clazz, String methodName, Class... paramTypes) { try { return clazz.getMethod(methodName, paramTypes); } catch (NoSuchMethodException var4) { return findDeclaredMethod(clazz, methodName, paramTypes); } } public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class... paramTypes) { try { return clazz.getDeclaredMethod(methodName, paramTypes); } catch (NoSuchMethodException var4) { return clazz.getSuperclass() != null ? findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes) : null; } } public static Method findMethodWithMinimalParameters(Class<?> clazz, String methodName) throws IllegalArgumentException { Method targetMethod = findMethodWithMinimalParameters(clazz.getMethods(), methodName); if (targetMethod == null) { targetMethod = findDeclaredMethodWithMinimalParameters(clazz, methodName); } return targetMethod; } public static Method findDeclaredMethodWithMinimalParameters(Class<?> clazz, String methodName) throws IllegalArgumentException { Method targetMethod = findMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName); if (targetMethod == null && clazz.getSuperclass() != null) { targetMethod = findDeclaredMethodWithMinimalParameters(clazz.getSuperclass(), methodName); } return targetMethod; } public static Method findMethodWithMinimalParameters(Method[] methods, String methodName) throws IllegalArgumentException { Method targetMethod = null; int numMethodsFoundWithCurrentMinimumArgs = 0; Method[] var4 = methods; int var5 = methods.length; for(int var6 = 0; var6 < var5; ++var6) { Method method = var4[var6]; if (method.getName().equals(methodName)) { int numParams = method.getParameterTypes().length; if (targetMethod != null && numParams >= targetMethod.getParameterTypes().length) { if (!method.isBridge() && targetMethod.getParameterTypes().length == numParams) { if (targetMethod.isBridge()) { targetMethod = method; } else { ++numMethodsFoundWithCurrentMinimumArgs; } } } else { targetMethod = method; numMethodsFoundWithCurrentMinimumArgs = 1; } } } if (numMethodsFoundWithCurrentMinimumArgs > 1) { throw new IllegalArgumentException("Cannot resolve method ‘" + methodName + "‘ to a unique method. Attempted to resolve to overloaded method with the least number of parameters but there were " + numMethodsFoundWithCurrentMinimumArgs + " candidates."); } else { return targetMethod; } } public static Method resolveSignature(String signature, Class<?> clazz) { Assert.hasText(signature, "‘signature‘ must not be empty"); Assert.notNull(clazz, "Class must not be null"); int firstParen = signature.indexOf("("); int lastParen = signature.indexOf(")"); if (firstParen > -1 && lastParen == -1) { throw new IllegalArgumentException("Invalid method signature ‘" + signature + "‘: expected closing ‘)‘ for args list"); } else if (lastParen > -1 && firstParen == -1) { throw new IllegalArgumentException("Invalid method signature ‘" + signature + "‘: expected opening ‘(‘ for args list"); } else if (firstParen == -1 && lastParen == -1) { return findMethodWithMinimalParameters(clazz, signature); } else { String methodName = signature.substring(0, firstParen); String[] parameterTypeNames = StringUtils.commaDelimitedListToStringArray(signature.substring(firstParen + 1, lastParen)); Class<?>[] parameterTypes = new Class[parameterTypeNames.length]; for(int i = 0; i < parameterTypeNames.length; ++i) { String parameterTypeName = parameterTypeNames[i].trim(); try { parameterTypes[i] = ClassUtils.forName(parameterTypeName, clazz.getClassLoader()); } catch (Throwable var10) { throw new IllegalArgumentException("Invalid method signature: unable to resolve type [" + parameterTypeName + "] for argument " + i + ". Root cause: " + var10); } } return findMethod(clazz, methodName, parameterTypes); } } public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException { CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); return cr.getPropertyDescriptors(); } public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName) throws BeansException { CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz); return cr.getPropertyDescriptor(propertyName); } public static PropertyDescriptor findPropertyForMethod(Method method) throws BeansException { return findPropertyForMethod(method, method.getDeclaringClass()); } public static PropertyDescriptor findPropertyForMethod(Method method, Class<?> clazz) throws BeansException { Assert.notNull(method, "Method must not be null"); PropertyDescriptor[] pds = getPropertyDescriptors(clazz); PropertyDescriptor[] var3 = pds; int var4 = pds.length; for(int var5 = 0; var5 < var4; ++var5) { PropertyDescriptor pd = var3[var5]; if (method.equals(pd.getReadMethod()) || method.equals(pd.getWriteMethod())) { return pd; } } return null; } public static PropertyEditor findEditorByConvention(Class<?> targetType) { if (targetType != null && !targetType.isArray() && !unknownEditorTypes.contains(targetType)) { ClassLoader cl = targetType.getClassLoader(); if (cl == null) { try { cl = ClassLoader.getSystemClassLoader(); if (cl == null) { return null; } } catch (Throwable var5) { if (logger.isDebugEnabled()) { logger.debug("Could not access system ClassLoader: " + var5); } return null; } } String editorName = targetType.getName() + "Editor"; try { Class<?> editorClass = cl.loadClass(editorName); if (!PropertyEditor.class.isAssignableFrom(editorClass)) { if (logger.isWarnEnabled()) { logger.warn("Editor class [" + editorName + "] does not implement [java.beans.PropertyEditor] interface"); } unknownEditorTypes.add(targetType); return null; } else { return (PropertyEditor)instantiateClass(editorClass); } } catch (ClassNotFoundException var4) { if (logger.isDebugEnabled()) { logger.debug("No property editor [" + editorName + "] found for type " + targetType.getName() + " according to ‘Editor‘ suffix convention"); } unknownEditorTypes.add(targetType); return null; } } else { return null; } } public static Class<?> findPropertyType(String propertyName, Class... beanClasses) { if (beanClasses != null) { Class[] var2 = beanClasses; int var3 = beanClasses.length; for(int var4 = 0; var4 < var3; ++var4) { Class<?> beanClass = var2[var4]; PropertyDescriptor pd = getPropertyDescriptor(beanClass, propertyName); if (pd != null) { return pd.getPropertyType(); } } } return Object.class; } public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) { return pd instanceof GenericTypeAwarePropertyDescriptor ? new MethodParameter(((GenericTypeAwarePropertyDescriptor)pd).getWriteMethodParameter()) : new MethodParameter(pd.getWriteMethod(), 0); } public static boolean isSimpleProperty(Class<?> clazz) { Assert.notNull(clazz, "Class must not be null"); return isSimpleValueType(clazz) || clazz.isArray() && isSimpleValueType(clazz.getComponentType()); } public static boolean isSimpleValueType(Class<?> clazz) { return ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() || CharSequence.class.isAssignableFrom(clazz) || Number.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz) || URI.class == clazz || URL.class == clazz || Locale.class == clazz || Class.class == clazz; } public static void copyProperties(Object source, Object target) throws BeansException { copyProperties(source, target, (Class)null, (String[])null); } public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException { copyProperties(source, target, editable, (String[])null); } public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException { copyProperties(source, target, (Class)null, ignoreProperties); } private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null; PropertyDescriptor[] var7 = targetPds; int var8 = targetPds.length; for(int var9 = 0; var9 < var8; ++var9) { PropertyDescriptor targetPd = var7[var9]; Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(target, value); } catch (Throwable var15) { throw new FatalBeanException("Could not copy property ‘" + targetPd.getName() + "‘ from source to target", var15); } } } } } } }