如何优雅的书写Controller模块,别老是在业务逻辑上琢磨,也看看基础的框架代码实现

大家好,我是程序员大猩猩。

很多时候,我们后端开发80%的工作都是在实践业务上不停地敲代码,而有些相对来说的基础的东西却无法掌握。

举个例子:我这有一个轮子,你给我仿照一下造一个新的轮子,这对于我们有模仿、抄袭基因的人来说很容易。但是当没有这个轮子,让你新造一个轮子的时候,就没有那么简单了。

所以在我们业务需求不忙的时候,或者在忙于增删改查时,我们抽出来几分钟的时候,看看轮子到底是怎么造的,我们怎么也能自己造轮子。

相信开发对写一个Controller,想想那肯定是驾轻就熟,很简单吗?我们来看看代码:

@RequestMapping(path = "/userInfo", method = RequestMethod.POST)
public Map<String, Object> getUserInfo(@RequestBody UserInfoCnd cnd) {
    return userService.inviteRegister(cnd);
}

@RequestMapping(path = "/changePwd", method = RequestMethod.POST)
public Integer changePwd(@RequestBody ChangePwdCnd cnd) {
    return userService.changePwd(cnd);
}

@RequestMapping(path = "/changeInfo", method = RequestMethod.POST)
public String changeInfo(Long userId, String userName) {
    try {
        return userService.changeInfo(userId, userName);
    } catch (Exception ex) {
        log.error("error", ex);
        return ex.getMessage();
    }
}

我们来看看上面的代码块,它犯了哪些不优雅的说不上错误的错误呢?

一、返回格式不统一

上面三个接口分别返回了Map、Integer、还有String,Map返回value还是Object类型,根本没有可读性。

二、没有考虑错误的返回,虽然changeInfo看似在异常快捕获后,返回了错误信息字符串,但是我们想一想,这样的返回并不符合我的规则,前端对接起来,是不是很困难。要处理单独的逻辑呢?

三、参数性质不一,前面俩个方式是整体的Body传入,而后一个是属性字段传入,我们其实需要和上面二个接口方法一样,必须闯入Bean类。

四、接口传参可以进行前期判断,加入@RequestBody @Valid然后在入参Bean内传入相关的基础判断拦截。

那么我们该如何正确的书写一段Controller呢?

我们针对以上四点问题进行一一优化

一、统一返回格式

首先,我们写一个统一的返回类型Bean,支持泛型及支持中英文语言资源切换

@Data
@Component
public class ResponseData<T> implements Serializable {

    private static final long serialVersionUID = -6936648847780505144L;

    /**
     * 狀態碼
     */
    public Integer code;

    /**
     * 返回的消息
     */
    public String message;

    /**
     * 返回的數據
     */
    public T data;


    @Autowired
    private static ResponseData responseData;

    /**
     * 支持中英文资源获取
     */
    @JsonIgnore
    @Autowired
    private MessageSource messageSource;

    @PostConstruct
    public void init() {
        responseData = this;
        responseData.messageSource = this.messageSource;
    }

    public ResponseData() {
    }

    public ResponseData(Integer code, String message, T data) {
        this.code = code;
        this.message = message;

        this.data = data;
    }
}

返回实体Bean确定后,我们来书写一下基础的方法类,表示成功与失败的返回结果体。

public static <T> ResponseData<T> success() {
    return success(ResultStatusEnum.SYSTEM_SUC.getCode(), ResultStatusEnum.SYSTEM_SUC.getMsg(), null);
}

public static <T> ResponseData<T> success(T data) {
    return success(ResultStatusEnum.SYSTEM_SUC.getCode(), ResultStatusEnum.SYSTEM_SUC.getMsg(), data);
}
public static <T> ResponseData<T> success(Integer code, String message, T data) {
    return build(code, message, data);
}

public static <T> ResponseData<T> error(ResultStatusEnum status, String message) {
    if (message == null || message.isEmpty()) {
        message = status.getMsg();
    }
    Integer code = status.getCode();
    List<String> split = Splitter.on("&").splitToList(message);
    if (split.size() > 1) {
        String codeInteger = split.get(0);
        if (isInteger(codeInteger)) {
            code = Integer.parseInt(codeInteger);
            message = StrUtil.replace(message, codeInteger + "&", "");
        }
    }
    return error(code, message, null);
}

