使用 Laravel 的 Http Facade(门面) 简化 API 集成

构建可重用的 API 请求和客户端类

最近一直致力于集成第三方 API。有多种不同的方法可以实现此目的,例如使用第三方提供的 SDK。然而,我觉得坚持 Laravel 的Http外观通常是更好的选择。通过使用Http外观,所有第三方集成都可以具有类似的结构,并且测试和模拟变得更加容易。此外,您的应用程序将具有更少的依赖项。您不必担心如何使 SDK 保持最新状态,也不必担心 SDK 不再受支持时该怎么办。

使用 集成  Google Books API 作为示例,创建一个可重用的客户端和请求类,以使 API 的使用变得非常简单

让我们开始吧!

Laravel 的 Http Facade(门面) 简化 API 集成

将 Google Books 配置添加到 Laravel

现在我们有了 API 密钥,我们可以将其.env与 API URL 一起添加到中。

GOOGLE_BOOKS_API_URL=https://www.googleapis.com/books/v1
GOOGLE_BOOKS_API_KEY=[API KEY FROM GOOGLE]

在此示例中,我存储了从 Google Cloud 控制台获取的 API 密钥,但我们将访问的 API 部分不需要该密钥。对于更高级的 API 使用,您需要与 Google 的 OAuth 2.0 服务器集成,并创建也可以存储在文件中的客户端 ID 和密钥.env。这超出了本文的范围。

环境变量就位后,打开 config/services.php 文件并添加 Google Books 的部分。

'google_books' => [
    // 从 .env 检索的 Google Books API 的基本 URL
    'base_url' => env('GOOGLE_BOOKS_API_URL'),
    // Google Books API 的 API 密钥,从 .env 检索
    'api_key' => env('GOOGLE_BOOKS_API_KEY'),
],

创建 ApiRequest 类

当向 API 发出请求时,我发现使用一个简单的类来设置我需要的任何请求属性是最简单的。

下面是一个ApiRequest类的示例,我用它来传递 URL 信息以及正文、标头和任何查询参数。可以轻松修改或扩展此类以添加附加功能。

<?php

namespace App\Support;

/**
  * ApiRequest 类是一个用于构建对 API 的 HTTP 请求的实用程序。
  * 提供设置HTTP方法、URI、标头、查询的方法
  * 请求的参数和正文。
  * 它还提供了获取这些属性的方法,以及
  * 清除标头、查询参数和正文。
  * 此外,它还提供了创建ApiRequest实例的静态方法
  * 对于特定的 HTTP 方法。
  */
class ApiRequest
{
    // 存储将与 API 请求一起发送的标头。
    protected array $headers = [];

    //存储任何查询字符串参数。
    protected array $query = [];

    // 存储请求的正文。
    protected array $body = [];

    /**
     * 为给定的 HTTP 方法和 URI 创建 API 请求。
     */
    public function __construct(protected HttpMethod $method = HttpMethod::GET, protected string $uri = '')
    {
    }

    /**
      * 设置请求的标头。
      * 这接受键和值,或键/值对数组。
      */
    public function setHeaders(array|string $key, string $value = null): static
    {
        if (is_array($key)) {
            $this->headers = $key;
        } else {
            $this->headers[$key] = $value;
        }

        return $this;
    }

    /**
      * 清除请求的标头。
      * 该方法可以清除请求中的特定标头或所有标头,如果
      * 不提供钥匙。
      */
    public function clearHeaders(string $key = null): static
    {
        if ($key) {
            unset($this->headers[$key]);
        } else {
            $this->headers = [];
        }

        return $this;
    }

    /**
      * 设置请求的查询参数。
      * 这接受键和值,或键/值对数组。
      */
    public function setQuery(array|string $key, string $value = null): static
    {
        if (is_array($key)) {
            $this->query = $key;
        } else {
            $this->query[$key] = $value;
        }

        return $this;
    }

    /**
     * 清除请求的查询参数。
     * 该方法可以清除某个参数或者某个按键的所有参数
     * 不提供。
     */
    public function clearQuery(string $key = null): static
    {
        if ($key) {
            unset($this->query[$key]);
        } else {
            $this->query = [];
        }

        return $this;
    }

