Magnolia 5.7 reached extended end of life on May 31, 2022. Support for this branch is limited, see End-of-life policy. Please note that to cover the extra maintenance effort, this EEoL period is a paid extension in the life of the branch. Customers who opt for the extended maintenance will need a new license key to run future versions of Magnolia 5.7. If you have any questions or to subscribe to the extended maintenance, please get in touch with your local contact at Magnolia.
This page provides an overview of the different ways of retrieving JSON for JCR-based content in Magnolia as well as listing their respective advantages and disadvantages.
Magnolia REST delivery API
The delivery
API is a Magnolia REST resource introduced with version 2.0 of the Magnolia REST modules. We recommend you use delivery endpoint API v2 to benefit from all the latest features.
This REST resource is highly customizable. You can define multiple endpoint configurations to serve different needs. Each configured endpoint is accessed through a distinct
endpointPath
and provides two sub-resources. The sub-resources can be called with HTTP GET both to read a node (and its subnodes) by a given path and to query nodes.
Example: Find a tour with the word "Japan" in its description. Return only the variant in the German language:
curl -g -G 'http://localhost:8080/magnoliaAuthor/.rest/tours' --data-urlencode "description[like]=%Japan%" \ -H "Accept-Language: de-DE" -u superuser:superuser
Pros
- Convenient and simple to use JSON structure.
- Supports RESTful URLs.
- Highly configurable via YAML file in a light module or in the JCR
configuration
workspace. - Supports multiple endpoint configurations to serve different needs.
- Provides a fine-grained security.
- Supports localization.
- Can resolve referenced nodes from other workspaces,including image renditions, within one request.
Cons
- Read-only.
- Cannot fetch personalized variants of pages or components.
Magnolia REST nodes and properties endpoints
Magnolia endpoints for nodes
and properties
- available since version 1.0 of the Magnolia REST modules.
Example: Read JCR content of the /magnolia-travels/Kyoto
tour from the Tours content app in the demo.
curl -H "Accept: application/json" http://localhost:8080/magnoliaAuthor/.rest/nodes/v1/tours/magnolia-travels/Kyoto \ -u superuser:superuser
For more information and examples, see their API pages:
Pros
- Complete API to create, read, update and delete (CRUD) JCR data.
- Supports RESTful URLs.
- Provides a fine-grained security.
Cons
- Can only fetch content from one workspace per single request.
- Rigid and verbose JSON structure.
- Poor localization support.
- Cannot fetch personalized variants of pages or components (see personalization).
Custom Java endpoints
Create a custom REST endpoint depending on your needs.
The following example snippet is taken from the
CameraCollectionEndpoint
.
@Path("/allCameras") @GET @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Get all cameras.", notes = "Returns json for the list of all a camera objects.") @ApiResponses(value = { @ApiResponse(code = 200, message = STATUS_MESSAGE_OK, response = List.class), @ApiResponse(code = 401, message = STATUS_MESSAGE_UNAUTHORIZED), @ApiResponse(code = 404, message = STATUS_MESSAGE_NODE_NOT_FOUND), @ApiResponse(code = 500, message = STATUS_MESSAGE_ERROR_OCCURRED) }) public List<Camera> getAllCameras() { List<Camera> result = null; try { result = cameraCollectionPojoService.getAllCameras(); } catch (RepositoryException e) { log.warn("Could not compute the list of all cameras."); } return result; }
To explore the complete example, clone the camera-collection
module from git and read Camera Collection module on Magnolia Community Wiki.
Also read How to create a custom Java-based REST endpoint.
Pros
- Highly configurable.
- Provides a fine-grained security.
- When using
com.wordnik.swagger.annotations.*
, endpoints are automatically exposed in the Swagger tool for testing (see Swagger API explorer). - Delivery of personalized variants of pages or components is possible (see personalization).
Cons
- Requires Java skills.
- Must be deployed in a Maven module.
FreeMarker template
You can implement a custom JSON provider in a FreeMarker template script. Use templating functions such as
cmsfn
,
damfn
and others to read data from JCR and render JSON in your preferred manner.
Approaches: A pure JSON provider or an HTML-mixed one
Pure JSON
You can write a FreeMarker template in a way that its response is pure JSON. This is what we consider as a template script based or FreeMarker based JSON provider. To go for this approach, do the following:
- Create a page template. A dialog is not required.
- Write FreeMarker code that renders JSON.
- Create a page with this new template.
- Request the new page to get JSON in the response.
Step 2 can go from very simple to very complex.
Mixed with HTML
You also can write a template that renders HTML and assigns JSON to JavaScript variables:
Assembling JSON manually
Here is a simple example of how you can assemble JSON structure in FreeMarker:
[#assign pageNode = cmsfn.contentByPath("/magnolia-travels/Kyoto", "tours")] { "path" : "${pageNode.@path}", "name" : "${pageNode.name}", "description" : "${pageNode.description}" }
The sample project
camera-collection
provides a more sophisticated FreeMarker-based JSON provider, see simple-json-provider.ftl and json-provider-utils.ftl on git.
Assembling JSON with jsonfn
jsonfn
templating functions generate JSON from JCR nodes in any workspace. jsonfn
helps you assemble JSON within a FreeMaker template script and provides powerful methods to generate JSON in a flexible way. It can resolve references to nodes of other workspaces and it also provides links to renditions of assets in one request. See resolving referenced nodes and renditions.
To achieve the same JSON as in Assembling JSON manually above, write the following code:
[#assign pageNode = cmsfn.contentByPath("/magnolia-travels/Kyoto", "tours")] ${jsonfn.from(pageNode).add("@path", "name", "description").print()}
jsonfn
templating functions are not bundled with preconfigured Magnolia bundles or webapps.
Setting the proper content type of the response
If your FreeMarker template acts as a pure JSON provider, you may want to set the content type of the response to application/json
. You cannot set the content type directly in the template definition. Instead you have to define a new renderer and reference this renderer in the template definition with the renderType
property.
However, having the "proper" content type may not be required. In many use cases it is sufficient to use the FreeMarker renderType
that responds with text/html
.
Renderer configuration
!inherit:freemarker contentType: application/json
The above example requires the Magnolia specific !inherit
directive available since Magnolia 5.5.6
Node name | Value |
---|---|
modules | |
rendering | |
renderers | |
json | |
contentType | application/json |
extends | ../freemarker |
type | json |
Note that you can extend the default freemarker
renderer.
Template definition
title: jsonprovider0815 templateScript: /ftl-json-provider/templates/pages/jsonprovider0815.ftl renderType: json #renderType: freemarker visible: true
Turning a template-based JSON provider into a RESTful API with virtualURIMapping
You can create a template that accepts request parameters to specify the JSON content to be returned. In the example below, you can specify the workspace
and the path
of the node.
[#assign path = ctx.path!"undefined" /] [#assign workspace = ctx.workspace!"tours" /] [#assign response = '{ "error" : "Missing path parameter." }' /] [#if path!="undefined"] [#assign contentNode = cmsfn.contentByPath(path, workspace)] [#assign response = jsonfn.from(contentNode).add("@path", "name", "description").wrapForI18n().print() /] [/#if] ${response}
/getjson
. You can call it with the following URL: http://localhost:8080/magnoliaAuthor/getjson?
workspace=tours
&
path=/magnolia-travels/Kyoto
http://localhost:8080/magnoliaAuthor/getjson/tours /magnolia-travels/Kyoto
Node name | Value |
---|---|
modules | |
<your-module> | |
virtualURIMapping | |
getjson-fromWorkspace-byPath | |
class | info.magnolia.cms.beans.config.RegexpVirtualURIMapping |
fromURI | /getjson/([0-9A-Z-a-z]+)([0-9A-Z-a-z-\/]+) |
toURI | forward:/getjson?workspace=$1&path=$2 |
Pros
- Highly configurable.
- Fully customizable when assembling JSON manually.
- Decent customizable when using jsonfn.
- No Java required.
- Localization support.
- Delivery of personalized variants of pages or components is possible (see personalization).
Cons
- Security cannot be set per workspace and path, but only generally for the page that acts as the JSON provider.
- Methods beyond
GET
(such asPUT
,POST
orDELETE
) cannot be implemented using just FreeMarker.
Getting localized JSON
If your content is localized, you can also get localized JSON output. See enabling multi-language content about how to create localized content.
Depending on the technique applied to get JSON, you may have to force getting the localized version or you will get a JSON version of the default locale.
With the delivery API
With the delivery endpoint API v2, you can configure multiple endpoints with full content localization support.
To request a specific single language variant, do one of the following: If both are set, the lang parameter takes priority. If neither is set, the decision is delegated to the I18nContentSupport interface. To return all language variants: You can use both the Accept-Language: de-DE
lang
query parameter. For example: lang=de-DE
lang
query parameter to all: lang=all
Accept-Language
header and the lang
query parameter for both sub-resources Read node and Query nodes.
See Requesting localized content with the delivery endpoint.
With default endpoints for nodes and properties
Localized properties must be read as <property-name>_<LOCALE>
. For example, description_de
for the property named description
. The default endpoints do not take the client's locale into account. When JSON is generated, the nodes are not wrapped with
With custom JAVA endpoints
In a custom endpoint, you typically fetch
cmsfn
):public Node wrapForI18n(Node content) { return content != null?new I18nNodeWrapper(content):null; }
I18nNodeWrapper
causes the values to be returned in their localized form.In a FreeMarker template
In a FreeMarker template, it is the wrapForI18n
method that makes sure the values are returned in their localized form.
With manually assembled JSON
Use cmsfn.wrapForI18n
to get a localized version of a content node:
[#assign pageNode_notLocalized = cmsfn.contentByPath("/travel/about", "website")]
[#assign pageNode_localized = cmsfn.wrapForI18n(pageNode_notLocalized)]
With jsonfn
When using jsonfn
, add the wrapForI18n
method before calling the print
method.
[#assign pageNode = cmsfn.contentByPath('/magnolia-travels/Kyoto', 'tours') /]
${jsonfn.from(pageNode).add("title").wrapForI18n().print()}
The same-origin policy problem
According to the same-origin policy: (...) a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. Therefore, if your application resides within a domain that is different from the domain that hosts your data, the web browser will actively prevent accessing content stored under the host domain. An elegant solution to solve the same-origin policy without changing Apache configuration is the Magnolia Add HTTP Headers filter. This fiter is available out-of-the-box; you only need to configure it.