如何在 Flutter 和 Nestjs 中使用 Session?

How to use Sessions with Flutter and Nestjs?

我试图让用户使用 nestjs 后端登录,当我使用 Postman 时,这个过程非常顺利,但是对于 Flutter,我不知道该怎么做。我不认为我真的了解会话如何在手机上工作,我试图寻找一些适当的解释,但到目前为止我找不到任何东西。

Nestjs 代码

 @Controller('users')
@Serialize(UserDto)
export class UsersController {
  constructor(
    private usersService: UsersService,
    private authService: AuthService,
  ) {}

  @Get('/whoami')
  @UseGuards(AuthGuard)
  whoAmI(@currentUser() user: User) {
    return user;
  }

  @Get()
  getUsers(@Query('email') email: string) {
    return this.usersService.find(email);
  }

  @Post('/signup')
  async sendUser(@Body() body: UserDto, @Session() session: any) {
    console.log(body);
    const user = await this.authService.signup(body.email, body.password);
    session.userId = user.id;
    console.log(session.userId);
    return user;
  }

  @Post('/signin')
  async signin(@Body() body: UserDto, @Session() session: any) {
    const user = await this.authService.signin(body.email, body.password);
    session.userId = user.id;
    console.log(session.userId);
    return user;
  }

  @Post('/signout')
  async signout(@Session() session: any) {
    console.log(session.userId);
    if (!session.userId) {
      throw new NotFoundException('user not found');
    }
    session.userId = null;
  }
}

Flutter 代码

Future<void> signin(
      String username, String password, BuildContext context) async {
    try {
      var url = 'https://example-app.herokuapp.com/users/signin';
      var dio = Dio();
      var response =
          await dio.post(url, data: {'email': username, 'password': password}, options: Options(headers: {'Accept': 'application/json'}));
      
      print(response.headers);

      // response;
      Navigator.of(context).pushNamed(CategoryDetailScreen.routeName);
    } catch (err) {
      print(err);
      throw err;
    }
  }

Future<void> signout() async {
    try {
      var url = 'https://example-app.herokuapp.com/users/signout';
      var dio = Dio();
      var response = await dio.post(url, 
          options: Options(headers: {
            'cookie':
                'key'
          }
          )
          );
      print(response.headers);
      response;
      // return response;
    } catch (err) {
      print(err);
      throw err;
    }
  }

感谢@RichardHeap 的评论,我设法解决了我的问题。

Check out session management with cookies:

我使用 FlutterSecureStorage 包来存储传入的 cookie,然后使用 FutureBuilder 决定将哪个屏幕显示为主屏幕,如下所示:

我使用这些函数来写入 cookie 并将它们从设备中删除:

    Future<void> signin(
          String username, String password, BuildContext context) async {
        try {
          // String cookie = '';
          var url = 'https://daleel-app.herokuapp.com/users/signin';
          var storage = FlutterSecureStorage();
          var dio = Dio();
          var response =
              await dio.post(url, data: {'email': username, 'password': password});
          List<String>? cookies = response.headers['set-cookie'];
          for (int i = 0; i <= cookies!.length - 1; i++) {
            var cokIndex = cookies[i].indexOf(';');
            var subCookies = cookies[i].substring(0, cokIndex + 1);
            cookie += subCookies + ' ';
          }
          var subbedCookie = cookie.substring(0, cookie.length - 2);
          print(subbedCookie);
    
          storage.write(key: 'cookie', value: subbedCookie);
          loggedIn = true;
          // print(response.headers['set-cookie']);
          // [express:sess=eyJ1c2VySWQiOjU1fQ==; path=/; httponly, express:sess.sig=Zy_Lc7kXM1BqZKIZRRt7ygpCTrM; path=/; httponly]
          Navigator.of(context).pushNamed(CategoryDetailScreen.routeName);
        } catch (err) {
          print(err);
          throw err;
        }
      }

Future<void> signout(BuildContext context) async {
    try {
      var url = 'https://daleel-app.herokuapp.com/users/signout';
      var dio = Dio();
      var fStorage = FlutterSecureStorage();
      var header = await fStorage.read(key: 'cookie');
          await dio.post(url, options: Options(headers: {'cookie': header}));
          fStorage.delete(key: 'cookie');
          Navigator.of(context).pushReplacementNamed(LoginScreen.routeName);
          print('you reached here');
    } catch (err) {
      print(err);
      throw err;
    }
  }

这里我使用 FutureBuilder 来决定显示哪个屏幕:

class MyApp extends StatefulWidget {

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
    var cookie = FlutterSecureStorage().read(key: 'cookie');
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (ctx) => Places(),
        ),
        ChangeNotifierProvider(
          create: (ctx) => Offers(),
        ),
      ],
      child: FutureBuilder(
        future: cookie,
        builder: (BuildContext context, AsyncSnapshot snapshot) => MaterialApp(
            debugShowCheckedModeBanner: true,
            title: 'Daleel',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: snapshot.hasData ? HomeScreen() : LoginScreen(),
            routes: {
              LoginScreen.routeName: (ctx) => LoginScreen(),
              TestScreen3.routeName: (ctx) => TestScreen3(),
              TestScreen2.routeName: (ctx) => TestScreen2(),
              HomeScreen.routeName: (ctx) => HomeScreen(),
              DetailsScreen.routeName: (ctx) => DetailsScreen(),
              ExploreScreen.routeName: (ctx) => ExploreScreen(),
              CategoryDetailScreen.routeName: (ctx) => CategoryDetailScreen(),
              SearchDetailsScreen.routeName: (ctx) => SearchDetailsScreen(),
            }),
      ),
    );
  }
}