    /**
     * 设置请求的正文数据。
     * 这接受键和值,或键/值对数组。
     */
    public function setBody(array|string $key, string $value = null): static
    {
        if (is_array($key)) {
            $this->body = $key;
        } else {
            $this->body[$key] = $value;
        }

        return $this;
    }

    /**
     * 清除请求的正文数据。
     * 该方法可以清除特定键的数据或全部数据。
     */
    public function clearBody(string $key = null): static
    {
        if ($key) {
            unset($this->body[$key]);
        } else {
            $this->body = [];
        }

        return $this;
    }

    /**
     * 此方法返回 API 请求的标头。
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }

    /**
     * 此方法返回 API 请求的查询。
     */
    public function getQuery(): array
    {
        return $this->query;
    }

    /**
     * 此方法返回 API 请求的正文。
     */
    public function getBody(): array
    {
        return $this->body;
    }

    /**
      * 该方法返回API请求的URI。
      * 如果查询为空,或者我们有一个GET请求,可以返回URI
      * 按原样。
      * 否则,我们需要将查询字符串附加到 URI 中。
      */
    public function getUri(): string
    {
        if (empty($this->query) || $this->method === HttpMethod::GET) {
            return $this->uri;
        }

        return $this->uri.'?'.http_build_query($this->query);
    }

    /**
     * This method returns the HTTP method for the API request.
     */
    public function getMethod(): HttpMethod
    {
        return $this->method;
    }

    // 以下方法用于创建特定 HTTP 的 API 请求
    // 方法。

    public static function get(string $uri = ''): static
    {
        return new static(HttpMethod::GET, $uri);
    }

    public static function post(string $uri = ''): static
    {
        return new static(HttpMethod::POST, $uri);
    }

    public static function put(string $uri = ''): static
    {
        return new static(HttpMethod::PUT, $uri);
    }

    public static function delete(string $uri = ''): static
    {
        return new static(HttpMethod::DELETE, $uri);
    }
}

类构造函数采用一个HttpMethod,它只是一个包含各种 HTTP 方法的简单枚举和一个 URI。

enum HttpMethod: string
{
    case GET = 'get';
    case POST = 'post';
    case PUT = 'put';
    case DELETE = 'delete';
}

有一些辅助方法可以使用 HTTP 方法名称并传递 URI 创建请求。最后,还有添加和清除标头、查询参数和正文数据的方法。


创建 API 客户端

现在我们有了请求,我们需要一个 API 客户端来发送它。这是我们可以使用Http门面的地方。

抽象ApiClient

首先,我们将创建一个抽象ApiClient类,该类将通过我们的各种 API 进行扩展。

<?php

namespace App\Support;

use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;

/**
 * ApiClient 类是一个用于向 API 发送 HTTP 请求的抽象基类。
 * 它提供了一个发送 ApiRequest 的方法,并提供了获取和授权基本请求的方法。
 * 子类必须实现 baseUrl 方法来指定 API 的基本 URL。
 */
abstract class ApiClient
{
    /**
     * 发送 ApiRequest 到 API 并返回响应。
     */
    public function send(ApiRequest $request): Response
    {
        return $this->getBaseRequest()
            ->withHeaders($request->getHeaders())
            ->{$request->getMethod()->value}(
                $request->getUri(),
                $request->getMethod() === HttpMethod::GET
                    ? $request->getQuery()
                    : $request->getBody()
            );
    }

    /**
     * 获取 API 的基本请求。
     * 这个方法对于 API 请求有一些有用的默认值。
     * 基本请求是一个具有 JSON 接受、内容类型为 'application/json' 和 API 的基本 URL 的 PendingRequest。
     * 它还会针对非成功的响应抛出异常。
     */
    protected function getBaseRequest(): PendingRequest
    {
        $request = Http::acceptJson()
            ->contentType('application/json')
            ->throw()
            ->baseUrl($this->baseUrl());

        return $this->authorize($request);
    }

