You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成Laravel登录API:如何获取并展示各类响应信息

Flutter对接Laravel登录API:精准捕获响应与错误处理

问题背景

作为Flutter新手,我正在用dio开发认证应用,Laravel登录API已通过Postman测试正常,但不知道如何对接实现以下需求:

  • 捕获服务器返回的各类错误(邮箱不存在、密码错误、邮箱未验证等)并展示在客户端UI
  • 获取登录成功的信息并处理
    目前Auth Service已配置JSON请求头和API地址,想知道如何精准获取这些响应信息,是否需要修改现有代码。

后端Laravel登录接口代码

public function login(Request $request) {
    // Validate the input data
    $validated = $request->validate([
        'email' => 'required|string|email|max:255',
        'password' => 'required|string|min:8',
    ]);

    // Find the user by email
    $user = User::where('email', $validated['email'])->first();

    // User not found, return a 404 with specific error message
    if (!$user) {
        return response()->json([
            'message' => 'User not found.',
            'type' => 'error',  // Indicating it's an error
        ], 404);
    }

    // Check if the password is correct
    if (!Hash::check($validated['password'], $user->password)) {
        return response()->json([
            'message' => 'Please check your password.',
            'type' => 'error',  // Indicating it's an error
        ], 401);
    }

    // Check if the user is verified
    if (!$user->email_verified_at) {
        // User is not verified, resend the verification code
        $verificationCode = random_int(100000, 999999);
        $user->verification_code = $verificationCode;
        $user->save();

        // Resend the verification email
        Mail::to($user->email)->send(new VerifyEmailMail($verificationCode));

        // Return a 400 with informational message
        return response()->json([
            'message' => 'Please verify your email.',
            'type' => 'info',  // Informational message
        ], 400);
    }

    // If the user is verified, generate an API token
    $token = $user->createToken('auth_token')->plainTextToken;

    // Return 200 (OK) with success message, token, and user data
    return response()->json([
        'message' => 'Login successful!.',
        'type' => 'success',  // Success message
        'token' => $token,
        'user' => $user,
    ], 200);  // OK
}

现有Flutter登录请求代码

//flutter code is here
Future<bool> login(String email, String password) async { 
    try { 
        final response = await _dio.post( loginUrl, data: json.encode({ 'email': email, 'password': password, }), );

      if (response.statusCode == 200) {
        final token = response.data['token'];
        await _secureStorage.write(key: 'auth_token', value: token);
        return true; // Successfully logged in
      } else {
        return false; // Something went wrong with login, no errors returned
      }
    } catch (e) {
      if (e is DioException && e.response != null) {
        // Check for validation errors (422)
        if (e.response?.statusCode == 422) {
          // Returning the errors as a map
          throw ValidationException(e.response?.data['errors']);
        }
      }
      print('Login failed: $e');
      return false; // Return false for other types of errors
    }

}

修改方案与实现

现有代码仅返回bool类型,无法携带错误详情,必须修改逻辑才能精准捕获响应信息。以下是具体实现步骤:

1. 定义自定义异常类

用于区分不同类型的认证错误,方便UI层处理:

class AuthException implements Exception {
  final String message;
  final String type;

  AuthException(this.message, this.type);
}

// 保留原有的表单验证异常类
class ValidationException implements Exception {
  final Map<String, dynamic> errors;

  ValidationException(this.errors);
}

2. 重构登录方法

修改返回逻辑,抛出具体异常(更符合Flutter错误处理习惯):

Future<void> login(String email, String password) async {
  try {
    // Dio可自动将Map转为JSON,无需手动json.encode
    final response = await _dio.post(
      loginUrl,
      data: {'email': email, 'password': password},
    );

    // 处理成功响应
    if (response.statusCode == 200) {
      final token = response.data['token'];
      await _secureStorage.write(key: 'auth_token', value: token);
      return;
    }

    // 处理非200但未抛出异常的情况(极少出现)
    final responseData = response.data;
    throw AuthException(
      responseData['message'] ?? '登录失败',
      responseData['type'] ?? 'error',
    );
  } on DioException catch (e) {
    if (e.response != null) {
      final responseData = e.response!.data;
      final message = responseData['message'] ?? '登录失败';
      final type = responseData['type'] ?? 'error';

      // 处理表单验证错误(422)
      if (e.response!.statusCode == 422) {
        throw ValidationException(responseData['errors']);
      }
      // 处理其他认证错误(404/401/400等)
      throw AuthException(message, type);
    }
    // 处理无响应的网络错误
    throw AuthException('网络连接失败', 'error');
  } catch (e) {
    // 处理未知错误
    throw AuthException('未知错误', 'error');
  }
}

3. UI层调用与错误展示

在登录页面的交互逻辑中捕获异常,根据不同异常类型展示对应信息:

void _onLoginPressed() async {
  final email = _emailController.text.trim();
  final password = _passwordController.text.trim();

  try {
    await _authService.login(email, password);
    // 登录成功:展示提示并跳转首页
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('登录成功')),
    );
    Navigator.pushReplacementNamed(context, '/home');
  } on ValidationException catch (e) {
    // 表单验证错误:拼接错误信息展示
    String errorMsg = '';
    e.errors.forEach((key, value) {
      errorMsg += '$value\n';
    });
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(errorMsg.trim())),
    );
  } on AuthException catch (e) {
    // 认证错误:展示对应提示
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(e.message)),
    );
    // 邮箱未验证时,可跳转验证页面
    if (e.type == 'info') {
      Navigator.pushNamed(context, '/verify-email');
    }
  } catch (e) {
    // 未知错误:通用提示
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('登录失败,请稍后重试')),
    );
  }
}

关键说明

  • Dio特性:Dio默认会将非2xx状态码的响应抛出DioException,因此大部分错误会进入catch (e)块,而非原代码的else分支。
  • 响应解析:后端返回的message和type字段可直接用于区分错误类型,无需额外判断状态码(状态码可作为辅助判断)。
  • 数据传递:避免用bool作为返回值,改用异常或包含详细信息的对象,让UI层能精准处理不同场景。

内容的提问来源于stack exchange,提问作者Cerline Jeremy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:03:12