NestJS实战08-在NestJS中如何调用百度网盘API(中)

by 雪隐 from juejin.cn/user/143341...

本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可联系授权

概要

这一章将详细探讨如何实现百度网盘的API授权流程,以及如何通过Cache module缓存Access_token,并利用这个access_token来获取百度网盘的文件列表信息。

授权是访问百度网盘API的关键步骤,因此本章的内容具有重要性。

实现授权流程

在本节中,我们将详细解释如何实现OAuth2授权流程,该流程允许我们获得访问百度网盘API所需的令牌。请注意,下述描述是基于我个人的经验,如果有更好的实践方法,欢迎在评论中分享。

步骤一:前端应用请求授权码

  • 前端应用启动授权流程,请求一个授权码(Code)。
  • 百度OAuth服务器呈现授权页面供用户登录和授权。

步骤二:用户同意授权

  • 用户登录并同意授权。
  • 用户同意授权后,百度OAuth服务器将页面重定向到开发者应用程序配置的回调URL,并返回授权码(Code)。

步骤三:后端应用请求访问令牌

  • 前端获得授权码后,将其传递给后端应用。
  • 后端应用使用授权码(Code)向百度OAuth服务器请求访问令牌。

步骤四:缓存Access Token

  • 百度OAuth服务器返回访问令牌(Access Token)。
  • 后端应用对获得的令牌进行缓存,以便将来的访问请求可以使用它。

这些步骤构成了完整的授权流程,它允许我们在获得访问令牌后访问百度网盘API。接下来,我们将进一步探讨如何有效地使用访问令牌来获取百度网盘的文件列表信息。

前端应用发起授权码 Code 请求,获得Code以后请求后端接口

首先让我来封装一个方法,用来发起授权码Code请求

  • 官方的说明如下:
ini 复制代码
GET http://openapi.baidu.com/oauth/2.0/authorize?
response_type=code&
client_id=您应用的AppKey&
redirect_uri=您应用的授权回调地址&
scope=basic,netdisk&
device_id=您应用的AppID

以上链接示例中参数仅给出了必选参数,其中device_id为硬件应用下的必选参数。
关于应用的相关信息,您可在控制台,点进去您对应的应用,查看应用详情获得。
  • 我们将这个请求封装如下:
ts 复制代码
// utils/baidu-api.ts
/**
 * 获得百度授权码
 * @param uri
 */
export const doBaiduAuth = (uri: string) => {
  const redirectUrl = `http://openapi.baidu.com/oauth/2.0/authorize?
  response_type=code&
  client_id=${process.env.APP_KEY}&
  redirect_uri=${encodeURIComponent(uri)}&
  scope=basic,netdisk&
  device_id=${process.env.APP_ID}`;

  window.location.href = redirectUrl;
};

资料汇总页面 中添加如下代码:

ts 复制代码
// resource/index.tsx
  useEffect(() => {
    const getCodeFromSearchParams = () => {
      // 创建 URLSearchParams 对象
      const searchParams = new URLSearchParams(search);

      // 获取特定参数的值
      return searchParams.get("code");
    };
    // 取得Url的Code参数
    const code = getCodeFromSearchParams();

    // 如果不存在Code则请求获取Code,如果已经有了Code则请求后端接口
    if (!code) {
      // 如果没有 Code,执行百度授权码请求
      doBaiduAuth(window.location.origin + window.location.pathname);
    } else {
      // 如果已经有了 Code,请求后端接口获取令牌
      getToken(code);
    }
  }, []);
  • 配置内容如下:
ts 复制代码
// config/config.dev.ts
export default {
  define: {
    "process.env.API_BASE_URL": "http://localhost:8001/api",
    "process.env.API_DOWNLOAD_URL": "http://localhost:8001",
    "process.env.APP_ID": "<您的AppID>",
    "process.env.APP_KEY": "<您的AppKey>",
    "process.env.SECRET_KEY": "<您的SECRET_KEY>",
    "process.env.SIGN_KEY": "<您的SIGN_KEY>",
  },
};

后端接收Code,然后请求百度来获取Access Token并进行存储

首先,在 Module 中加入 CacheModule

  • 安装依赖
ts 复制代码
$ npm install @nestjs/cache-manager cache-manager@4.1.0
  • 注意:

官方文档中已经详细介绍,cache-manager 的 v4 版本和 v5 版本存在一些差异,其中 v4 版本的 ttl 单位是秒,而 v5 版本的单位是毫秒。由于百度网盘的 Access_token 过期时间以秒为单位,因此在此示例中,我们使用 v4 (官方推荐)版本的 cache-manager

  • BaiduApiModule中加入CacheModule
ts 复制代码
// baidu-api/baidu-api.module.ts
import { CacheModule } from '@nestjs/cache-manager';
@Module({
  imports: [
    CacheModule.register(),
    // 。。。其他
  ],
  controllers: [BaiduApiController],
  providers: [BaiduApiService],
})
export class BaiduApiModule {}
  • Controller层简单的接收请求信息并交给服务层去处理