    /**
     * 授权 API 请求。
     * 这个方法用于被子类重写,以提供特定于 API 的授权。
     * 默认情况下,它只是返回给定的请求。
     */
    protected function authorize(PendingRequest $request): PendingRequest
    {
        return $request;
    }

    /**
     * 获取 API 的基本 URL。
     * 子类必须实现这个方法来提供 API 的基本 URL。
     */
    abstract protected function baseUrl(): string;
}

此类有一个 getBaseRequest 方法,可以使用外观创建一些合理的默认值 Http 来创建 PendingRequest. 它调用authorize我们可以在 Google Books 实现中重写的方法来设置 API 密钥。

该 baseUrl 方法只是一个简单的抽象方法,我们的 Google Books 类将设置它以使用我们之前设置的 Google Books API URL。

最后,send 方法是将请求发送到 API 的方法。它需要一个ApiRequest参数来构建请求,然后返回响应。

GoogleBooksApi客户端

创建抽象客户端后,我们现在可以创建一个GoogleBooksApiClient来扩展它。

<?php

namespace App\Support;

use Illuminate\Http\Client\PendingRequest;

/**
 * GoogleBooksApiClient 类是一个对 Google Books API 的 ApiClient 基类的具体实现。
 * 它提供了获取基本 URL 和授权请求的方法,用于操作 Google Books API。
 */
class GoogleBooksApiClient extends ApiClient
{
    /**
     * 获取 Google Books API 的基本 URL。
     * 基本 URL 是从 'services.google_books.base_url' 配置值中获取的。
     */
    protected function baseUrl(): string
    {
        return config('services.google_books.base_url');
    }

    /**
     * 授权 Google Books API 的请求。
     * Google Books API 将 API 密钥作为名为 'key' 的查询参数接受。
     * API 密钥是从 'services.google_books.api_key' 配置值中获取的。
     */
    protected function authorize(PendingRequest $request): PendingRequest
    {
        return $request->withQueryParameters([
            'key' => config('services.google_books.api_key'),
        ]);
    }
}

在这个类中,我们只需要设置基本URL并配置授权。对于 Google Books API,这意味着将 API 密钥作为 URL 参数传递并设置空 Authorization 标头。

如果我们有一个使用不记名授权的 API,我们可以有authorize如下方法:

protected function authorize(PendingRequest $request): PendingRequest
{
    return $request->withToken(config(services.someApi.token));
}

使用此方法的好处 authorize 是它可以灵活地支持各种 API 授权方法。


按书名查询书籍

现在我们有了ApiRequest类 和GoogleBooksApiClient,我们可以创建一个操作来按标题查询书籍。它看起来像这样:

<?php

namespace App\Actions;

use App\Support\ApiRequest;
use App\Support\GoogleBooksApiClient;
use Illuminate\Http\Client\Response;

/**
 * QueryBooksByTitle 类是一个从 Google Books API 查询书籍标题的操作类。
 * 它提供了一个 __invoke 方法,接受一个标题,并返回 API 的响应。
 */
class QueryBooksByTitle
{
    /**
     * 从 Google Books API 查询书籍标题并返回响应。
     * 此方法创建了一个 GoogleBooksApiClient 和一个针对 'volumes' 终点的 ApiRequest,
     * 使用给定的标题作为 'q' 查询参数,并将 'books' 设置为 'printType' 查询参数。
     * 然后使用客户端发送请求并返回响应。
     */
    public function __invoke(string $title): Response
    {
        $client = app(GoogleBooksApiClient::class);

        $request = ApiRequest::get('volumes')
            ->setQuery('q', 'intitle:'.$title)
            ->setQuery('printType', 'books');

        return $client->send($request);
    }
}

然后,为了调用该操作,如果我想查找有关我刚刚阅读并强烈推荐的《The Ferryman》一书的信息,请使用以下代码片段:

use App\Actions\QueryBooksByTitle;
$response = app(QueryBooksByTitle::class)("The Ferryman");
$response->json();

奖励:测试

