如何使用服务类来设计Flutter中的认证API

127 阅读5分钟

使用服务类来设计Flutter中的认证API

服务类是一种很好的方法,当从你的代码中的许多地方调用一个API方法时,可以将第三方代码的实现细节排除在你的软件之外。服务类的一个副作用是,它通过你的服务的API产生一个特定于你的应用程序的领域语言,所以你的视图模型可以被读取。

服务类将有助于学习如何封装和隔离第三方库和API。我们将在本教程中使用Firebase认证。

前提条件

在我们开始之前,读者需要具备以下条件。

  • 在他们的机器上安装了Flutter
  • 对Flutter有一些基本的认识和了解。

何时创建服务类

当你在你的应用程序中为它构建API时,你可以创建一个服务类。它将为应用程序的其他部分提供API功能,包括。

  • 从代码库中删除第三方包。
  • 在不同的视图模型之间交换功能。

你可能会开发一个服务的典型场景

  • 当本地存储可以被读写的时候。
  • 当利用网络API的时候。
  • 当创建一个用户帐户时。
  • 当进行一些复杂的计算时。
  • 当用包装器包装firebase或其他第三方包时。

认证状态

认证状态可以通过一个流来订阅,使用FirebaseAuth 。这个流在每次用户的认证状态发生变化时都会产生一个事件,并且会无限期地持续下去。

对于使用服务类作为API封装的终端用户或应用程序来说,没有任何实现细节需要透露。Firebase认证是通过下面的代码设置的。检索一个Firebase认证对象就像调用ProviderFirebaseAuth>(context) 函数一样简单。

import 'package:firebase_auth/firebase_auth.dart';
FirebaseAuth auth = FirebaseAuth.instance;
class Login extends StatelessWidget {
 //sign in activity
  Future<void> _signInAnonymously() async {
    try {
    FirebaseAuth=> Firebase Authentication
      final firebaseAuth = Provider.of<FirebaseAuth>(context);
      await firebaseAuth.signInAnonymously();
    } catch(e) {
      print(e);
    }
  }
}

注意:虽然许多程序不需要用户明确地登录,但单独识别他们是很关键的。

交换偏好、授予访问权、编译数据和要求本地认证的能力是一些最常见的安全功能。可以通过以下方式提高安全性。

  • 验证一个请求是否来自一个已经登录到你的应用程序的用户。
  • 验证该用户是否使用Firebase数据库、实时数据库或外部API。

尽管我们在代码中使用了firebase认证的API,但我们仍然在直接访问它,而这可能会导致几个问题。

  • 首先,你将如何处理Firebase认证的变化,使事情在未来变得更糟。
  • 假设我们决定在将来使用另一个认证服务而不是firebase。我们将不得不对我们的计划做一些改变。

我们的程序对firebase的认证调用将被升级以解决上面的问题。此外,随着我们项目范围的扩大,我们可以预期会在我们的应用程序中添加更多的内容。

创建服务类

你可以使用firebase的API构建一个通用的认证系统。服务类只不过是底层类的克隆。在我们下面的示例代码中,使用Firebase Authentication Service 类演示了firebase认证API的调用。

class TheUser {
  const TheUser({@required this.id});
  final String id;
}
class FirebaseAuthService {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  // It is possible to create a new user by calling the 'FirebaseTheUser' private method "TheUser".
  TheUser _userFromFirebase(User theuser)
  {
    return theuser == null? null : TheUser(id: theuser.id);
  }
  Stream<TheUser> get onAuthStateChanged {
    return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
  }
  Future<TheUser> signInAnonymously() async
  {
    return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
    return_userFromFirebase(theuser);
  }
  Future<void> logOut() async
  {
    return_firebaseAuth.logOut();
  }
}

下面我们更新我们的顶层小部件,以利用新的服务类。

class ThisApp extends StatelessWidget
{
  @override
  Widget build(BuildContext context)
  {
    return Provider<FirebaseAuthService>(
    
      builder:(_)=>FirebaseAuthService(),
      child: MaterialApp(
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: LandingPage(),
      ),
    );
  }
}

我们的Firebase认证服务类将是唯一一个被Firebase认证的突破性变化所影响的。没有必要创建一个基类,但如果你选择这样做,下面是一个代码的例子。

abstract class AuthService {
//creation of a base class
  Future<TheUser>loginAnonymously();
  Future<void>logOut();
  Stream<TheUserget onAStateChanged;
}
class FirebaseAuthService implements AuthService {
}

使用基类的类型,建立一个子类的实例。

class ThisApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider<AuthService>(
    // base class
      builder:(_)=>FirebaseAuthService(),
      // our concrete subclass
      child: MaterialApp(
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: LandingPage(),
      ),
    );
  }
}

注意。

  • 引入基类是一种退步,一次写一个服务类也是一种选择。
  • 在现代集成开发环境中,类的重命名和它们的使用变得简单。例如,当涉及到认证服务时,Firebase和一个假的认证服务可以在运行时互换,用于测试和演示。

创建一个持有应用程序状态的服务

第一步:创建一个服务类

NewAbstract.dart 是你应该创建的文件的名称。创建一个抽象类,在其中定义你的服务的能力。

abstract class newServiceClass {
  Future<int> getSomeValue();
  Future<void> doSomething(int value);
}

第二步:服务的实现

创建一个名为newImplService.dart 的新文件。扩展你的newServiceClass 类并添加以下方法。

import 'NewAbstract.dart';

class newImplService extends newServiceClass {
  @override
  Future<int> getSomeValue() async {
    // do something
    return someValue;
  }
  @override
  Future<void> doSomething(int value) async {
    // do something
  }
}

第三步:服务定位器和初始化

创建一个名为locatingService.dart 的文件并将GetIt服务定位器包添加到pubspec.yaml 。注册你的服务的实现。

import 'package:get_it/get_it.dart';
import 'newImplService.dart';
import 'NewAbstract.dart';
GetIt ourGetIt = GetIt.instance;
setupServiceLocator() {
  ourGetIt.registerLazySingleton<newServiceClass>(() => newImplService());
}

main.dart 中运行应用程序之前,初始化服务定位。

void main() {
  setupServiceLocator();
  runApp(MyApp());
}

第四步:使用该服务

在这一步,我们将使用服务定位器从代码的任何地方获得对我们服务的引用。

class MyClass {
  newServiceClass _newServiceClass = locator<newServiceClass>();
}

这就是你在该类中的实现方式。

  • _newServiceClass.getSomeValue()
  • _newServiceClass.doSomething(someValue)

这里有一个GIF来说明它应该如何工作。

Results

服务类的目的

  • 服务类的设计是为了将一个特定的操作与应用程序的其他部分分开,并隐藏其实现细节。
  • 从本质上讲,将代码耦合到一个单一的功能上,会使修改变得困难和容易出错。在这种情况下,你将需要帮助。例如,存储服务是你所创建的一个新类的名称。它在内部如何运作,其他类并不知道。它就像调用服务的函数来存储和获取数据一样简单。

结论

使用服务类,你能够在你的应用程序中隐藏第三方代码的实现细节。然而,如果你需要在你的代码库中多次联系一个API方法,这一点尤其适用。

总而言之,使用API封装类来隐藏实现细节,包括API方法的所有输入和输出参数,并设计一个简单的抽象服务类来轻松改变另一个版本。