bizley / yii2-mercure
Yii 2 的 Mercure 发布者。
Requires
- php: >=7.2.0
- yiisoft/yii2: >=2.0.13 <2.1.0
Requires (Dev)
- phpunit/phpunit: ^7.3
- roave/security-advisories: dev-master
Suggests
- bizley/jwt: ^2.0
- yiisoft/yii2-httpclient: ^2.0
This package is auto-updated.
Last update: 2024-09-02 04:41:11 UTC
README
将 Mercure 发布者作为 Yii 2 组件
此包为 Yii 2 框架提供组件,用于向 Mercure 中心发布更新。
什么是 Mercure?
Mercure 是一种协议,允许以方便、快速、可靠和节能的方式将数据更新推送到网络浏览器和其他 HTTP 客户端。它特别适用于发布通过 Web API 提供的资源实时更新,用于响应式 Web 和移动应用。
查看相关仓库以了解更多关于 Mercure 的信息。还包括如何设置服务器和客户端以使用 Mercure 协议建立连接的说明。
安装
将包添加到您的 composer.json
{
"require": {
"bizley/yii2-mercure": "^1.0"
}
}
然后运行 composer update
,或者也可以运行 composer require bizley/yii2-mercure:^1.0
当然,您还需要 Mercure Hub。有关获取 Hub 的说明,请参阅 dunglas/mercure(我建议使用 Docker 镜像)。
配置
在您的配置文件中添加以下内容
'components' => [
'mercure' => [
'class' => \bizley\yii2\mercure\Publisher::class,
'hubUrl' => 'http://mercure.local/hub', // URL of the Mercure hub
'jwt' => '...', // string or anonymous function returning string with JWT (see details below)
'httpClient' => '...', // HTTP client (see details below)
'useYii2Client' => true, // HTTP client mode (see details below)
],
],
配置详细信息
hubUrl
Mercure 中心的 URL。jwt
JSON Web Token 或返回它的匿名函数。有关更多信息,请参阅 授权 部分。httpClient
带有已注册 HTTP 客户端组件名称的字符串、带有 HTTP 客户端配置的数组,或实际的 HTTP 客户端对象。当useYii2Client
选项设置为 true(默认)时,此选项应指向 Yii 2 HTTP 客户端 组件。如果您想使用它,您必须像链接中描述的那样安装它,并在配置中注册它(因此您可以将它设置为'httpClient' => 'client-component-name'
),或者为它提供数组配置(如'httpClient' => ['class' => \yii\httpclient\Client::class]
)。useYii2Client
布尔标志,表示此组件是否应将 Yii 2 HTTP 客户端 作为 HTTP 客户端(默认为true
)或其他自定义 HTTP 客户端(false
)。
用法
应用程序必须携带一个 JSON Web Token(JWT)到 Mercure 中心以获得发布更新的授权。
此 JWT 应存储在前面提到的 jwt
属性中。
JWT 必须使用与 Hub 用于验证 JWT 相同的密钥进行签名(默认 Mercure 演示密钥是 !ChangeMe!
- 不应在生产中使用)。其负载必须至少包含以下结构才能获得发布权限
{
"mercure": {
"publish": []
}
}
由于数组为空,应用程序将仅被授权发布公开更新(有关更多信息,请参阅 授权 部分)。
提示:jwt.io 网站是创建和签名 JWT 的便捷方式。查看此 示例 JWT,该 JWT 授予所有目标发布权限(注意数组中的星号)。不要忘记在表单右侧面板的底部正确设置您的密钥!
当您想向 Mercure 中心发布更新时,只需调用
\Yii::$app->mercure->publish($update);
其中 $update
是 \bizley\yii2\mercure\Update
类的实例。例如
\Yii::$app->mercure->publish(
new \bizley\yii2\mercure\Update(
'http://example.com/books/1',
\yii\helpers\Json::encode(['status' => 'OutOfStock'])
)
);
传递给Update
构造函数的第一个参数是要更新的主题。这个主题应该是一个IRI(国际资源标识符,RFC 3987):正在派发的资源的唯一标识符。
通常,此参数包含传递给客户端的资源原始URL,但它可以是任何有效的IRI,不必是存在的URL(类似于XML命名空间)。
构造函数的第二个参数是更新的内容。它可以包含任何内容,存储在任何格式中。但是,建议将资源序列化为JSON-LD、Atom、HTML或XML等超媒体格式。
使用JavaScript订阅客户端
const eventSource = new EventSource(
'https://:3000/hub?topic=' + encodeURIComponent('http://example.com/books/1')
);
eventSource.onmessage = event => {
// Will be called every time an update is published by the server
console.log(JSON.parse(event.data));
}
Mercure还允许订阅多个主题,并使用URI模板作为模式
// URL is a built-in JavaScript class to manipulate URLs
const url = new URL('https://:3000/hub');
url.searchParams.append('topic', 'http://example.com/books/1');
// Subscribe to updates of several Book resources
url.searchParams.append('topic', 'http://example.com/books/2');
// All Review resources will match this pattern
url.searchParams.append('topic', 'http://example.com/reviews/{id}');
const eventSource = new EventSource(url);
eventSource.onmessage = event => {
console.log(JSON.parse(event.data));
}
发现
Mercure协议自带发现机制。要利用它,应用程序必须在Link
HTTP头中公开Mercure Hub的URL。
namespace app\controllers;
use yii\web\Controller;
class BookController extends Controller
{
public function actionView($id)
{
$hubUrl = 'https://:3000/hub';
$response = $this->asJson([
'@id' => '/books/' . $id,
'availability' => 'https://schema.org/InStock',
]);
$response->getHeaders()->set('Link', "<$hubUrl>; rel=mercure");
return $response;
}
}
然后,客户端可以解析此头以找到Hub的URL,并对其进行订阅
// Fetch the original resource served by the web API
fetch('/books/1') // Has Link: <https://:3000/hub>; rel=mercure
.then(response => {
// Extract the hub URL from the Link header
const hubUrl = response.headers.get('Link').match(/<([^>]+)>;\s+rel=(?:mercure|"[^"]*mercure[^"]*")/)[1];
// Append the topic(s) to subscribe as query parameter
const hub = new URL(hubUrl);
hub.searchParams.append('topic', 'http://example.com/books/{id}');
// Subscribe to updates
const eventSource = new EventSource(hub);
eventSource.onmessage = event => console.log(event.data);
});
授权
Mercure还允许仅向授权客户端派发更新。为此,将允许接收更新的目标列表设置为Update构造函数的第三个参数
\Yii::$app->mercure->publish(
new \bizley\yii2\mercure\Update(
'http://example.com/books/1',
\yii\helpers\Json::encode(['status' => 'OutOfStock']),
['http://example.com/user/kevin', 'http://example.com/groups/admin'] // Here are the targets
)
);
发布者的JWT必须包含所有这些目标或在mercure.publish
中使用*
,否则您将收到401。
订阅者的JWT必须包含这些目标中的一个或*
在mercure.subscribe
中以接收更新。
要订阅私人更新,订阅者必须提供一个包含至少一个目标以标记更新到Hub的JWT。
要提供此JWT,订阅者可以使用cookie或Authorization
HTTP头。当浏览器打开EventSource
连接时,浏览器会自动发送cookie。当客户端是浏览器时,这是最安全和首选的方法。如果客户端不是浏览器,则使用授权头是最佳选择。
在以下示例控制器中,生成的cookie包含一个JWT,该JWT本身包含适当的目标。当浏览器连接到Hub时,浏览器会自动发送此cookie。然后,Hub将验证提供的JWT的有效性,并从中提取目标。
要生成JWT,我们将使用bizley/jwt
,这是一个带有lcobucci/jwt
库的Yii 2组件。安装它
composer require bizley/jwt
并配置
'components' => [
'jwt' => [
'class' => \bizley\jwt\Jwt::class,
'key' => '!ChangeMe!' // default Mercure demo key not to be used on production
],
],
现在控制器
namespace app\controllers;
use bizley\jwt\JWT;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Yii;
use yii\web\Controller;
use yii\web\Cookie;
class BookController extends Controller
{
public function actionView($id)
{
$hubUrl = 'https://:3000/hub';
$username = Yii::$app->user->name; // Retrieve the username of the current user
$token = Yii::$app->jwt->getBuilder()
// set other appropriate JWT claims, such as an expiration date
->set(
'mercure',
['subscribe' => ["http://example.com/user/$username"]]
// could also include the security roles, or anything else
)
->sign(new Sha256(), Yii::$app->jwt->key)
->getToken();
$response = $this->asJson([
'@id' => '/books/' . $id,
'availability' => 'https://schema.org/InStock',
]);
$response->getHeaders()->set('Link', "<$hubUrl>; rel=mercure");
$response->cookies->add(new Cookie([
'name' => 'mercureAuthorization',
'value' => $token,
'path' => '/hub',
'secure' => true,
'sameSite' => Cookie::SAME_SITE_STRICT, // from PHP 7.3 and Yii 2.0.21
]));
return $response;
}
}
注意:要使用cookie身份验证方法,应用程序和Hub必须来自同一个域名(可以是不同的子域名)。
本文档的一些部分是从Symfony的“使用Mercure协议向客户端推送数据”页面复制的。