下面,我添加了一些用于测试请求和客户端类的示例。对于测试,我使用 Pest PHP,它在 PHPUnit 之上提供了干净的语法和附加功能。

API请求

<?php

use App\Support\ApiRequest;
use App\Support\HttpMethod;

it('sets request data properly', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders(['foo' => 'bar'])
        ->setQuery(['baz' => 'qux'])
        ->setBody(['quux' => 'quuz']);

    expect($request)
        ->getHeaders()->toBe(['foo' => 'bar'])
        ->getQuery()->toBe(['baz' => 'qux'])
        ->getBody()->toBe(['quux' => 'quuz'])
        ->getMethod()->toBe(HttpMethod::GET)
        ->getUri()->toBe('/');
});

it('sets request data properly with a key->value', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders('foo', 'bar')
        ->setQuery('baz', 'qux')
        ->setBody('quux', 'quuz');

    expect($request)
        ->getHeaders()->toBe(['foo' => 'bar'])
        ->getQuery()->toBe(['baz' => 'qux'])
        ->getBody()->toBe(['quux' => 'quuz'])
        ->getMethod()->toBe(HttpMethod::GET)
        ->getUri()->toBe('/');
});

it('clears request data properly', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders(['foo' => 'bar'])
        ->setQuery(['baz' => 'qux'])
        ->setBody(['quux' => 'quuz']);

    $request->clearHeaders()
        ->clearQuery()
        ->clearBody();

    expect($request)
        ->getHeaders()->toBe([])
        ->getQuery()->toBe([])
        ->getBody()->toBe([])
        ->getUri()->toBe('/');
});

it('clears request data properly with a key', function () {
    $request = (new ApiRequest(HttpMethod::GET, '/'))
        ->setHeaders('foo', 'bar')
        ->setQuery('baz', 'qux')
        ->setBody('quux', 'quuz');

    $request->clearHeaders('foo')
        ->clearQuery('baz')
        ->clearBody('quux');

    expect($request)
        ->getHeaders()->toBe([])
        ->getQuery()->toBe([])
        ->getBody()->toBe([])
        ->getUri()->toBe('/');
});

it('creates instance with correct method', function (HttpMethod $method) {
    $request = ApiRequest::{$method->value}('/');

    expect($request->getMethod())->toBe($method);
})->with([
    [HttpMethod::GET],
    [HttpMethod::POST],
    [HttpMethod::PUT],
    [HttpMethod::DELETE],
]);

测试ApiRequest检查是否设置了正确的请求数据以及是否使用了正确的方法。


API客户端

测试ApiClient会稍微复杂一些。由于它是一个抽象类,我们将在函数中使用匿名类beforeEach来创建一个客户端来使用该扩展ApiClient。

请注意,我们也使用该Http::fake()方法。这会在Http外观上创建模拟,我们可以对其进行断言并防止在测试中发出 API 请求。

<?php

use App\Support\ApiClient;
use App\Support\ApiRequest;
use App\Support\HttpMethod;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;

beforeEach(function () {
    Http::fake();

    $this->client = new class extends ApiClient
    {
        protected function baseUrl(): string
        {
            return 'https://example.com';
        }
    };
});

it('sends a get request', function () {
    $request = ApiRequest::get('foo')
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::GET->name)
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('sends a post request', function () {
    $request = ApiRequest::post('foo')
        ->setBody(['foo' => 'bar'])
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::POST->name)
            ->data()->toBe(['foo' => 'bar'])
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('sends a put request', function () {
    $request = ApiRequest::put('foo')
        ->setBody(['foo' => 'bar'])
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::PUT->name)
            ->data()->toBe(['foo' => 'bar'])
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('sends a delete request', function () {
    $request = ApiRequest::delete('foo')
        ->setBody(['foo' => 'bar'])
        ->setHeaders(['X-Foo' => 'Bar'])
        ->setQuery(['baz' => 'qux']);

    $this->client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)
            ->url()->toBe('https://example.com/foo?baz=qux')
            ->method()->toBe(HttpMethod::DELETE->name)
            ->data()->toBe(['foo' => 'bar'])
            ->header('X-Foo')->toBe(['Bar']);

        return true;
    });
});

