Skip to content

Commit 13db8cc

Browse files
Merge pull request #5821 from nextcloud/enhancement/dev-web-host-metadata
Document the new Web Host Metadata API for apps
2 parents 33bd1ea + 02700f6 commit 13db8cc

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

developer_manual/app_publishing_maintenance/upgrade-guide.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ Last version with database.xml support and migration
2424

2525
Nextcloud 21 is the last major release that supports an app's ``appinfo/database.xml`` to :ref:`define the database schema<database-xml>`. This is your last change to :ref:`automatically convert this deprecated file into the new migration classes<migrate-database-xml>`.
2626

27+
Replaced well-known handler API
28+
*******************************
29+
30+
There was an old, unused and inofficial mechanism to hook into well-known discovery via config settings. This includes ``host-meta``, ``host-meta.json``, ``nodeinfo`` and ``webfinger``. A :ref:`new public API replaces this mechanism<web-host-metadata>` in Nextcloud 21.
31+
2732
Upgrading to Nextcloud 20
2833
-------------------------
2934

developer_manual/digging_deeper/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ Digging deeper
2525
two-factor-provider
2626
users
2727
dashboard
28+
web_host_metadata
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
.. _web-host-metadata:
2+
3+
=================
4+
Web Host Metadata
5+
=================
6+
7+
`RFC6415`_ defines how web hosts can expose their metadata through resources. Starting with Nextcloud 21, it's possible to register handlers for HTTP requests to the ``.well-known/*`` route.
8+
9+
Writing a handler
10+
-----------------
11+
12+
A well known handler is a simple class that implements the ``\OCP\Http\WellKnown\IHandler`` interface.
13+
14+
.. code-block:: php
15+
16+
<?php
17+
18+
declare(strict_types=1);
19+
20+
namespace OCA\MyApp\Http\WellKnown;
21+
22+
class Handler implements IHandler {
23+
24+
public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse {
25+
// the handler-specific logic
26+
}
27+
28+
}
29+
30+
The basic concept is that every handler will be called consecutively. A handler can react to the request and return a new response object or modify the one of the previous handler. The first handler will get a ``$previousResponse`` of null. The second handler gets whatever the first handler returned, so either ``null`` or an instance of ``\OCP\Http\WellKnown\IResponse``.
31+
32+
33+
Example generic handler
34+
^^^^^^^^^^^^^^^^^^^^^^^
35+
36+
.. code-block:: php
37+
38+
<?php
39+
40+
declare(strict_types=1);
41+
42+
namespace OCA\MyApp\Http\WellKnown;
43+
44+
use OCP\AppFramework\Http\JSONResponse;
45+
use OCP\Http\WellKnown\GenericResponse;
46+
use OCP\Http\WellKnown\IHandler;
47+
use OCP\Http\WellKnown\IRequestContext;
48+
use OCP\Http\WellKnown\IResponse;
49+
use OCP\IURLGenerator;
50+
51+
class GenericHandler implements IHandler {
52+
53+
public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse {
54+
if ($service !== 'nextcloudtest') {
55+
// Not relevant to this handler
56+
57+
return $previousResponse;
58+
}
59+
60+
return new GenericResponse(
61+
new JSONResponse(['message' => 'hello']),
62+
);
63+
}
64+
}
65+
66+
67+
Example webfinger handler
68+
^^^^^^^^^^^^^^^^^^^^^^^^^
69+
70+
The following example shows how an app could react to `RFC6415`_ webfinger requests:
71+
72+
.. code-block:: php
73+
74+
<?php
75+
76+
declare(strict_types=1);
77+
78+
namespace OCA\MyApp\Http\WellKnown;
79+
80+
use OCP\Http\WellKnown\IHandler;
81+
use OCP\Http\WellKnown\IRequestContext;
82+
use OCP\Http\WellKnown\IResponse;
83+
use OCP\Http\WellKnown\JrdResponse;
84+
use OCP\IURLGenerator;
85+
86+
class WebFingerHandler implements IHandler {
87+
88+
/** @var IURLGenerator */
89+
private $urlGenerator;
90+
91+
public function __construct(IURLGenerator $urlGenerator) {
92+
$this->urlGenerator = $urlGenerator;
93+
}
94+
95+
public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse {
96+
if ($service !== 'webfinger') {
97+
// Not relevant to this handler
98+
99+
return $previousResponse;
100+
}
101+
102+
$subject = $context->getHttpRequest()->getParam('resource', '');
103+
$href = $this->urlGenerator->linkToRouteAbsolute('myapp.example.test');
104+
105+
// Use the previous response and amend it, if possible
106+
$response = $previousResponse;
107+
if (!($response instanceof JrdResponse)) {
108+
// We override null or any other types
109+
$response = new JrdResponse($subject);
110+
}
111+
112+
return $response->addLink('self', 'application/activity+json', $href);
113+
}
114+
}
115+
116+
Handler registration
117+
--------------------
118+
119+
The handler class is registered via the :ref:`bootstrap mechanism<Bootstrapping>` of the ``Application`` class.
120+
121+
.. code-block:: php
122+
123+
124+
<?php
125+
126+
declare(strict_types=1);
127+
128+
namespace OCA\MyApp\AppInfo;
129+
130+
use OCA\MyApp\Http\WellKnown\Handler;
131+
use OCP\AppFramework\App;
132+
use OCP\AppFramework\Bootstrap\IBootContext;
133+
use OCP\AppFramework\Bootstrap\IBootstrap;
134+
use OCP\AppFramework\Bootstrap\IRegistrationContext;
135+
136+
class Application extends App implements IBootstrap {
137+
138+
public function register(IRegistrationContext $context): void {
139+
$context->registerWellKnownHandler(Handler::class);
140+
}
141+
142+
public function boot(IBootContext $context): void {}
143+
144+
}
145+
146+
147+
148+
.. _`RFC6415`: https://tools.ietf.org/html/rfc6415
149+
.. _`RFC7033`: https://tools.ietf.org/html/rfc7033

0 commit comments

Comments
 (0)