diff --git a/developer_manual/basics/controllers.rst b/developer_manual/basics/controllers.rst index 34bb1ddc314..c8de6729c03 100644 --- a/developer_manual/basics/controllers.rst +++ b/developer_manual/basics/controllers.rst @@ -335,11 +335,176 @@ Cookies can be set or modified directly on the response class: } } - Responses --------- -Similar to how every controller receives a request object, every controller method has to return a Response. This can be in the form of a Response subclass or in the form of a value that can be handled by a registered responder. +Similar to how every controller receives a request object, every controller method has to return a Response. +This can be in the form of a Response subclass or in the form of a value that can be handled by a registered responder. + +There are different kinds of responses available, like HTML-based responses, data responses, or other. +The app decides of which kind the response is, by returning an appropriate ``Response`` object in the corresponding controller method. +The following sections give an overview over the various kinds and how to implement them. + +.. _controller_html_responses: + +HTML-based Responses +-------------------- + +HTML pages are typically served using template responses. +This is typically used as a starting point to load the website. +This code linked by the template is by default encapsulated by the server to provide some common styling (e.g. the header row). +The code then uses JavaScript to load further components (see :ref:`Frontend building in Vue`) and the actual data. +This section only focuses on the actual HTML content, not the data to fill into the dynamic pages. + +.. _controller_template: + +Templates +^^^^^^^^^ + +A :doc:`template ` can be rendered by returning a TemplateResponse. A TemplateResponse takes the following parameters: + +* **appName**: tells the template engine in which app the template should be located +* **templateName**: the name of the template inside the templates/ folder without the .php extension +* **parameters**: optional array parameters that are available in the template through $_, e.g.:: + + array('key' => 'something') + + can be accessed through:: + + $_['key'] + +* **renderAs**: defaults to *user*, tells Nextcloud if it should include it in the web interface, or in case *blank* is passed solely render the template + +.. code-block:: php + + 'hi'); + return new TemplateResponse($this->appName, $templateName, $parameters); + } + + } + +Public page templates +^^^^^^^^^^^^^^^^^^^^^ + +For public pages, that are rendered to users who are not logged in to the +Nextcloud instance, a ``OCP\\AppFramework\\Http\\Template\\PublicTemplateResponse`` should be used, to load the +correct base template. It also allows adding an optional set of actions that +will be shown in the top right corner of the public page. + + +.. code-block:: php + + appName, 'main', []); + $template->setHeaderTitle('Public page'); + $template->setHeaderDetails('some details'); + $template->setHeaderActions([ + new SimpleMenuAction('download', 'Label 1', 'icon-css-class1', 'link-url', 0), + new SimpleMenuAction('share', 'Label 2', 'icon-css-class2', 'link-url', 10), + ]); + return $template; + } + + } + +The header title and subtitle will be rendered in the header, next to the logo. +The action with the highest priority (lowest number) will be used as the +primary action, others will shown in the popover menu on demand. + +A ``OCP\\AppFramework\\Http\\Template\\SimpleMenuAction`` will be a link with an icon added to the menu. App +developers can implement their own types of menu renderings by adding a custom +class implementing the ``OCP\\AppFramework\\Http\\Template\\IMenuAction`` interface. + +Data-based responses +-------------------- + +In contrast to the HTML template responses, the data responses return some user-data in packed form. +There are different encodings thinkable like JSON, XML, or other formats. +The main point is that the data is requested by the browser using JavaScript on behalf of the shown website. +The user only indirectly requested the data by user interaction with the frontend. + + +.. _ocscontroller: + +OCS +^^^ + +.. note:: + This is purely for compatibility reasons. If you are planning to offer an external API, go for a :ref:`REST APIs ` instead. + +In order to simplify exchange of data between the Nextcloud backend and any client (be it the web frontend or whatever else), the OCS API has been introduced. +To use OCS in your API you can use the **OCP\\AppFramework\\OCSController** base class and return your data in the form of a **DataResponse** in the following way: + +.. code-block:: php + + ` can be registered as with any other ``Controller`` method. +The ``OCSController`` class have however automatically two responders pre-installed: +Both JSON (``application/json``) and XML (``text/xml``) are generated on-the-fly depending on the request by the browser/user. +To select the output format, the ``?format=`` query parameter or the ``Accept`` header of the request work out of the box, no intervention is required. +It is advised to prefer the header generally, as this is the more standardized way. + +To make routing work for OCS, the route must be registered in the core. +This can be done in two ways: +You can add an attribute `#[ApiRoute]` to the controller method. +Alternatively, you can add :ref:`a separate 'ocs' entry` to the routing table in ``appinfo/routes.php`` of your app. +Inside these, there are the same information as there are for normal routes. + +.. code-block:: php + + [ + [ + 'name' => 'Share#getShares', + 'url' => '/api/v1/shares', + 'verb' => 'GET', + ], + ], + ]; + +Now your method will be reachable via ``/ocs/v2.php/apps//api/v1/shares`` + +.. versionadded:: 29 + You can use the attribute ``ApiRoute`` as described in :doc:`Routing ` instead of the entry in ``appinfo/routes.php`` as an alternative. + JSON ^^^^ @@ -382,6 +547,36 @@ Because returning JSON is such a common task, there's even a shorter way to do t Why does this work? Because the dispatcher sees that the controller did not return a subclass of a Response and asks the controller to turn the value into a Response. That's where responders come in. +Handling errors +^^^^^^^^^^^^^^^ + +Sometimes a request should fail, for instance if an author with id 1 is requested but does not exist. In that case use an appropriate `HTTP error code `_ to signal the client that an error occurred. + +Each response subclass has access to the **setStatus** method which lets you set an HTTP status code. To return a JSONResponse signaling that the author with id 1 has not been found, use the following code: + +.. code-block:: php + + ` can be rendered by returning a TemplateResponse. A TemplateResponse takes the following parameters: - -* **appName**: tells the template engine in which app the template should be located -* **templateName**: the name of the template inside the templates/ folder without the .php extension -* **parameters**: optional array parameters that are available in the template through $_, e.g.:: - - array('key' => 'something') - - can be accessed through:: - - $_['key'] - -* **renderAs**: defaults to *user*, tells Nextcloud if it should include it in the web interface, or in case *blank* is passed solely render the template - -.. code-block:: php - - 'hi'); - return new TemplateResponse($this->appName, $templateName, $parameters); - } - - } - -Public page templates -^^^^^^^^^^^^^^^^^^^^^ - -For public pages, that are rendered to users who are not logged in to the -Nextcloud instance, a ``OCP\\AppFramework\\Http\\Template\\PublicTemplateResponse`` should be used, to load the -correct base template. It also allows adding an optional set of actions that -will be shown in the top right corner of the public page. - - -.. code-block:: php - - appName, 'main', []); - $template->setHeaderTitle('Public page'); - $template->setHeaderDetails('some details'); - $template->setHeaderActions([ - new SimpleMenuAction('download', 'Label 1', 'icon-css-class1', 'link-url', 0), - new SimpleMenuAction('share', 'Label 2', 'icon-css-class2', 'link-url', 10), - ]); - return $template; - } - - } - -The header title and subtitle will be rendered in the header, next to the logo. -The action with the highest priority (lowest number) will be used as the -primary action, others will shown in the popover menu on demand. - -A ``OCP\\AppFramework\\Http\\Template\\SimpleMenuAction`` will be a link with an icon added to the menu. App -developers can implement their own types of menu renderings by adding a custom -class implementing the ``OCP\\AppFramework\\Http\\Template\\IMenuAction`` interface. - - +Some special responses are available as well. +These are discussed here. Redirects ^^^^^^^^^ @@ -643,9 +765,6 @@ By default all responses are rendered at once and sent as a string through middl } - - - If you want to use a custom, lazily rendered response simply implement the interface **OCP\\AppFramework\\Http\\ICallbackResponse** for your response: .. code-block:: php @@ -666,45 +785,57 @@ If you want to use a custom, lazily rendered response simply implement the inter .. note:: Because this code is rendered after several usually built in helpers, you need to take care of errors and proper HTTP caching by yourself. -Modifying the content security policy -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Security considerations +----------------------- -By default Nextcloud disables all resources which are not served on the same domain, forbids cross domain requests and disables inline CSS and JavaScript by setting a `Content Security Policy `_. -However if an app relies on third-party media or other features which are forbidden by the current policy the policy can be relaxed. +Depending on your use-case, you can tighten or loosen the security measurements installed by default on the routes. +This section gives you a quick overview over the options. -.. note:: Double check your content and edge cases before you relax the policy! Also read the `documentation provided by MDN `_ +.. _controller_authentication: -To relax the policy pass an instance of the ContentSecurityPolicy class to your response. The methods on the class can be chained. +Authentication +^^^^^^^^^^^^^^ -The following methods turn off security features by passing in **true** as the **$isAllowed** parameter +By default every controller method enforces the maximum security, which is: -* **allowInlineScript** (bool $isAllowed) -* **allowInlineStyle** (bool $isAllowed) -* **allowEvalScript** (bool $isAllowed) -* **useStrictDynamic** (bool $isAllowed) +* Ensure that the user is admin +* Ensure that the user is logged in +* Ensure that the user has passed the two-factor challenge, if applicable +* Ensure the request is no CSRF attack, that is at least one of the following: - Trust all scripts that are loaded by a trusted script, see 'script-src' and 'strict-dynamic' + - Ensure the CSRF token is present and valid + - Ensure the ``OCS-APIRequest`` header is present and set to ``true`` [1]_ -* **useStrictDynamicOnScripts** (bool $isAllowed) +Loosening the default restrictions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Trust all scripts that are loaded by a trusted script which was loaded using a ``