it('handles authorization', function () {
    $client = new class extends ApiClient
    {
        protected function baseUrl(): string
        {
            return 'https://example.com';
        }

        protected function authorize(PendingRequest $request): PendingRequest
        {
            return $request->withHeaders(['Authorization' => 'Bearer foo']);
        }
    };

    $request = ApiRequest::get('foo');

    $client->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)->header('Authorization')->toBe(['Bearer foo']);

        return true;
    });
});

对于测试,我们确认在各种请求方法上正确设置了请求属性。我们还确认baseUrl和authorize方法被正确调用。为了做出这些断言,我们使用的Http::assertSent方法需要一个带有 a 的回调$request,我们可以对其进行测试。请注意,我正在使用 PestPHP 期望,然后返回true. 我们可以只使用正常的比较并返回它,但是通过使用期望,当测试失败时我们会得到更清晰的错误消息。阅读这篇优秀的文章以获取更多信息。

GoogleBooksApiClient测试

测试与我们只想确保正确处理自定义实现细节的测试GoogleBooksApiClient类似,例如设置基本 URL 并使用 API 密钥添加查询参数。ApiClient

另外,不是方法config中的助手beforeEach。通过使用帮助程序,我们可以为将在每个测试中使用的 Google 图书服务配置设置测试值。

<?php

use App\Support\ApiRequest;
use App\Support\GoogleBooksApiClient;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\Request;

beforeEach(function () {
    Http::fake();
    config([
        'services.google_books.base_url' => 'https://example.com',
        'services.google_books.api_key' => 'foo',
    ]);
});

it('sets the base url', function () {
    $request = ApiRequest::get('foo');

    app(GoogleBooksApiClient::class)->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)->url()->toStartWith('https://example.com/foo');

        return true;
    });
});

it('sets the api key as a query parameter', function () {
    $request = ApiRequest::get('foo');

    app(GoogleBooksApiClient::class)->send($request);

    Http::assertSent(static function (Request $request) {
        expect($request)->url()->toContain('key=foo');

        return true;
    });
});

总结

在本文中,我们介绍了在 Laravel 中集成第三方 API 的一些有用步骤。通过使用这些简单的自定义类以及外观Http,我们可以确保所有集成功能相似,更易于测试,并且不需要任何项目依赖项。在后面的文章中,我将通过介绍 DTO、使用模拟响应进行测试以及使用 API 资源来扩展这些集成技巧。文章来源地址https://www.toymoban.com/diary/laravel/692.html

到此这篇关于使用 Laravel 的 Http Facade(门面) 简化 API 集成的文章就介绍到这了,更多相关内容可以在右上角搜索或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

原文地址:https://www.toymoban.com/diary/laravel/692.html

如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用
在Java中使用GSON处理JSON数据的简易教程
上一篇 2024年01月17日 10:20
使用 Laravel Mock 提高您的工作效率
下一篇 2024年01月18日 10:51

