优化登录接口设计:摆脱 Code 和 Message 的困扰

3,076 阅读4分钟

传统的登录接口设计常常会遇到一些问题,例如容易混淆的状态码、模块间的耦合和不清晰的返回值。本文将通过一个具体例子探讨这些问题,然后提出一种优化后的登录接口设计方案,以解决这些问题。

基于 Code 和 Message 的设计存在的问题

假设我们有一个基于 code 和 message 的登录接口设计。当用户尝试登录时,可能会遇到以下几种情况:

  1. 登录成功
  2. 邮箱格式错误
  3. 密码格式错误
  4. 邮箱未注册
  5. 密码错误

这些情况的返回值可能是这样的:

{
    "status": 200,
    "message": "登录成功!",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRlc3QgVXNlciIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}

{
    "status": 400,
    "message": "邮箱格式错误!"
}

{
    "status": 400,
    "message": "密码格式错误!"
}

{
    "status": 404,
    "message": "邮箱未注册!"
}
{
    "status": 401,
    "message": "密码错误!"
}

这个设计存在以下缺点:

  1. 容易混淆的状态码:在这个设计中,status 可能与 HTTP 状态码混淆。开发人员需要查阅文档或代码才能弄清楚这个 status 到底代表什么含义。
  2. 模块间的耦合status 的值往往是基于整个项目定义的通用值,各个模块都在用,这可能会造成模块间的耦合。当需要修改某个状态码时,可能需要在多个地方进行更改。
  3. 不清晰的返回值:对于一些接口,只需要返回一个简单的布尔值(true 或 false)。然而,在基于 code 和 message 的设计中,我们不得不返回一个包含 code 和 message 的复杂对象。

优化后的登录接口设计方案

为了解决上述问题,我们可以采取以下优化措施:

1. 使用枚举类型表示登录结果

首先,我们需要区分登录成功和登录失败的情况。我们可以使用枚举类型表示登录结果,例如:

public enum LoginResult {
    SUCCESS, // 登录成功
    INVALID_EMAIL, // 邮箱格式不正确
    INVALID_PASSWORD, // 密码格式不正确
    EMAIL_NOT_FOUND, // 邮箱未注册
    WRONG_PASSWORD // 密码错误

2. 定义登录成功时的返回值类型

接下来,我们需要考虑登录成功时需要返回的信息。根据题目要求,我们需要返回一个包含 Token 和用户信息的对象。因此,我们可以定义一个名为 LoginSuccessResponse 的类来表示登录成功时的返回值:

class LoginSuccessResponse { 
    final String token; // JWT Token 
    final String email; // 用户邮箱 
    final String nickname; // 用户昵称 
    LoginSuccessResponse({ 
        required this.token, 
        required this.email, 
        required this.nickname, 
    }); 
}

3. 定义统一的返回值类型

接着,我们需要将登录结果和返回值组合成一个统一的返回值类型。我们可以定义一个名为 LoginResponse 的类来表示登录接口的返回值:

class LoginResponse {
  final LoginResult result; // 登录结果
  final LoginSuccessResponse? successResponse; // 登录成功时的返回值

  LoginResponse({
    required this.result,
    this.successResponse,
  });
}

4. 实现登录函数

最后,我们需要定义一个登录函数,它将接收用户输入的登录信息,并返回一个 LoginResponse 对象。函数的实现应该先对输入数据进行验证,然后再进行用户认证。以下是一个简单的登录函数的示例代码:

class AuthService {
  LoginResponse login(String email, String password) {
    // 验证邮箱格式是否正确
    if (!isValidEmail(email)) {
      return LoginResponse(result: LoginResult.INVALID_EMAIL);
    }

    // 验证密码格式是否正确
    if (!isValidPassword(password)) {
      return LoginResponse(result: LoginResult.INVALID_PASSWORD);
    }

    // 查询用户是否存在
    final user = userRepository.findByEmail(email);
    if (user == null) {
      return LoginResponse(result: LoginResult.EMAIL_NOT_FOUND);
    }

    // 验证密码是否正确
    if (!passwordEncoder.matches(password, user.password)) {
      return LoginResponse(result: LoginResult.WRONG_PASSWORD);
    }

    // 生成JWT Token
    final token = jwtTokenUtil.generateToken(user);

    // 返回登录成功的响应
    final successResponse = LoginSuccessResponse(
      token: token,
      email: user.email,
      nickname: user.nickname,
    );
    return LoginResponse(result: LoginResult.SUCCESS, successResponse: successResponse);
  }
}

优化后的好处

通过以上优化,我们可以获得更清晰、易于维护的登录接口设计。具体的好处有:

  1. 清晰的错误原因:通过使用枚举类型表示登录结果,我们可以明确地知道错误的原因,而不是依赖通用的 code 和 message。

  2. 减少模块间的耦合:每个模块都有自己独立的枚举类型,这有助于减少模块间的耦合。当需要修改某个状态时,只需更改相应模块的枚举类型即可,无需修改其他模块的代码。

  3. 简化返回值:针对不同的结果,我们可以自定义对应的返回值类型,使其更加简洁和直观。对于只需要返回一个简单的布尔值的接口,我们可以直接返回 true 或 false,而无需返回一个包含 code 和 message 的复杂对象。

  4. 易于维护:优化后的设计更容易理解,也更易于维护。在需要修改或扩展功能时,开发人员可以更快地定位到相关代码,从而提高开发效率。

  5. 提高代码可读性:优化后的设计可以提高代码的可读性,让其他开发人员更容易理解代码的逻辑和功能。

综上所述,优化后的登录接口设计可以帮助我们摆脱 code 和 message 的困扰,提高开发效率和降低维护成本。通过这个例子,我们希望能给其他开发人员带来一些启发,以实现更加清晰、易于维护的 API 设计。