ts 复制代码
// baidu-api/baidu-api.controller.ts

@Controller('baidu-api')
export class BaiduApiController {
  constructor(private readonly baiduApiService: BaiduApiService) {}

  @Post('token')
  getBaiduToken(@Body() baiduTokenDto: BaiduTokenDto) {
    return this.baiduApiService.getBaiduToken(baiduTokenDto);
  }
}
  • Dto的内容如下,接收coderedictUri
ts 复制代码
// baidu-api/dto/baidu_token.dto.ts
import { IsString } from 'class-validator';

export class BaiduTokenDto {
  // 用户授权码 Code
  @IsString({ message: 'code必须为字符串' }) // 为title字段添加索引,提高查询速度
  code: string;

  @IsString({ message: 'redirctUri必须为字符串' })
  redirctUri: string;
}
bash 复制代码
GET /rest/2.0/xpan/file?method=list&access_token=xxx HTTP/1.1
Host: pan.baidu.com
  • 实现service的代码如下
ts 复制代码
import * as fs from 'fs/promises';
import { createWriteStream } from 'fs';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from '../enum/config.enum';
import { BaiduTokenDto } from './dto/baidu_token.dto';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { HttpService } from '@nestjs/axios';
import { catchError, map } from 'rxjs/operators';
import { firstValueFrom } from 'rxjs';
import { BaiduListDto } from './dto/baidu_list.dto';
import { BaiSearchDto } from './dto/baidu_search.dto';
import { promisify } from 'util';
import { join } from 'path';

@Injectable()
export class BaiduApiService {
  private readonly logger = new Logger(BaiduApiService.name);

  constructor(
    private readonly configService: ConfigService,
    @Inject(CACHE_MANAGER)
    private cacheManager: Cache,
    private readonly httpService: HttpService,
  ) {}

  async getBaiduToken(baiduTokenDto: BaiduTokenDto): Promise<any> {
    // 获取缓存的Access_token
    const token: string = await this.cacheManager.get('access_token');

    // 如果不存在则请求百度接口
    if (!token) {
      const BAIDU_CONFOG = this.configService.get(ConfigEnum.BAIDU_CONFIG);
      const { code, redirctUri } = baiduTokenDto;
      const url = `https://openapi.baidu.com/oauth/2.0/token?grant_type=authorization_code&code=${code}&client_id=${BAIDU_CONFOG.APP_KEY}&client_secret=${BAIDU_CONFOG.SECRET_KEY}&redirect_uri=${redirctUri}`;
      this.logger.debug(url);
      const result = await firstValueFrom(
        this.httpService
          .get(url)
          .pipe(map((response) => response.data))
          .pipe(
            catchError((error) => {
              this.logger.error(error);
              throw '获取百度网盘token失败!';
            }),
          ),
      );
      const { access_token, refresh_token, expires_in } = result;
      // 将返回的access_token和refresh_token进行缓存,过期时间和返回expires_in秒单位过期时间一样
      await this.cacheManager.set('access_token', access_token, expires_in);
      await this.cacheManager.set('refresh_token', refresh_token, expires_in);

      return await this.getBaidulist(access_token);
    } else {
      return await this.getBaidulist(token);
    }
  }
  
  // 通过access_token获取百度列表
  async getBaidulist(token?: string): Promise<any> {
    const access_token = (await this.cacheManager.get('access_token')) || token;
    if (access_token) {
      const url = `/rest/2.0/xpan/file?method=list&access_token=${access_token}`;
      this.logger.debug(url);
      const response = await firstValueFrom(
        this.httpService
          .get(url)
          .pipe(map((response) => response.data))
          .pipe(
            catchError(async (error) => {
              this.logger.error(error);
              await this.cacheManager.del('access_token');
              await this.cacheManager.del('refresh_token');
              throw '取得百度网盘列表失败!';
            }),
          ),
      );
      return response;
    }
    return this.noToken();
  }
}

总结

本章简要地演示了如何实现百度网盘的授权流程,并介绍了与缓存相关的知识。在下一节中,我们将继续深入探讨如何完成百度网盘的各项操作。

相关推荐
太阳花ˉ2 分钟前
html+css+js实现step进度条效果
javascript·css·html
小白学习日记43 分钟前
【复习】HTML常用标签<table>
前端·html
john_hjy1 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd1 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
丁总学Java1 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele1 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
It'sMyGo2 小时前
Javascript数组研究09_Array.prototype[Symbol.unscopables]
开发语言·javascript·原型模式
懒羊羊大王呀2 小时前
CSS——属性值计算
前端·css
DOKE2 小时前
VSCode终端:提升命令行使用体验
前端
xgq2 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试