相关文章

  • HTTP简化版 API使用

    1.1、获取当前IP(限制 1200次 /小时) 用浏览器访问 http://ip.hahado.cn/simple/current-ip?username=usernamepassword=password URL上面加上用户名和密码 \\\"ip\\\": 字段是当前的外网IP (\\\"ip\\\":\\\"null\\\" 正在切换中,暂时没有IP) \\\"ttl\\\": 字段是ip可以使用的剩余时间(秒) 1.2、手动切换IP(限制 180次 /小时,间隔

    2023年04月09日
    浏览(47)
  • 如何使用Laravel的HTTP客户端与外部API交互

    Laravel使API交互对新的和有经验的Web开发人员来说都是轻而易举的。Larvel的HTTP客户端是建立在PHP的Guzzle HTTP客户端之上,让开发者在进行HTTP请求时有更顺畅的体验。它的主要功能包括认证, 路由, 和有效的对象关系映射(ORM). 本文将探讨如何使用Laravel的HTTP客户端来进行请求, 调

    2024年01月21日
    浏览(85)
  • 《golang设计模式》第二部分·结构型模式-05-门面模式Facade)

    门面(Facade)向客户端提供使用子系统的统一接口,用于简化客户端使用子系统的操作。 门面角色(Facade) 客户端可以调用的接口或方法 此角色知晓系统的所有功能和责任 一般情况下,该角色没有实际业务逻辑,只是一个委托类。 子系统角色(Subsystem) 同时有一个或多个

    2024年02月07日
    浏览(60)
  • WPF项目创建HTTP WEB服务,不使用IIS业务 WPF桌面程序WebApi WPF 集成WebApi C# 创建HTTP Web API服务

    在C# WPF应用程序中直接创建HTTP服务或WebAPI服务有以下优点: 自托管服务: 简化部署:无需依赖外部服务器或IIS(Internet Information Services),可以直接在应用程序内部启动和运行Web服务。 集成紧密:与WPF应用程序的其他组件和逻辑可以更紧密地集成,因为它们都在同一个进程

    2024年02月02日
    浏览(61)
  • 【设计模式-05】Facade门面Mediator调停者 | Decorator装饰器 | Chain Of Responsibility责任链

    一般是系统内部相互交错,比如 消息中间件(MQ) 就是这种设计模式,对各个功能或系统之间进行解耦。 业务场景:论坛或者博客发表帖子或者评论,对敏感词过滤处理。使用责任链的模式进行过滤处理。 把变化的进行封装处理 核心代码实现 GitHub - jxaufang168/Design-Patterns: 设计

    2024年01月19日
    浏览(110)
  • C#桌面程序 winform 集成内置WebApi C# 创建HTTP Web API服务,winform项目创建HTTP WEB服务,不使用IIS业务 C#桌面程序WebApi C#winform集

     在维护旧的项目时,有时需要提供APP连接的需求,就要提供HTTP服务,winform项目就要提供HTTP服务,就不用再去写个c# web的IIS相关的业务了,简化项目的复杂度。只需要简单化实例就可以实现提供HTTP服务

    2024年02月08日
    浏览(51)
  • larvel 中的api.php_Laravel 开发 API

    Laravel10中提示了Target *classController does not exist,为什么呢? 原因是:laravel8开始写法变了。换成了新的写法了 解决方法一: 在路由数组加入 AppHttpControllers 即可。 再次访问URL,搞定。 解决方法二: 打开 appProvidersRouteServiceProvider.php 修改,添加一个namespace变量

    2024年02月06日
    浏览(73)
  • 使用Azure Data Factory REST API和HDInsight Spark进行简化数据处理

    在这篇文章中,我们将探讨如何利用Azure Data Factory和HDInsight Spark创建一个强大的数据处理管道。 在当今数据驱动的世界中,组织经常面临着高效可靠地处理和分析大量数据的挑战。Azure Data Factory是一种基于云的数据集成服务,结合HDInsight Spark,一种快速可扩展的大数据处理框

    2024年02月10日
    浏览(101)
  • Forest-声明式HTTP客户端框架-集成到SpringBoot实现调用第三方restful api并实现接口数据转换

    声明式HTTP客户端API框架,让Java发送HTTP/HTTPS请求不再难。它比OkHttp和HttpClient更高层, 是封装调用第三方restful api client接口的好帮手,是retrofit和feign之外另一个选择。 通过在接口上声明注解的方式配置HTTP请求接口。 官网: Forest   代码地址: forest: 声明式HTTP客户端API框架,让

    2024年02月04日
    浏览(115)
  • 使用 Python 集成 ChatGPT API

    目录 一、安装 ChatGPT API 二、创建 Python 程序 三、调用 ChatGPT API 四、使用上下文进行对话 五、自定义模型 六、总结 随着人工智能技术的不断发展,自然语言处理技术也越来越成熟。ChatGPT 是一种基于深度学习的自然语言生成技术,可以用于构建智能对话系统。ChatGPT API 是

    2024年02月04日
    浏览(59)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包