public static <T> ResponseData<T> error(Integer code, String message) {
    return error(code, message, null);
}

public static <T> ResponseData<T> error(Integer code, String message, T data) {
    return build(code, message, data);
}

public static <T> ResponseData<T> build(ResultStatusEnum status) {
    return build(status.getCode(), status.getMsg(), null);
}

public static <T> ResponseData<T> build(ResultStatusEnum status, T data) {
    return build(status.getCode(), status.getMsg(), data);
}

public static <T> ResponseData<T> build(Integer code, String message, T data) {
    return new <T>ResponseData<T>(code, getMsgValue(message), data);
}
/**
   * <b>功能描述:</b>查詢國際化配置信息<br>
   * <b>修訂記錄:</b><br>
   * <li>20201009&nbsp;&nbsp;|&nbsp;&nbsp;HMBB&nbsp;&nbsp;|&nbsp;&nbsp;創建方法</li><br>
   */
  private static String getMsgValue(String msg) {
      if (isContainChinese(msg)) {
          return msg;
      }
      if (msg == null || msg.isEmpty()) {
          return "failed";
      }
      try {
          Locale LOCALE = LocaleContextHolder.getLocale();
          if (LOCALE.equals(Locale.ENGLISH)) {
              LOCALE = Locale.US;
          }
          if (LOCALE.equals(Locale.CHINESE)) {
              LOCALE = Locale.SIMPLIFIED_CHINESE;
          }
          if (LOCALE.equals(Locale.TRADITIONAL_CHINESE)) {
              LOCALE = Locale.TRADITIONAL_CHINESE;
          }
          String value = responseData.messageSource.getMessage(msg, null, LOCALE);
          if (value == null) {
              return msg;
          }
          return value;
      } catch (Exception ex) {
          return msg;
      }
  }

返回类创建后,我们就可以在Controller类中使用统一使用ResponseData了

二、如何处理异常返回

统一异常,我们不在Controller类中使用,查看我之前的文章,可以操作。

如何在微服务代码中优雅的处理异常 | 全局异常的实现方式

统一异常在于我们不管在services模块,还是在内部哪里,捕获异常或者抛出异常时,所有的异常捕获都在@ControllerAdvice内。

三、参数性质不一,我们讲多个或者一个参数属性,都使用Bean包装传入即可。

四、接口传参可以进行前期判断

@RequestMapping(path = "/inviteRegister", method = RequestMethod.POST)
public ResponseData<Integer> inviteRegister(@RequestBody @Valid InviteRegisterCnd cnd) throws Exception {
    return ResponseData.success(userService.inviteRegister(cnd));
}

如上接口参数内

@RequestBody @Valid InviteRegisterCnd cnd

@RequestBody 这个不必说了。

@Valid 加入这个注解参数的主要作用是用于数据校验,可以在定义的实体中的属性上,添加不同的注解来完成不同的校验规则,而在接口类中的接收数据参数中添加 @valid 注解,这时你的实体将会开启一个校验的功能。

@Data
public class InviteRegisterCnd implements Serializable {

    private static final long serialVersionUID = 4061633295778293743L;

    /**
     * 手机号
     */
    @ApiModelProperty(value = "手机号", required = true)
    @NotNull(message = "手机号不可为空")
    @NotBlank(message = "手机号不可为空")
    @Pattern(regexp = "1[3|4|5|6|7|8|9][0-9]\\d{8}", message = "手机号码格式错误")
    private String phone;

    /**
     * 短信验证码
     */
    @ApiModelProperty(value = "短信验证码", required = true)
    @NotNull(message = "短信验证码不可为空")
    @NotBlank(message = "短信验证码不可为空")
    private String smsCode;
}如上InviteRegisterCnd内的属性字段 @NotNull @NotBlank @Pattern的校验注解都可以起作用了。

最后,其实我们写代码用什么方法,只要功能正常就可以,这是最基础的要求。但是我们在开发工程中加入自己的思维和想法,那么我们写的代码就是有效的工作和经验。

猜你喜欢

转载自blog.csdn.net/t610654893/article/details/139063371