使用Mercure协议向客户端推送数据
编辑本页异步作业已经完成或创建聊天应用程序是需要“推送”功能的典型用例。
欧宝娱乐app下载地址Symfony提供了一个简单的组件美居协议,专门为这类用例设计的。
Mercure是一种开放协议,用于从服务器向客户端发布更新。它是基于计时器的轮询和WebSocket的现代而有效的替代方案。
因为它建在上面服务器发送事件(SSE)Mercure在现代浏览器中是开箱即用的(旧版本的Edge和IE需要)一个polyfill),并有高层实现在许多编程语言中。
Mercure提供了一种授权机制,在检索丢失的更新时出现网络问题时自动重新连接,一个在线API,智能手机的“无连接”推送和自动发现(受支持的客户端可以通过特定的HTTP头自动发现和订阅给定资源的更新)。
Symfony集成支持所有这些特性。欧宝娱乐app下载地址
在这段录音中你可以看到Symfony web A欧宝娱乐app下载地址PI如何利用Mercure和API平台来实时更新一个React应用程序和一个使用API平台客户端生成器生成的移动应用程序(React Native)。
为了管理持久连接,Mercure依赖于一个集线器:一个专门处理与客户端的持久SSE连接的服务器。Symf欧宝娱乐app下载地址ony应用程序将更新发布到中心,中心将广播给客户端。
多亏了Symfony的Docker集成欧宝娱乐app下载地址,0b足球
建议安装一个美居中心。运行docker-compose起来
如果选择此选项,则启动中心。
如果你使用欧宝娱乐app下载地址Symfony本地Web服务器,你必须从。开始——no-tls
选择。
1
$欧宝娱乐app下载地址Symfony服务器:启动——no-tls -d
环境变量.
安装MercureBundle后,将.env
你的项目文件已经被Flex配方更新到包含可用的env变量。
此外,如果您正在使用Docker与Symfony本地Web服务器的集成,欧宝娱乐app下载地址欧宝娱乐app下载地址Symfony码头工人或者是API平台分布时,已自动设置适当的环境变量。直接跳到下一节。
的值设置集线器的URLMERCURE_URL
而且MERCURE_PUBLIC_URL
env var。有时,Symfony应用程序(通常用于发布)和JavaScript客户端(通常用于订阅)必须调用欧宝娱乐app下载地址不同的URL。当Symfony应用程序必须使用本地URL而客户端JavaScr欧宝娱乐app下载地址ipt代码必须使用公共URL时,这种情况尤其常见。在这种情况下,MERCURE_URL
必须包含Symfony应用程序使用的本地URL(例如:欧宝娱乐app下载地址https://mercure/.well-known/mercure
),MERCURE_PUBLIC_URL
公开可用的URL(例如:https://example.com/.well-known/mercure
).
客户还必须承担JSON Web令牌(JWT)向Mercure Hub授权发布更新,有时还授权订阅。
此令牌必须使用与Hub用于验证JWT (ChangeThisMercureHubJWTSecretKey !
如果你使用Docker集成)。此密钥必须存储在MERCURE_JWT_SECRET
环境变量。MercureBundle将使用它自动生成和签署所需的jwt。
除了这些环境变量,MercureBundle还提供了一个更高级的配置:
秘密
:用于签署JWT的密钥-必须使用与哈希输出相同大小的密钥(例如,256位的“HS256”)或更大的密钥。(所有其他选择,除了算法
,订阅
,发布
将被忽略)发布
:生成JWT时允许发布的主题列表(仅在秘密
,或工厂
提供)订阅
:生成JWT时允许订阅的主题列表(仅在秘密
,或工厂
提供)算法
:用于签名JWT的算法(仅在秘密
提供)提供者
:要调用的服务ID,以提供JWT(所有其他选项将被忽略)工厂
:要调用的服务ID以创建JWT(除了订阅
,发布
将被忽略)价值
:要使用的原始JWT(所有其他选项将被忽略)
- YAML
- XML
- PHP
12 3 4 5 6 7 8 9 10 11 12 13
#配置/包/ mercure.yaml美居酒店:中心:默认值:url:https://mercure-hub.example.com/.well-known/mercurejwt:秘密:“ChangeThisMercureHubJWTSecretKey !”发布:(“foo”,“https://example.com/foo”]订阅:['酒吧',“https://example.com/bar”]算法:“hmac.sha256”供应商:“我的\提供者”工厂:“我的\工厂”值:“my.jwt”
更新值对象表示要发布的更新。它还提供了出版商
服务将更新发送到中心。
的出版商
服务可以使用自动装配在任何其他服务中,包括控制器:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21 22
/ / src /控制器/ PublishController.php名称空间应用程序\控制器;使用欧宝娱乐app下载地址\包\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\美居酒店\HubInterface;使用欧宝娱乐app下载地址\组件\美居酒店\更新;类PublishController扩展AbstractController{公共函数发布(HubInterface$中心):响应{$更新=新更新(“https://example.com/books/1”, json_encode (“状态”= >“OutOfStock”)));$中心->发布($更新);返回新响应(“发表!”);}}
对象传递的第一个参数更新
构造函数是主题被更新。这个话题应该是一个IRI(国际化资源标识符,RFC 3987):被分派资源的唯一标识符。
通常,此参数包含传输到客户端的资源的原始URL,但它可以是任何字符串或IRI,它不必是一个存在的URL(类似于XML名称空间)。
构造函数的第二个参数是更新的内容。它可以是任何东西,以任何格式存储。但是,建议以超媒体格式(如JSON-LD、Atom、HTML或XML)序列化资源。
出版商
服务将更新发送到中心。
的出版商
服务可以使用自动装配在任何其他服务中,包括控制器:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21 22
/ / src /控制器/ PublishController.php名称空间应用程序\控制器;使用欧宝娱乐app下载地址\包\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\美居酒店\HubInterface;使用欧宝娱乐app下载地址\组件\美居酒店\更新;类PublishController扩展AbstractController{公共函数发布(HubInterface$中心):响应{$更新=新更新(“https://example.com/books/1”, json_encode (“状态”= >“OutOfStock”)));$中心->发布($更新);返回新响应(“发表!”);}}
对象传递的第一个参数更新
构造函数是主题被更新。这个话题应该是一个IRI(国际化资源标识符,RFC 3987):被分派资源的唯一标识符。
通常,此参数包含传输到客户端的资源的原始URL,但它可以是任何字符串或IRI,它不必是一个存在的URL(类似于XML名称空间)。
构造函数的第二个参数是更新的内容。它可以是任何东西,以任何格式存储。但是,建议以超媒体格式(如JSON-LD、Atom、HTML或XML)序列化资源。
的美居酒店()
Twig函数根据配置生成Mercure hub的URL。URL包含主题
查询与作为第一个参数传递的主题对应的参数。
如果你想从外部JavaScript文件访问这个URL,在一个专用的HTML元素中生成URL:
1 2 3
<脚本类型=“application / json”id=“mercure-url”>{{美居酒店|(“https://example.com/books/1”)json_encode(常数(“JSON_UNESCAPED_SLASHES”)b或常数(“JSON_HEX_TAG”)) |生}}脚本>
然后从JS文件中检索它:
1 2 3
常量url =JSON.parse (文档.getElementById (“mercure-url”) .textContent);常量eventSource =新EventSource (url);/ /……
Mercure还允许订阅多个主题,并使用URI模板或特殊值*
(与所有主题匹配)作为模式:
12 3 4 5 6 7 8 9 10 11 12
<脚本>{#订阅几个Book资源和所有匹配给定模式的Review资源的更新#}常量eventSource =新EventSource ("{{水银([' https://example.com/books/1 ', ' https://example.com/books/2 ', ' https://example.com/reviews/ {id} ']) |逃避(js)}}”);eventSource。onmessage =事件= >{控制台.log(JSON.parse(event.data)); }脚本>
提示
谷歌Chrome开发工具本机集成一个实用的用户界面实时显示接收到的事件:
使用它:
- 打开DevTools
- 选择“网络”选项卡
- 单击到Mercure集线器的请求
- 点击“EventStream”子选项卡。
提示
测试URI模板是否与使用的URL匹配在线调试器
链接HTTP报头。
你可以创建链接
的头文件发现
Helper类(在底层,它使用连接组件):
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21
/ / src /控制器/ DiscoverController.php名称空间应用程序\控制器;使用欧宝娱乐app下载地址\包\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\JsonResponse;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\组件\美居酒店\发现;类DiscoverController扩展AbstractController{公共函数发现(请求$请求,发现$发现):JsonResponse{//链接:;rel = "美居酒店" $发现->通过addLink ($请求);返回$这->json ([“@ id”= >“/书籍/ 1”,“可用性”= >“https://schema.org/InStock”]);}}
然后,这个头可以在客户端解析,以找到Hub的URL,并订阅它:
12 3 4 5 6 7 8 9 10 11 12 13 14
//获取Symfony web API服务的原始资源欧宝娱乐app下载地址fetch (“/书籍/ 1”)//有链接:;rel = "美居酒店" 不要犹豫(响应= >{//从Link头中提取hub URL常量hubUrl = response.headers.get(“链接”) .match (/<([^>]+)>;\ s + rel =(?:美居酒店|“^”*水银[^]*)/) [1];//添加主题订阅作为查询参数常量中心=新URL (hubUrl,窗口.origin);hub.searchParams.append (“主题”,“https://example.com/books/ {id}”);//订阅更新常量eventSource =新EventSource(中心);eventSource。onmessage =事件= >控制台. log (event.data);});
链接
的头文件发现
Helper类(在底层,它使用连接组件):12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21
/ / src /控制器/ DiscoverController.php名称空间应用程序\控制器;使用欧宝娱乐app下载地址\包\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\JsonResponse;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\组件\美居酒店\发现;类DiscoverController扩展AbstractController{公共函数发现(请求$请求,发现$发现):JsonResponse{//链接:;rel = "美居酒店" $发现->通过addLink ($请求);返回$这->json ([“@ id”= >“/书籍/ 1”,“可用性”= >“https://schema.org/InStock”]);}}
12 3 4 5 6 7 8 9 10 11 12 13 14
//获取Symfony web API服务的原始资源欧宝娱乐app下载地址fetch (“/书籍/ 1”)//有链接:;rel = "美居酒店" 不要犹豫(响应= >{//从Link头中提取hub URL常量hubUrl = response.headers.get(“链接”) .match (/<([^>]+)>;\ s + rel =(?:美居酒店|“^”*水银[^]*)/) [1];//添加主题订阅作为查询参数常量中心=新URL (hubUrl,窗口.origin);hub.searchParams.append (“主题”,“https://example.com/books/ {id}”);//订阅更新常量eventSource =新EventSource(中心);eventSource。onmessage =事件= >控制台. log (event.data);});
更新构造函数真正的
:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/ / src /控制器/ Publish.php名称空间应用程序\控制器;使用欧宝娱乐app下载地址\包\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\美居酒店\更新;类PublishController扩展AbstractController{公共函数发布(HubInterface$中心):响应{$更新=新更新(“https://example.com/books/1”, json_encode (“状态”= >“OutOfStock”]),真正的/ /私有);//发布者的JWT必须包含该主题,它匹配的URI模板或*在mercure中。出版,否则你就得退休了//订阅者的JWT必须包含该主题、它匹配的URI模板或mercure中的*。订阅以接收更新$中心->发布($更新);返回新响应(“私人更新已发布!”);}}
要订阅私有更新,订阅者必须向Hub提供包含与更新主题匹配的主题选择器的JWT。
要提供这个JWT,订阅者可以使用cookie或授权
HTTP报头。
通过将适当的选项传递给欧宝娱乐app下载地址美居酒店()
树枝的功能。由Symfony设置的cook欧宝娱乐app下载地址ie被浏览器自动传递到Mercure中心withCredentials
属性。EventSource
类设置为真正的
.然后,Hub验证所提供的JWT的有效性,并从中提取主题选择器。
1 2 3 4 5
<脚本>常量eventSource =新EventSource ("{{mercure('https://example.com/books/1', {subscribe: 'https://example.com/books/1'})|逃避(js)}}, {withCredentials: true});脚本>
支持的选项有:
订阅
的主题选择器列表mercure.subscribe
JWT的声明
发布
的主题选择器列表mercure.publish
JWT的声明
additionalClaims
: JWT中包含的额外声明(过期日期,令牌ID…)
当客户端是web浏览器时,使用cookie是最安全和首选的方式。如果客户端不是web浏览器,那么使用授权标头是正确的方法。
谨慎
要使用cookie身份验证方法,Symfony应用程序和Hub必须从相同的域(可以是不同的欧宝娱乐app下载地址子域)提供服务。
提示
EventSource的本机实现不允许指定头文件。例如,使用承载令牌进行授权。为了达到这个目的,使用一个polyfill
1 2 3 4 5 6 7
<脚本>常量es =新EventSourcePolyfill ("{{mercure('https://example.com/books/1')}}, {headers: {'Authorization': ' holder ' + token,}});脚本>
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/ / src /控制器/ Publish.php名称空间应用程序\控制器;使用欧宝娱乐app下载地址\包\FrameworkBundle\控制器\AbstractController;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\美居酒店\更新;类PublishController扩展AbstractController{公共函数发布(HubInterface$中心):响应{$更新=新更新(“https://example.com/books/1”, json_encode (“状态”= >“OutOfStock”]),真正的/ /私有);//发布者的JWT必须包含该主题,它匹配的URI模板或*在mercure中。出版,否则你就得退休了//订阅者的JWT必须包含该主题、它匹配的URI模板或mercure中的*。订阅以接收更新$中心->发布($更新);返回新响应(“私人更新已发布!”);}}
授权
HTTP报头。美居酒店()
树枝的功能。由Symfony设置的cook欧宝娱乐app下载地址ie被浏览器自动传递到Mercure中心withCredentials
属性。EventSource
类设置为真正的
.然后,Hub验证所提供的JWT的有效性,并从中提取主题选择器。1 2 3 4 5
<脚本>常量eventSource =新EventSource ("{{mercure('https://example.com/books/1', {subscribe: 'https://example.com/books/1'})|逃避(js)}}, {withCredentials: true});脚本>
订阅
的主题选择器列表mercure.subscribe
JWT的声明发布
的主题选择器列表mercure.publish
JWT的声明additionalClaims
: JWT中包含的额外声明(过期日期,令牌ID…)谨慎
要使用cookie身份验证方法,Symfony应用程序和Hub必须从相同的域(可以是不同的欧宝娱乐app下载地址子域)提供服务。
提示
EventSource的本机实现不允许指定头文件。例如,使用承载令牌进行授权。为了达到这个目的,使用一个polyfill
1 2 3 4 5 6 7
<脚本>常量es =新EventSourcePolyfill ("{{mercure('https://example.com/books/1')}}, {headers: {'Authorization': ' holder ' + token,}});脚本>
然后,创建以下实体就足以获得一个全功能的超媒体API,并通过Mercure hub自动更新广播:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src /实体/ Book.php名称空间应用程序\实体;使用ApiPlatform\核心\注释\ApiResource;使用学说\ORM\映射作为ORM;# (ApiResource(水银:真)]# (ORM \实体)类书{# (ORM \ Id)# (ORM \列)公共字符串$的名字=”;# (ORM \列)公共字符串$状态=”;}
作为展示在这段录音中, API平台客户端生成器还允许从这个API构建完整的React和React Native应用程序。这些应用程序将实时呈现Mercure更新的内容。
结帐专用的API平台文档欧宝体育电话以了解更多有关Mercure的支持。