美团CPS分销接口开发中Java请求参数校验的优雅实现技巧
美团CPS分销接口开发中Java请求参数校验的优雅实现技巧在对接美团CPSCost Per Sale分销接口时上游系统或前端传入的参数可能存在缺失、格式错误或业务逻辑不合法等问题。若未做严格校验轻则导致接口异常重则引发佣金计算错误或数据污染。本文结合JSR-380Bean Validation 2.0、自定义注解与分组校验提供一套高内聚、低侵入的参数校验方案。1. 基础DTO与标准注解使用首先定义接收美团回调或调用其OpenAPI的DTO并使用javax.validation注解约束字段packagebaodanbao.com.cn.cps.dto;importjavax.validation.constraints.*;importjava.math.BigDecimal;publicclassMeituanOrderCreateRequest{NotBlank(message商户ID不能为空)privateStringmerchantId;Pattern(regexp^[A-Za-z0-9]{1,32}$,message订单号仅支持字母数字长度1-32)privateStringorderId;NotNull(message用户ID不可为空)Min(value10000,message用户ID需大于10000)privateLonguserId;NotNull(message订单金额不可为空)DecimalMin(value0.01,message订单金额至少0.01元)DecimalMax(value99999.99,message单笔订单金额不得超过99999.99元)privateBigDecimalorderAmount;Email(message通知邮箱格式不正确)privateStringnotifyEmail;// getters setters}2. 分组校验应对多场景复用同一DTO可能用于“创建订单”和“查询订单”两种接口所需字段不同。通过分组实现差异化校验packagebaodanbao.com.cn.cps.groups;publicinterfaceCreateGroup{}publicinterfaceQueryGroup{}在DTO中指定分组publicclassMeituanOrderRequest{NotBlank(groups{CreateGroup.class,QueryGroup.class})privateStringorderId;NotNull(groupsCreateGroup.class)DecimalMin(value0.01,groupsCreateGroup.class)privateBigDecimalamount;Min(value1,groupsQueryGroup.class)privateIntegerpageNum;// ...}Controller中触发对应分组校验packagebaodanbao.com.cn.cps.controller;importbaodanbao.com.cn.cps.dto.MeituanOrderRequest;importbaodanbao.com.cn.cps.groups.CreateGroup;importbaodanbao.com.cn.cps.groups.QueryGroup;importorg.springframework.validation.annotation.Validated;importorg.springframework.web.bind.annotation.*;RestControllerRequestMapping(/meituan/cps)publicclassMeituanCpsController{PostMapping(/order/create)publicStringcreateOrder(Validated(CreateGroup.class)RequestBodyMeituanOrderRequestreq){returnsuccess;}GetMapping(/order/list)publicStringlistOrders(Validated(QueryGroup.class)MeituanOrderRequestreq){returnsuccess;}}3. 自定义业务校验注解美团CPS要求某些字段组合满足特定规则例如“当订单类型为团购时必须包含团购券码”。此时需自定义校验器packagebaodanbao.com.cn.cps.validation;importjavax.validation.Constraint;importjavax.validation.Payload;importjava.lang.annotation.*;Target({ElementType.TYPE})Retention(RetentionPolicy.RUNTIME)Constraint(validatedByCouponCodeValidator.class)DocumentedpublicinterfaceValidCouponIfGroupBuy{Stringmessage()default团购订单必须提供有效券码;Class?[]groups()default{};Class?extendsPayload[]payload()default{};}实现校验逻辑packagebaodanbao.com.cn.cps.validation;importbaodanbao.com.cn.cps.dto.MeituanOrderCreateRequest;importjavax.validation.ConstraintValidator;importjavax.validation.ConstraintValidatorContext;publicclassCouponCodeValidatorimplementsConstraintValidatorValidCouponIfGroupBuy,MeituanOrderCreateRequest{OverridepublicbooleanisValid(MeituanOrderCreateRequestrequest,ConstraintValidatorContextcontext){if(GROUP_BUY.equals(request.getOrderType())){returnrequest.getCouponCode()!null!request.getCouponCode().trim().isEmpty();}returntrue;// 非团购订单无需校验}}在DTO上使用ValidCouponIfGroupBuypublicclassMeituanOrderCreateRequest{privateStringorderType;// NORMAL / GROUP_BUYprivateStringcouponCode;// other fields...}4. 全局异常处理统一返回格式捕获MethodArgumentNotValidException并返回结构化错误信息packagebaodanbao.com.cn.cps.exception;importorg.springframework.http.HttpStatus;importorg.springframework.validation.FieldError;importorg.springframework.web.bind.MethodArgumentNotValidException;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.ResponseStatus;importorg.springframework.web.bind.annotation.RestControllerAdvice;importjava.util.HashMap;importjava.util.Map;RestControllerAdvicepublicclassValidationExceptionHandler{ExceptionHandler(MethodArgumentNotValidException.class)ResponseStatus(HttpStatus.BAD_REQUEST)publicMapString,StringhandleValidationExceptions(MethodArgumentNotValidExceptionex){MapString,StringerrorsnewHashMap();ex.getBindingResult().getAllErrors().forEach((error)-{StringfieldName((FieldError)error).getField();StringerrorMessageerror.getDefaultMessage();errors.put(fieldName,errorMessage);});returnerrors;}}5. 手动触发校验适用于非Controller场景在Service层或消息消费中可手动校验对象packagebaodanbao.com.cn.cps.service;importbaodanbao.com.cn.cps.dto.MeituanOrderCreateRequest;importorg.springframework.stereotype.Service;importjavax.validation.ConstraintViolation;importjavax.validation.Validation;importjavax.validation.Validator;importjavax.validation.ValidatorFactory;importjava.util.Set;ServicepublicclassOrderValidationService{privatefinalValidatorvalidator;publicOrderValidationService(){ValidatorFactoryfactoryValidation.buildDefaultValidatorFactory();this.validatorfactory.getValidator();}publicvoidvalidate(MeituanOrderCreateRequestrequest){SetConstraintViolationMeituanOrderCreateRequestviolationsvalidator.validate(request);if(!violations.isEmpty()){StringBuildersbnewStringBuilder();for(ConstraintViolationMeituanOrderCreateRequestviolation:violations){sb.append(violation.getPropertyPath()).append(: ).append(violation.getMessage()).append(; );}thrownewIllegalArgumentException(sb.toString());}}}本文著作权归 俱美开放平台 转载请注明出处

相关新闻