Classic REST controllers

To create a REST API endpoint, create an .es6 file in your plugin's rest/controllers/ folder, e.g. "UM/cmsbs-conf/cse/plugins/de.mycompany.restdemo/rest/controllers/DemoController.es6":

class DemoController { constructor() { allowActions(this, "document_GET"); allowActions(this, "document_POST"); allowActions(this, "foo_GET"); protectFromForgery(this); protectFromCaching(this); applyCorsPolicy(this); handlePreflightRequests(this); } document_GET() { // Will be called when requesting GET <http://localhost:8080/cmsbs/rest/de.mycompany.restdemo.Document/document> UM.dump(params, "params:"); } document_POST() { // Will be called when requesting POST <http://localhost:8080/cmsbs/rest/de.mycompany.restdemo.Document/document> UM.dump(params, "params:"); UM.dump(request, "request:"); } foo_GET() { // Will be called when requesting GET <http://localhost:8080/cmsbs/rest/de.mycompany.restdemo.Document/document> } beforeInterceptor() { // Add your custom authorization code here. return true; } } de.mycompany.restdemo.DocumentController = DocumentController;

allowActions

allowActions() defined, which actions may be requested using which HTTP method(s).

protectFromForgery

protectFromForgery() tells the controller to accept POST, PUT and DELETE requests only if the request header X-CSRF-Token is provided and contains the correct CSRF Token.
The CSRF Token will be returned with every request as the response header named X-CSRF-Token to the client.

protectFromCaching

Sets the Cache-Control header appropriately to prevent client and reverse proxies from caching any of the responses.

applyCorsPolicy

applyCorsPolicy() tells the controller to send Access-Control-Allow headers for POST, PUT and DELETE requests whenever a cross-site request -- indicated by a present Origin header -- comes in.

The Access-Control-Allow-Origin header is set to the URL from the global CORS plugin instance that matches the given Origin header. If at least one such URL is given in the CORS plugin, the request will be denied (status 403) if none of the URLs matches the Origin.

handlePreflightRequests

handlePreflightRequests() tells the controller to respond to all OPTIONS (=preflight) requests by setting the appropriate Access-Control-Allow headers according to the global default settings defined in the CORS app instance.
This is necessary to allow POST, PUT and DELETE requests in a Cross Origin situation.

beforeInterceptor

If a method named beforeInterceptor is present, the request will only be fulfilled if this method returns true.
This method is usually a good place to implement you Authentication logic.

Consuming a REST API

REST APIs that make use of the above mentioned features require a special request header to be sent with every non-GET request:

  • X-CSRF-Safe or

  • X-CSRF-Token if protectFromForgery has been used.

This is true no matter what kind of client is used to perform the request.

If the client is a standard web browser, the presence of either of theses two headers proves that the request has not been triggered by posting a simple POST form, but rather is an XHR request, which ensures that CORS rules apply and Cross Site Request Forgery can be prohibited.

Widget controllers

When implementing a ConfigWizard2 based Widget -- i.e. a controller that borrows logic from ConfigWizard2 by calling something like

de.pinuts.cmsbs.lib.configwizard2.mixinWidgetController(...)

you might not want to use the aforementioned applyCorsPolicy() method.

In this case your ConfigWizard might bring along its own list of allowed URLs, so you might want to use them rather than the global default.

Your config wizard must contain the following config fields:

"tabs": [{ "name": "settings", "fields": [{ "type": "Fieldset", "name": "settings", "fields": [{ "name": "checkPublisherUrls", "type": "FieldGroup", "fields": [{ "type": "TextField", "name": "publisherUrls" }] }, ...

The action method delivering your Widget's client-side JavaScript code should be secured like this:

Each action method must be secured like this: