Document Properties
Kbid
2324R8
Last Modified
31-May-2023
Added to KB
18-Jun-2012
Public Access
Everyone
Status
Online
Doc Type
Concepts
Product
  • ICM 7.10
  • ICM 11
Concept - REST Framework

Introduction

The present concept is addressed to developers who want to make use of Intershop's REST framework.
The Intershop platform offers simple-to-use lightweight WebService APIs for the integration into external systems. The REST framework serves as the foundation for those APIs.

RESTful web services are services that are built to work best on the web. Representational State Transfer (REST) is an architectural style that specifies constraints, such as the uniform interface, that if applied to a web service induce desirable properties, such as performance, scalability, and modifiability, that enable services to work best on the web. In the REST architectural style, data and functionality are considered resources, and these resources are accessed using Uniform Resource Identifiers (URIs), typically links on the web. The resources are acted upon by using a set of simple, well-defined operations.

( http://java.sun.com/javaee/6/docs/tutorial/doc/gijqy.html )

The central concept of a REST API is a resource. A resource represents a business object like catalog, product etc. and is mapped to a URI. By invoking HTTP methods like GET and POST on the URI, it is possible to perform certain operations on that resource. The HTTP methods are mapped to the typical CRUD operations (a 1:1 mapping is not possible).

A REST-based web service offers advantages over traditional WSDL/SOAP-based web services:

  • Lightweight & simple (based on HTTP and plain data)
  • Allows flexible output representation for support of XML, JSON, ...
  • Easy to understand and implement (server and clients)

The REST framework acts as an additional access layer on top of the existing application layer. It is made available by a single cartridge rest. The rest cartridge has only absolutely essential dependencies (cartridges tools, core, app, and component). Specific APIs (back office, storefront, integration services, customer-specific etc.) can be defined on top of the REST layer.

Glossary

Term

Explanation

CRUD

Create, Read, Update, Delete are the four basic functions of persistent storage.

Resource

A server-side object that handles the request and is identified by the URI

ResourceObject (RO)

A serialized/deserialized data object used to transfer structured data between client and server

References

Cookbooks about this topic:

Rest API Design Guidelines

The RESTful WebService should be as close as possible to the definition of REST. This is achieved by following these principles:

  • All accessible objects (resources, collection resources etc.) are accessed via URIs using standard HTTP methods ( GET, POST, PUT, DELETE, OPTIONS).
  • The return type is JSON. A different return type can be requested using the HTTP- Accept header (e.g. text/xml).
  • Resource hierarchies as well as the returned JSON/XML structures should be as self-explanatory as possible. This implies that:
    • URIs should be readable and understandable by humans and
    • That the semantics of the returned structure are clear.
  • As far as possible, the server should provide information / guidance to the client on how to send valid requests that achieve the intended results. Therefore, wherever possible use semantically fitting (pseudo-)standards (return codes as HTTP status codes, link relations as used by Atom etc.).
  • Every URI segment represents a resource. Method calls/identifiers as well as processing instructions (sorting, locale parameters, ...) are illegal in URIs except for query or matrix parameters.

Communication

URI

An Intershop REST URI is constructed according to the following pattern:

http://<web-server>:<port>/INTERSHOP/rest/WFS/<SiteID>/<AppUrlID>;loc=en_US;cur=USD/<Top-level Resources>

The URI uses the new .../rest/... as part of the URL. Thereby, the Web Adapter recognizes REST requests and handles them in a specific way (e.g., allowing PUT and DELETE methods).

Each part of a REST URI should be either a resource, a resource collection, or (in case of GET requests) a parameter (query or matrix). Collection resources are named with plural nouns, e.g., sites, categories, or products. Resources are named using a locally unique ID, e.g., /sites/<siteID>/categories/<categoryID>. This also means that elements of a collection resource are accessed by appending their key/name to the collection resource URI, i.e. a specific category is accessed via /categories/<categoryID>, not /category/<categoryID>. /category does not represent a resource itself.

Parameters

The parameters in a URI can be query parameters or matrix parameters. Matrix parameters have several advantages compared with query parameters:

  • Matrix parameters are treated as belonging to the URI by proxies (many proxies prevent caching of URIs with query parameters).
  • Matrix parameters can be used at any point in the URI (make it possible to set the parameters where they are needed instead of appending all of them at the end).
  • Matrix parameters can occur multiple times (even makes it possible to use different values for the same parameter for different resources in the same URI).
  • Matrix parameters are not stripped when the client takes the baseURI (e.g., the one the client sends a request to) and appends relative URI-segments.

As all resources are requested via the root resource and subsequent sub-resource lookup using the component framework, the RootResource provided by the REST framework handles the matrix parameters loc and cur and puts the corresponding values into the AppContext / Request dictionary as current locale and current currency. Sub-resources can simply use these values. It is possible to override currency or locale at sub-resource level by simply adding the corresponding matrix parameters at that point in the URI. The abstract base class for all sub-resources parses these parameters and overwrites the corresponding values set by the root resource. Using this approach, each resource can be used to define currency and locale for all its sub-resources. Whether or not specific APIs support the override mechanism is subject to API design.

Content Negotiation

The response format is chosen by the server in response to the client's HTTP Accept header (cf. RFC 2616, Sec.14 ).
Basically, this means that a client sending Accept: text/xml gets the response in XML format and a client sending Accept: application/json gets the response in JSON. If sending multiple values, the server selects its preferred of the supported formats.
If the server cannot provide the needed format, it returns the HTTP status code 406.

HTTP Methods

HTTP methods represent the actions that are to be applied to a resource. The following table gives a short overview of the most important actions. For more details, see RFC 2616, Sec.9 .

Method

Purpose

CRUD equivalent

GET

Retrieves whatever information (in the form of an entity) is identified by the Request-URI. If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in the response

Read

POST

Requests that the origin server accepts the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line

Create

PUT

Requests that the enclosed entity is stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity should be considered as a modified version of the one residing on the origin server.

Update

DELETE

Requests that the origin server deletes the resource identified by the Request-URI

Delete

OPTIONS

Requests information about the communication options available on the request/response chain identified by the Request-URI


Note

The methods POST and PUT are often confused since POST has been used for years to achieve the results of PUT on the web.


The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI of a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request – the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.

( RFC 2616, Sec.9.6 )


Note

The OPTIONS request is automatically handled by the framework. It returns all HTTP methods for which a resource method is registered at this URI with the correct content types (HTTP Accept and HTTP Content-Type).

GET and HEAD are safe methods: They do not change anything on the server side. They can be called multiple times for the same URI and do not have any effects than returning (maybe) different values.

GET, HEAD, PUT, DELETE, OPTIONS, and TRACE are idempotent methods: They may have server side effects, but can still be called multiple times for the same resource without negative effects. For example, the same change to the same resource can be done multiple times; the state after that will be the same as after the first change (e.g., x=1 is the same as x=1;x=1;...;x=1). An HTTP PUT in Java corresponds to something like map.put(key, value) with the URI (or parts of it) being the key and the transferred data being the value.

In contrast, POST is a non-idempotent operation: In Java, it corresponds to something like list.add(value) with the transferred data being the value. It can have undesired effects when doing that multiple times.

HTTP-Status Codes

When an error occurs, an appropriate HTTP status code (see W3C RFC2616 ) is returned. This can be achieved by manually calling addResponseData at the current resource or by simply throwing a RestException with the appropriate status code.

For all operations, the most relevant status codes are:

HTTP status code

Meaning

Description

Automatically generated?

200

OK

The request has succeeded.

Yes

201

Created

The request has been fulfilled and resulted in a new resource being created. The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URI for the resource given by a location header field. The response should include an entity containing a list of resource characteristics and location(s) from which the user or user agent can choose the one most appropriate.

No

204

No content

The request has succeeded, but has no response data.

No

303

See other

The response to the request can be found under a different URI and should be retrieved using a GET method on that resource. This method exists primarily to allow the output of a POST-activated script to redirect the user agent to a selected resource. The new URI is not a substitute reference for the originally requested resource. The 303 response must not be cached, but the response to the second (redirected) request may be cacheable. The different URI should be given by the location field in the response. Unless the request method was HEAD, the entity of the response should contain a short hypertext note with a hyperlink to the new URI(s).

No

400

Bad Request

The request could not be understood by the server due to malformed syntax. The client should not repeat the request without modifications.

No

401

Unauthorized

The request requires user authentication. The response must include a WWW-Authenticate header field ( section 14.47 ) containing a challenge applicable to the requested resource. The client may repeat the request with a suitable Authorization header field ( section 14.8 ). If the request already included authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.

No

403

Forbidden

The server understood the request, but is refusing to fulfill it. Authorization will not help and the request should not be repeated.

No

404

Not found

The server has not found anything matching the Request-URI. No indication is given of whether the condition is temporary or permanent. This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable.

No

405

Method not allowed

The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. The response must include an Allow header containing a list of valid methods for the requested resource.

Yes, the Web Adapter generates this status and the corresponding "allow" header. At the moment, only GET, HEAD, and POST are supported.

406

Not acceptable

The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. 
Unless it was a HEAD request, the response should include an entity containing a list of available entity characteristics and location(s) from which the user or user agent can choose the one most appropriate. The entity format is specified by the media type given in the Content-Type header field.

Yes

409

Conflict

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user may be able to resolve the conflict and resubmit the request. The response body should include enough information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, this may not be possible and is not required. Conflicts are most likely to occur in response to a PUT request.

No

500

Internal Server Error

The server encountered an unexpected condition which prevented it from fulfilling the request.

Yes

In addition, operations may have specific status codes.

Implementation

The REST framework provides interfaces and base classes that provide a way to look up a REST API and its resources via the component framework. Any specific API needs to declare the resources and their relationships (parent-child) for the component framework (see the following chapter Concept - REST Framework#Configuration). Thus, the API-specific resources can be reduced to handle their specific requests.

Configuration

Any REST API needs to be accessed by a URI of the form http://<http-server>:<port>/rest/<siteID>/<appID>/<resourceNames>.

The AppID is defined in the apps.component file of the API's cartridge:

apps.component (of Back Office PoC)
<?xml version="1.0" encoding="UTF-8"?>

<components xmlns="http://www.intershop.de/component/2010">
    <instance name="intershop.RestPoC.Cartridges" with="CartridgeListProvider">
        <fulfill requirement="selectedCartridge" value="rest"/>
        <!-- include the cartridges of another cartridge list provider  -->
        <fulfill requirement="parent" with="intershop.B2CWebShop.Cartridges"/>
    </instance>

    <fulfill requirement="app" of="AppEngine">
        <instance with="ApplicationType">
            <fulfill requirement="id" value="backoffice"/>
            <fulfill requirement="namedObject" with="backoffice-rest-api"/>
            <fulfill requirement="cartridgeListProvider" with="intershop.RestPoC.Cartridges"/>
        </instance>
    </fulfill>
</components>

The resources and their hierarchy (the part of the API independent from the used HTTP methods) is declared in the instances.component file:

instances.component (of Back Office PoC)
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://www.intershop.de/component/2010">

    <instance name="backoffice-rest-api" with="NamedObject">
        <fulfill requirement="name" value="rest-api" />
        <fulfill requirement="object">
            <instance with="RootResource">
                <fulfill requirement="name" value="rest-api" />
                <fulfill requirement="authenticationProvider">
                    <instance with="TokenAuthenticationProvider">
                        <fulfill requirement="loginProvider">
                            <instance with="SimpleLoginProvider" />
                        </fulfill>
                        <fulfill requirement="encryptionPassword" value="topsecret" />
                        <!--optional: fulfill requirement="encryptionAlgorithm" value="..." /-->
                    </instance>
                </fulfill>
                <fulfill requirement="prefixPipelineName" value="PrefixREST" />
                <fulfill requirement="prefixPipelineStartNode" value="Start" />
                <fulfill requirement="defaultCacheTTL" value="86400" />
                <fulfill requirement="subResource">
                    <instance with="CategoryResource">
                        <fulfill requirement="name" value="categories" />
                        <fulfill requirement="handler">
                            <instance with="CategoryHandlerImpl" />
                        </fulfill>
                        <fulfill requirement="subResource">
                            <instance with="ProductResource">
                                <fulfill requirement="name" value="products" />
                                <fulfill requirement="handler">
                                    <instance with="ProductHandlerImpl" />
                                </fulfill>
                            </instance>
                        </fulfill>
                    </instance>
                    <instance with="JobsResource">
                        <fulfill requirement="name" value="jobs" />
                        <fulfill requirement="itemResource">
                            <instance with="JobItemResource" />
                        </fulfill>
                    </instance>
                    <instance with="LocalizedTextResource">
                        <fulfill requirement="name" value="localizedTexts" />
                    </instance>
		            <instance with="RegionalSettingResource">
        		        <fulfill requirement="name" value="regionalSettings" />
		            </instance>
	                <instance with="UserResource">
                        <fulfill requirement="name" value="users" />
                    </instance>
                </fulfill>
            </instance>
        </fulfill>
    </instance>

</components>

The component framework automatically creates one instance of each of the defined resources.

It is easy to see that the above API has the following URI paths:
/categories
/categories/.../products
/jobs
/localizedTexts
/users

The structure is easy to alter/extend by customized APIs.

The resource handling /jobs is separated into a collection resource JobsResource extending AbstractRestCollectionResource and an item resource JobItemResource. The framework will automatically direct all calls to /jobs to the JobsResource, and all calls to /jobs/<jobID> to the JobItemResource, setting the jobID as the name of the item resource.

To make it even more customizable, the CategoryResource and the ProductResource have definable handlers which are called from pure API methods in the CategoryResource, e.g., to get business objects. In that way, the internal and external APIs can be separated.

The RootResource in this example is configured to call PrefixREST-Start before processing the request. The pipeline dictionary written by this call is available to all subsequent pipeline calls of this request.

The interfaces of all resource classes need to be declared in the contracts.component file:

contracts.component (of Back Office PoC)
<?xml version="1.0" encoding="UTF-8"?>

<components xmlns="http://www.intershop.de/component/2010">

    <contract name="CategoryResource" class="com.intershop.component.rest.capi.resource.CategoryResource" />
    <contract name="CategoryHandler" class="com.intershop.component.rest.capi.resource.CategoryHandler" />
    <contract name="ProductResource" class="com.intershop.component.rest.capi.resource.ProductResource" />
    <contract name="ProductHandler" class="com.intershop.component.rest.capi.resource.ProductHandler" />
    <contract name="JobsResource" class="com.intershop.component.rest.capi.resource.JobsResource" />
    <contract name="LocalizedTextResource" class="com.intershop.component.rest.capi.resource.LocalizedTextResource" />
    <contract name="UserResource" class="com.intershop.component.rest.capi.resource.UserResource" />

</components>

The implementations are declared in the implementations.component file:

implementations.component (of Back Office PoC)
<?xml version="1.0" encoding="UTF-8"?>

<components xmlns="http://www.intershop.de/component/2010">

    <implementation name="SimpleLoginProvider" implements="LoginProvider" class="com.intershop.component.rest.example.internal.auth.SimpleLoginProvider" />

    <implementation name="CategoryResource" implements="AbstractRestResource" class="com.intershop.component.rest.capi.resource.CategoryResource">
        <requires name="subResource" contract="RestResource" cardinality="0..n" />
        <requires name="name" contract="String" cardinality="1..1" />
        <requires name="handler" contract="CategoryHandler" cardinality="1..1" />
    </implementation>
    <implementation name="ProductResource" implements="AbstractRestResource" class="com.intershop.component.rest.capi.resource.ProductResource">
        <requires name="subResource" contract="RestResource" cardinality="0..n" />
        <requires name="name" contract="String" cardinality="1..1" />
        <requires name="handler" contract="ProductHandler" cardinality="1..1" />
    </implementation>

    <implementation name="CategoryHandlerImpl" implements="CategoryHandler" class="com.intershop.component.rest.internal.resource.CategoryHandlerImpl" />
    <implementation name="ProductHandlerImpl" implements="ProductHandler" class="com.intershop.component.rest.internal.resource.ProductHandlerImpl" />

    <implementation name="JobsResource" implements="AbstractRestCollectionResource" class="com.intershop.component.rest.capi.resource.JobsResource">
        <requires name="itemResource" contract="RestResource" cardinality="1..1" />
        <requires name="subResource" contract="RestResource" cardinality="0..n" />
        <requires name="name" contract="String" cardinality="1..1" />
    </implementation>
    <implementation name="JobItemResource" implements="RestResource" class="com.intershop.component.rest.capi.resource.JobItemResource">
        <requires name="subResource" contract="RestResource" cardinality="0..n" />
    </implementation>
    <implementation name="LocalizedTextResource" implements="AbstractRestResource" class="com.intershop.component.rest.capi.resource.LocalizedTextResource">
        <requires name="subResource" contract="RestResource" cardinality="0..n" />
        <requires name="name" contract="String" cardinality="1..1" />
    </implementation>
    <implementation name="UserResource" implements="AbstractRestResource" class="com.intershop.component.rest.capi.resource.UserResource">
        <requires name="subResource" contract="RestResource" cardinality="0..n" />
        <requires name="name" contract="String" cardinality="1..1" />
    </implementation>

</components>

Framework Implementation Details

General

The siteID and appID are used by the RestDispatcher to find the root resource of the specific REST API. The RestDispatcher requests a named object with the name rest-api from the app. If that is found, it is used as the root resource.

The root resource is used as the starting point for a sequential lookup of the resource names of the URI, where each resolved resource is asked to find the matching sub-resource for the next resource name. Again, the component framework is used for that lookup. If a sub-resource inherits from AbstractRestCollectionResource, it directly requests to the appropriate item resource (defined via its itemResource requirement), which handles the resolving of a single item, and which can route the request to its sub-resources.

The returned resource implementations are components that are not request-specific, yet Jersey's strength is easy modeling request-specific objects by using annotations; the returned resources are copied (including any attributes set by the component framework) and enriched with request context. Only those copies are used.

Major API-Classes Provided by the Framework

c.i.c.rest.capi.RestException

Throwing a RestException is the preferred way to notify the client about errors. The RestException itself is only a more convenient way to use the WebApplicationException. The main differences are:

  • It allows to return custom headers
  • It suppresses stack traces when the logging level does not include DEBUG

There are three constructors for this:

    /**
     * Create a new instance with a specified statusCode
     */
    public RestException(int statusCode) {...}

    /**
     * Create a new instance with the default statusCode and a custom message
     */
    public RestException(String message) {...}

    /**
     * Create a new instance with a specified statusCode, a custom message, and a set of response headers.
     */
    public RestException(int statusCode, String message, Map<String, String> headers) {...}

For many errors, the correct HTTP status code has to be known and returned, and the response has to include certain mandatory headers and/or body content. 

To enable developers to implement consistent error handling in their APIs with as little room for error as possible, the class  RestException provides builder methods to return the correct error response for certain use cases.

    /**
     * RFC2616: "The server has not found anything matching the Request-URI. "
     */
    public RestException notFound() {...}

    /**
     * RFC2616:
     * "The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user may be able to resolve the conflict and resubmit the request. The response body should include enough information for the user to recognize the source of the conflict."
     */
    public RestException conflict() {...}

    /**
     * RFC2616:
     * "The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications."
     */
    public RestException badRequest() {...}

    /**
     * This exception indicates that the request could not be fulfilled due to invalid attributes contained in the
     * request body. It will return a status code 400 (Client error/Bad request) and include the invalid attributes.
     * 
     * @param invalidAttributeNames
     *            The names of the attributes that contained invalid values.
     */
    public RestException invalidAttributes(List<String> invalidAttributeNames) {...}

    /**
     * This exception indicates that the request could not be fulfilled due to required attributes missing from the
     * request body. It will return a status code 400 (Client error/Bad request) and include the missing attribute names.
     * 
     * @param missingAttributeNames
     *            The names of the attributes that were missing values.
     * @return
     */
    public RestException missingAttributes(List<String> missingAttributeNames) {...}

    /**
     * Add a localization key to the exception which can be used by the client to look-up localized error messages.
     * 
     * @param key
     * @return
     */
    public RestException localizationKey(String key) {...}

 c.i.c.rest.capi.response.RestResponseBuilder

The RestResponseBuilder provides methods to ensure consistent, valid responses for certain server conditions. In this case, the conditions do not represent errors, and the response may still be modified before it is sent to the client. Classes derived from  AbstractRestResource can get the response builder by calling  getResponseBuilder().

It is especially useful to avoid repetitive, error-prone code when sending e.g. a "Resource created" response (status code has to be  201 (Created), header  Location has to be set, response body should contain a link representation pointing to the created response).

    /**
     * RFC 2616:
     * "The request has been fulfilled and resulted in a new resource being created. The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URI for the resource given by a location header field."
     * 
     * @param uri
     *            The location of the created resource.
     */
    public static Response created(String uri);

    /**
     * RFC 2616:
     * "The request has been fulfilled and resulted in a new resource being created. The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URI for the resource given by a location header field."
     * 
     * @param linkRO
     *            The LinkRO containing the location of the created resource and additional optional information.
     */
    public static Response created(LinkRO linkRO);

    /**
     * RFC 2616:
     * "The response to the request can be found under a different URI and should be retrieved using a GET method on that resource."
     * 
     * @param uri
     *            The location of the created resource.
     */
    public static Response seeOther(String uri);

    /**
     * RFC 2616:
     * "The response to the request can be found under a different URI and should be retrieved using a GET method on that resource."
     * 
     * @param linkRO
     *            The LinkRO containing the location of the created resource and additional optional information.
     */
    public static Response seeOther(LinkRO linkRO);

    /**
     * RFC 2616: "The request has succeeded, but has no response data."
     */
    public static Response noContent();

Usage:

@POST
public Response createBasket()
{
   ...
   return getResponseBuilder().created(new LinkRO(basketRO.getBasketID(), basketURI)).build();
}


c.i.c.rest.capi.pipeline.PipelineCall

A PipelineCall is a convenient way to access pipeline logic from REST resources.
The most important methods are:

public PipelineCall(String pipelineName, String pipelineStartnodeName, Class<?> resultType, String resultName) {...}

    /**
     * Invokes the specified pipeline using CurrentDomain and CurrentRequest
     *
     * @return
     */
    public Object invoke() {...}

    /**
     * Set a parameter. If value is null and a parameter with this name exists,
     * it will be removed.
     */
    public void setParameter(String name, Object value) {...}

    /**
     * Set whether an exception will be thrown, if the result cannot be found under the given resultName in the PipelineDictionary after an invoke().
     */
    public void setResultMandatory(boolean resultMandatory) {...}

It is usually used like this:

PipelineCall pipelineCall = new PipelineCall("RESTProcessCart", "GetCart", B2CBasketBO.class, "CurrentCartBO");

    pipelineCall.setParameter("BasketID", basketID);
    pipelineCall.setParameter("RequireCurrentCartBO", 1);

    b2cBasketBO = (B2CBasketBO)pipelineCall.invoke();

c.i.c.rest.capi.resource.AbstractRestResource

The AbstractRestResource is the base class for all resources in an Intershop REST API. It automatically handles the lookup and instantiation of sub-resources, and provides some methods and properties relevant to sub-resources.

The most important methods and properties are:

/**
     * The current site.
     */
    protected Domain currentSite;

    protected PermissionProvider permissionProvider;

    private RestResource parentResource;

    /**
     * Get the current permission context. If no PermissionContext is available, constructs a new one from the known information.
     */
    protected PermissionContext getPermissionContext() {...}

    /**
     * @return the combined matrix parameters regardless of where in the
     *         resource hierarchy they are set. Never returns null.
     */
    private MultivaluedMap<String, String> getCombinedMatrixParams() {...}

    /**
     * Get the value of a matrix parameter.
     *
     * @param name
     * @param localOnly
     *            If true, only Jersey-compliant, resource-specific matrix
     *            parameters are accepted. If false, all matrix parameters from
     *            the URI are searched.
     * @return The value of the matrix parameter occurring right-most in the
     *         URI.
     */
    public String getMatrixParamValue(String name, boolean localOnly) {...}


    /**
     * Get the name of the resource (i.e. the  URI-segment representing this resource)
     */
    public String getName() {...}

    /**
     * Get the request-specific instance of this resource. Is often overridden to transfer more data from the component definition to the instance.
     */
    public RestResource getRequestSpecificCopy(ResourceContext rc) {...}

    /**
     * Get the parent resource.
     */
    public RestResource getParent() {...}

    /**
     * Store data which will be added to the final response before returning it
     * to the client.
     * This method can be used if a resource method doesn't return a new Jersey
     * Response-object but instead a local type.
     *
     * @param status
     *            The response status to set
     * @param headerName
     *            A header to be set.
     * @param headerValue
     *            The header's value.
     */
    public void addResponseData(int status, String headerName, String headerValue) {...}

    /**
     * Store data which will be added to the final response before returning it
     * to the client.
     * This method can be used if a resource method doesn't return a new Jersey
     * Response-object but instead a local type.
     *
     * @param status
     *            The response status to set
     * @param headers
     *            A map of headers and header values to be set.
     */
    public void addResponseData(int status, Map<String, String> headers) {...}

    /**
     * Store a single header which will be added to the final response before returning it
     * to the client.
     * This method can be used if a resource method doesn't return a new Jersey
     * Response-object but instead a local type.

     * @param headers
     */
    private void addResponseHeader(String name, String value) {...}

    /**
     * Set the time until which the current response may be cached by the
     * WebAdapter.
     *
     * @param remainingSeconds
     */
    public void setCacheExpires(long remainingSeconds)

c.i.c.rest.capi.resource.AbstractRestCollectionResource

The AbstractRestCollectionResource is the base class for all collection resources in an Intershop REST API. It is derived from the AbstractRestResource and distinguishes between items and sub-resources (named sub-resources take precedence).

A typical REST API will distinguish between actions on resource collections ( GET list, POST item) and actions on single resources ( GET item, PUT modified item, DELETE item). The AbstractRestCollectionResource helps to keep the declaration and code for these cases separate:

instances.component
...
      <fulfill requirement="subResource">
          ...
          <instance with="OrganizationsResource">
              <fulfill requirement="name" value="organizations" />
              <fulfill requirement="itemResource">
                  <instance with="OrganizationItemResource">
                      <fulfill requirement="subResource">
                          <instance with="UserResource">
		              <fulfill requirement="name" value="users" />
		          </instance>
		      </fulfill>
                  </instance>
              </fulfill>
          </instance>
          ...
      </fulfill>
      ...

c.i.c.rest.capi.resourceobject.AbstractResourceObject

The AbstractResourceObject is the base class for all resource objects, i.e. for all data objects that are to be serialized and deserialized.
It is Jackson-annotated to expose only categoryID public fields and methods. All other fields and methods which should be exposed have to be annotated with @JsonProperty. This behavior will be inherited by all sub-classes.
As a result, a typical RO can be a plain old Java object (POJO) extending AbstractResourceObject.

c.i.c.rest.capi.resourceobject.ExtensibleResourceObject

The ExtensibleResourceObject can be used as base class for all resource objects that can have custom, dynamic attributes.
As noted above, using custom attributes when POSTing or PUTting data can be quite difficult (especially when an attribute value is an RO itself), so in most cases, developers should opt for using POJOs derived from AbstractResourceObject instead.

c.i.c.rest.capi.resourceobject.LinkRO

As REST APIs typically need to return links to resources (e.g., in an item listing), the framework provides a simple class for this purpose.
The LinkRO has the following attributes:

  • name (inherited from AbstractResourceObject
  • uri
  • relation: an optional attribute describing the relation of the linked object to the current resource
  • title: as the links have to be consumed and possibly displayed on the client side, a title can be set to show to the user
  • description: as the links have to be consumed and possibly displayed on the client side, a description can be set to show to the user

c.i.c.rest.capi.resourceobject.ResourceCollectionRO

The ResourceCollectionRO is a typed ordered list of ROs. In addition, it has properties for paging and sorting information (although such functionality is not provided by the class itself).

Producing Collections

Typical use of a ResourceCollectionRO - Generating a list
@GET
    @Produces({ MediaType.APPLICATION_JSON })
    public ResourceCollectionRO<BasketLineItemRO> getBasketContent(@QueryParam(BasketConst.PRODUCT_ATTRS) String productAttrs) {...}
Typical use of a ResourceCollectionRO - List output
{
  "elements" : [ {
    "name" : "basketLineItem",
    "type" : "BasketLineItem",
    "id" : "sIIKAB155Z8AAAEw_nQISGna",
    "displayName" : "1team Automator 4P - DVD duplicator",
    "total" : {
      "value" : 1010.04,
      "available" : true,
      "currencyMnemonic" : "USD"
    },
    "quantity" : 3,
    "price" : {
      "value" : 336.68,
      "available" : true,
      "currencyMnemonic" : "USD"
    },
    "promotions" : [ ],
    "sku" : "2745176",
    "warranty" : {
      "name" : "dependentLineItem",
      "type" : "DependentLineItem",
      "displayName" : "AppleCare Protection Plan - extended service agreement - 2 years",
      "price" : {
        "value" : 176.97,
        "available" : true,
        "currencyMnemonic" : "USD"
      },
      "sku" : "2968847",
      "shortDesc" : "AppleCare Protection Plan - Extended service agreement - parts and labor - 2 years ( 2nd/3rd year )",
      "longDesc" : "The AppleCare Protection Plan is a uniquely integrated service and support solution that extends the complimentary coverage on your Mac to three years from the computer's purchase date. This comprehensive plan includes expert telephone assistance, onsite repairs for desktop computers, global repair coverage for portable computers and Mac mini, web-based support resources, and powerful diagnostic tools - all for one economical price."
    },
    "shortDesc" : "1team Automator 4P - DVD duplicator - external",
    "thumbnail" : "PrimeTech-PrimeTechSpecials-Site/-/PrimeTech/en_US/PrimeTech/thumbnails/T311716.jpg",
    "longDesc" : "CD Team is specializing in the supply of CD and DVD discs, duplication equipment and services. With discs, its services range from the straight forward supply of discs right through to complex duplication, bespoke packaging, fulfillment and delivery work. PRODUCT FEATURES: Remote network software for both Mac & PC; Standalone system; Simple to use; Internal hard disk to store multiple images; Fitted with DVD-R/DVD+R and CD-R combo recorders; Batch mode - load multiple masters and walk away; Full color thermal printer; Display shows status of recording, type of master etc; Compare master option mode allows you to verify your copied CD or DVD for complete data integrity.",
    "attributes" : {
    }
  } ],
  "type" : "ResourceCollection",
  "name" : "basketLineItems"
}

Consuming Collections

Typical use of a ResourceCollectionRO - List as input
{
    "elements" : [
     {
         "sku" :  "2745176",
         "quantity" : 3,
         "warranty" :
          {
              "sku" :  "2968847"
          }
     } ]
}
Typical use of a ResourceCollectionRO - Consuming a list
@POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public AbstractResourceObject addItemsToBasket(ResourceCollectionRO<BasketLineItemRO> productLines)

Note

When consuming collections, it is strongly recommended to use always a typed ResourceCollectionRO as input parameter for the methods (use ResourceCollectionRO<LinkRO> instead of just ResourceCollectionRO).

Others

The class c.i.c.rest.capi.paging.PagingUtils provides a method getRangeFromPageable(PageableIterator<?> pageable, Integer offset, Integer amount) which can be used to retrieve a sub-range from a pageable iterator (useful for dynamic lists based on offsets and amounts instead of pages).

Testing

The REST web service can be tested using Intershop's automated test framework. The relevant part of the framework is based on HTMLUnit.
First test cases have been implemented for the B2C WebShop REST API. These tests can be found in the package tests.features.com.intershop.maintenance.rest.
Tests extending RestTestCase look like:

public void testGetPromotionList()
    {
        headers.put("Accept", "application/json");

        result = restTester.doGET("/-/promotions", headers);

        assertEquals("wrong response code returned", 200, result.getStatusCode());
        assertEquals("wrong contentType returned", "application/json", result.getContentType());
        assertNotNull("no JSON object returned", result.getJSONObject());

        if(result.isArray())
        {
            JsonNode arrayNode = result.getRootNode();

            // check all promotions
            for(int i=0; i<arrayNode.size(); i++)
            {
                // get promotion element
                JsonNode node = arrayNode.get(i);

                assertNotNull("no name value returned", result.getAttributeValueByNode(node,"name"));
                assertNotNull("no uri value returned", result.getAttributeValueByNode(node,"uri"));
            }
        }
    }

Page Caching

The framework and APIs based on it can use the standard page caching of the Web Adapter. This includes the support for selective pagecache deletion via keywords or full-text search.
The caching differentiates between responses with different content types, i.e. a request to a resource using Accept: text/xml does not return a cached JSON-response and vice versa.

Requests Using GET

The default cache time-to-live (TTL) can be set in the component configuration of the RootResource (see example instances.component above):

<fulfill requirement="defaultCacheTTL" value="86400" />

The TTL set here is used for all responses unless specified otherwise in specific (API-)resources.

A resource can define its own cache TTL simply by calling the method (inherited from AbstractRestResource):

setCacheExpires(1800);

(The example would set the maximum time for caching this response to 1800s, i.e. 30 min).

To prevent a response from being cached at all, setCacheExpires(RestFrameworkConstants.CACHE_EXPIRY_NOCACHE) has to be called.

REST resources can also specify cache keys (equivalent to the <ISCACHEKEY ...> in ISML). A resource can add cache keys to the response by calling the method (inherited from AbstractRestResource):

addCacheKey(Object key)

The key can be either a String or any object.

Requests Using Other HTTP Methods

Request using the HTTP methods POST, PUT, and DELETE will not be cached.

Security

Authentication

General

Several protocols are available for the authentication of clients in the context of RESTful web services:

  • Basic HTTP authentication
  • Digest HTTP Authentication
  • Client Certificate Authentication
  • (OAuth ( http://oauth.net/ ))

Initially, the HTTP authentication is sufficient for most authentication purposes.

The OAuth protocol allows a client/app/widget to act on behalf of a particular user (without knowledge of the user's password) and may be important in the context of widgets. This does not fit our current use cases and is therefore disregarded for the time being.

The authentication mechanism can be selected when defining the API (see Concept - REST Framework#Configuration). A first (and default) implementation will be the token-based authentication.

Authentication Token

To allow authentication to a stateless system, the client has to authenticate every request to a restricted resource. To avoid resending the password each time, a token-based approach is taken.

When a client sends user credentials, these are validated on the server using standard Intershop mechanisms (the authentication domain is resolved from the SiteID- part of the request URI; login name and password have to be supplied using the HTTP Authorization header). If they match an existing user, its ID, login time (==token creation time), and remaining validity time are concatenated. The resulting string will then be encrypted and sent to the client.

The client can simply include the authentication token with its requests. If they can be decrypted successfully and are still valid, the user identified by the decrypted user ID is assumed to have previously been authenticated.

The token is never decrypted by the client or the web adapter.

The handling of the interaction token is quite similar to the session-id of a classic request. In contrast, the token contains all important information for the interaction, so no server-side storage is required.

With the TokenAuthenticationProvider, the client has the choice to

  • Authenticate via HTTP-Basic authentication (support digest later) or form parameters whenever it encounters a 401
  • Always send user credentials
  • Authenticate once (even without a challenge) and include a header authentication-token with every request (this token will be updated with every response and the new one should be used subsequently)
    • This can be used to simulate a login: request credentials from the client once, and keep sending/receiving tokens after the first authentication. A logout in this case can be achieved by "forgetting" the token on the client side.

The TokenAuthenticationProvider does not perform a login on the server side because the specific login handling is part of the business logic for a specific API. A LoginProvider can be set using the component framework.

Each token is only valid for a given timespan, which is defined by the maximum inactivity time for the accessed site.

If an invalid token is sent, the server returns 400 (BAD REQUEST). The message is returned in the entity as well as in the header authentication-token: AuthenticationTokenInvalid (instead of the token value).
If an outdated token is sent, the request is processed as an anonymous request, but the result will include the header authentication-token with the value AuthenticationTokenOutdated (instead of the token value). If the request is for a restricted resource, the resource returns status code 401 (Authorization required).

The token-based approach as well as the form-based or HTTP-Basic login require that all communication between client and server is encrypted. Therefore, it must use TLS/https.

For any token-based authentication, a number of security implications/considerations apply. A good overview is given in the OAuth 2.0 Bearer-token specification (sect. 3.3) :

3.3. Summary of Recommendations

Safeguard bearer tokens Client implementations must ensure that bearer tokens are not leaked to unintended parties, as they will be able to use them to gain access to protected resources. This is the primary security consideration when using bearer tokens with OAuth and underlies all the more specific recommendations that follow.

Validate SSL certificate chains The client must validate the TLS certificate chain when making requests to protected resources. Failing to do so may enable DNS hijacking attacks to steal the token and gain unintended access.

Always use TLS (https) Clients must always use TLS (https) when making requests with bearer tokens. Failing to do so exposes the token to numerous attacks that could give attackers unintended access.

Do not store bearer tokens in cookies As cookies are generally sent in the clear, implementations must not store bearer tokens within them.

Issue short-lived bearer tokens Using short-lived (one hour or less) bearer tokens can reduce the impact of one of them being leaked. The User-Agent flow should only issue shortlived access tokens.

Do not pass bearer tokens in page URLs Browsers, web servers, and other software may not adequately secure URLs in the browser history, web server logs, and other data structures. If bearer
tokens are passed in page URLs (typically as query string parameters), attackers may be able to steal them from the history data, logs, or other unsecured locations. Instead, pass browser tokens in message bodies for which confidentiality measures are taken.

Tokens for Anonymous Requests

The REST framework also returns a token for anonymous requests. In that case, a token contains the ID of the current anonymous user (automatically created). This makes it possible to restrict access to certain resources (e.g., baskets) to the client that created them. The client can identify itself using the authentication token even if it does not represent a registered user.
A use case for this is the creation and modification of baskets (shopping carts): An anonymous user can create a shopping cart, but that cart should only be accessible to the same anonymous user. (In a standard web context, this is achieved by using sessions. In a REST context, there is no session.)

Tokens and Cached Responses

If the response of a REST request comes from a cache, the response will not contain a token. There is no general rule that says if a response can be cached or not, the general rule is that the token is optional.

Authorization

The authorization framework for the REST framework depends on the standard Intershop authorization approach, which is being reworked at the moment.

Until the new authorization framework is completed, an approach similar to the classic pipeline access control lists (ACLs) is supported. The pipeline ACL scheme cannot simply be used for this, since the permission check would happen too late in the request processing.

Instead, a new file  staticfiles\cartridge\components\resources-acl.properties with a syntax almost identical to that of the pipelines-acl.properties will be used:

/=Organization\:PRC_LOGIN_BACKOFFICE
organizations-POST=Channel\:NONE;Organization\:PRC_MANAGE_ORGANIZATIONS

For each resource path relative to the API root path (usually .../INTERSHOP/rest/WFS/<siteID>/<appUrlID>) and HTTP method, a set of permissions can be defined. The HTTP method is optional; a set of permissions specific to a path and a method will take precedence over a set of permissions just for the path.
As all parts of the path will be handled sequentially, a user without permission for a path aaa will automatically be unable to access any sub resource of that path aaa/bbb, too.

The permissions and permission processing (right-hand side of each declaration) are identical to the permission handling for pipelines.

The example given above means that:

  • Only users that are allowed to log in to the back office can access any resource (no matter what method is used).
  • Only users that are allowed to manage organizations are able to POST to an organization resource (regardless of where in the resource hierarchy it is located).

    Note

    If the same resource needs different permissions depending on their location, different names (URI identifiers) have to be used for the resource instances.

Exception Mapping

Exceptions (that are not handled by a resource implementation itself) are handled by exception mapper(s) as specified by https://jakarta.ee/specifications/restful-ws/3.0/jakarta-restful-ws-spec-3.0.html#exceptionmapper. The ICM's primary exception mapper is com.intershop.component.rest.internal.application.RestExceptionMapper which handles the exceptions as follows:

ExceptionTreated AsResponse CodeLogged (Level)
com.intershop.component.rest.capi.RestExceptionclient errorunchangedtrace without stacktrace
jakarta.ws.rs.ClientErrorExceptionclient errorunchangedtrace without stacktrace
org.glassfish.jersey.server.ParamExceptionclient error400

trace without stacktrace

jakarta.ws.rs.WebApplicationExceptionserver errorunchangedtrace without stacktrace
java.io.EOFExceptionclient error400trace without stacktrace
com.intershop.beehive.orm.capi.common.OCAExceptionclient error409warn without stacktrace
every other exceptionserver error500error with stacktrace

There are two additional exception mappers which handle special exceptions that may be thrown by the framework during request parsing:

MapperHandles
com.intershop.component.rest.internal.application.JsonParseExceptionMappercom.fasterxml.jackson.core.JsonParseException
com.intershop.component.rest.internal.application.JsonMappingExceptionMappercom.fasterxml.jackson.databind.JsonMappingException

Both mappers respond with HTTP-400 and log the exception (no stacktrace) using the trace level.

Disclaimer
The information provided in the Knowledge Base may not be applicable to all systems and situations. Intershop Communications will not be liable to any party for any direct or indirect damages resulting from the use of the Customer Support section of the Intershop Corporate Web site, including, without limitation, any lost profits, business interruption, loss of programs or other data on your information handling system.
Home
Knowledge Base
Product Releases
Log on to continue
This Knowledge Base document is reserved for registered customers.
Log on with your Intershop Entra ID to continue.
Write an email to supportadmin@intershop.de if you experience login issues,
or if you want to register as customer.