ค้นหาเว็บไซต์

javascript - มีตัวดำเนินการ RXJs ที่ดีกว่าใช้ในสถานการณ์นี้ไหม?


ฉันมีวิธีการให้บริการที่ทำสิ่งต่อไปนี้:

  1. ค้นหาผู้ใช้จากฐานข้อมูลโดยใช้รหัสของพวกเขา
  2. ตรวจสอบว่าพบผู้ใช้หรือไม่
  3. ใช้ bcrypt เพื่อเปรียบเทียบรหัสผ่านที่เก็บไว้ในฐานข้อมูลกับรหัสผ่านที่ให้มาเป็นพารามิเตอร์
  4. จะโยน UnauthorizedException หากรหัสผ่านไม่ถูกต้อง และจะคืนผู้ใช้หากถูกต้อง

ฉันแค่พยายามหาว่ามีวิธีที่ดีกว่าในการใช้ตัวดำเนินการ RxJS เพื่อทำสิ่งนี้ไหม เพราะฉันไม่ชอบที่ต้องใช้ pipe จาก bcrypt.compare

public validateUser(email: string, pass: string): Promise<UserDto> {
    return this.userService
      .findOne({ email })
      .pipe(
        map((user: UserDto) => {
          if (!user || !user.password) {
            return throwError(new UnauthorizedException());
          }
          return user;
        }),
        switchMap((user: UserDto) => {
          return from(
            bcrypt.compare(pass, user.password) as Promise<boolean>
          ).pipe(
            map((passwordIsCorrect) => ({
              passwordIsCorrect,
              user
            }))
          );
        }),
        switchMap((res) => {
          if (!res.passwordIsCorrect) {
            return throwError(new UnauthorizedException());
          }
          return of(res.user);
        })
      )
      .toPromise();
  }

คำตอบ #1

ฉันไม่คิดว่าจะมีตัวดำเนินการที่ดีกว่านี้ให้ใช้งาน แต่คุณสามารถทำให้โค้ดง่ายขึ้นโดยให้อยู่ทั้งหมดภายใน switchMap แบบนี้:

  public validateUser(email: string, pass: string): Promise<UserDto> {
    return this.userService.findOne({ email }).pipe(
      switchMap(user => {
        if (!user?.password) {
          return throwError(new UnauthorizedException());
        }

        return from(bcrypt.compare(pass, user.password)).pipe(
          switchMap(passwordIsCorrect => passwordIsCorrect ? of(user) : throwError(new UnauthorizedException()))
        )
      })
    ).toPromise();
  }

อย่างไรก็ตาม ในกรณีนี้ ดูเหมือนว่าคุณกำลังพยายามใช้ observables (แปลง promise เป็น observable เพียงเพื่อแปลงกลับเป็น promise)

แม้ว่าฟังก์ชัน userService จะส่งค่า observable กลับมา ทำไมไม่แปลงมันเป็น promise ไปเลยล่ะ? ดูเหมือนว่าโค้ดจะง่ายขึ้นมาก:

  public async validateUser(email: string, pass: string): Promise<UserDto> {
    const user = await this.userService.findOne({ email }).toPromise();
    
    if (!user?.password || !await bcrypt.compare(pass, user.password)) {
      throw new UnauthorizedException();
    }
    
    return user;
  }

คำตอบ #2

ถ้าคุณไม่ชอบท่อภายในจริง ๆ คุณสามารถใช้ combineLatest เพื่อส่งค่าของ user พร้อมกับ passwordIsCorrect ได้เช่นกัน:

public validateUser(email: string, pass: string): Promise<UserDto> {
    return this.userService
      .findOne({ email })
      .pipe(
        map((user: UserDto) => {
          if (!user || !user.password) {
            return throwError(new UnauthorizedException());
          }
          return user;
        }),
        switchMap((user: UserDto) => {
          return combineLatest([
            from(bcrypt.compare(pass, user.password)),
            of(user)
          ])
        }),
        map(([passwordIsCorrect, user]) => ({
          passwordIsCorrect,
          user
        })),
        switchMap((res) => {
          if (!res.passwordIsCorrect) {
            return throwError(new UnauthorizedException());
          }
          return of(res.user);
        })
      )
      .toPromise();
  }