Junos Space SDK > Developer Guides > Junos Space Application Developer Guide > Programming with the Junos Space SDK > Using the Junos Space SDK Plug-in > Using the EJB-REST Wizard

REST Services in the Junos Space Platform

Table of contents

  1. REST Web service
  2. REST Web services in the Junos Space Platform
    1. GET request by example
    2. POST request by example
    3. PUT request by example
    4. DELETE request by example
    5. How to discover media types

REST Web service

A RESTful (or simply REST) web service is a web service implemented using HTTP and follows the REST principles listed below:

RESTful services are stateless by design, meaning no client context is being stored on the server between requests.

REST Web services in Junos Space Platform

The Junos Space Platform provides an interface to internal EJB resources through REST APIs to external users. These external use consists of providing access to Junos Space Application running on the Junos Space Platform—as Web UI applications—or back-end server applications.The general use is to partially automate the RESTification of stateless EJB methods. This results in a REST service exposing some or all of the EJB methods to the outer world. The typical case is semi-automated RESTification of stateless EJB that results in a REST service exposing some of the EJB methods to the outer world.

REST services can be implemented to include the desire specific business logic. However, since such implementation is costly in reference to maintenance, we discourage building a business logic in the implementation of REST services.

Two representations are commonly accepted by Junos Space REST services: XML and JSON. A request can specify required representation by adding the representation part to the accept header value, similar to the following example:

   "accept=application/vnd.net.juniper.space.user-management.users+xml;version=1"

The following terms (in additional to the general REST terms) are used in Junos Space REST Services:

While generally any EJB method can be mapped on an arbitrary HTTP method type, Junos Space Platform REST services implement the following convention:

Each HTTP request must contain an accept header, content-type header, or both. The Content-type header specifies the format of contents in the request body (how the request body is formatted). If server does not recognize the specified format, the request is ignored, and an error is returned. The accept header specifies the format of response body contents which the client expects to receive (from the server). Again, the server will return an error if the server does not recognize the accept header value.

Space Platform uses the complete media-type to encode three types of primary information about the media (input and output) associated with resource on the wire: its type, syntax/representation, and version. Decoded values for example, application/vnd.net.juniper.space.user-management.users+xml;version=1, are:

GET request by example

GET request to /api/space/user-management/users URL will result in the following response:

<users total="1" size="1" uri="/api/space/user-management/users">
<user key="66043" uri="/api/space/user-management/users/66043"
href="/api/space/user-management/users/66043">
<name>super</name>
<primaryEmail>super@juniper.net</primaryEmail>
<firstName>Open</firstName>
<lastName>Space</lastName>
</user>
</users>

This particular URL represents collection of users, which are returned in XML format on GET request. Each user is represented by a separate <user> tag.

The same GET request for the same URL, with the accept=application/vnd.net.juniper.space.user-management.users+xml;version=1 header, will result in the same response, as all parts of this accept media type are default ones. However, sending a GET request to this URL with accept=application/vnd.net.juniper.space.user-management.users+json;version=1 accept media type will result in the following response, returning the same information in JSON format:

{
"users" : {
"@total" : "1" ,
"@size" : "1" ,
"@uri" : "/api/space/user-management/users" ,
"user" : {
"@key" : "66043" ,
"@uri" : "/api/space/user-management/users/66043" ,
"@href" : "/api/space/user-management/users/66043" ,
"name" : "super" ,
"primaryEmail" : "super@juniper.net" ,
"firstName" : "Open" ,
"lastName" : "Space"
}
}
}

GET request for /api/space/user-management/users/66043 URL will result in following response, where the response represents a single user with ID 66043:

<user uri="/api/space/user-management/users/66043">
<id>66043</id>
<name>super</name>
<firstName>Open</firstName>
<lastName>Space</lastName>
<password>******</password>
<primaryEmail>super@juniper.net</primaryEmail>
<authMode>local</authMode>
<roles uri="/api/space/user-management/users/66043/roles">
<role uri="/api/space/user-management/users/66043/roles/65711"
href="/api/space/user-management/roles/65711">
<id>65711</id>
<name>superAdmin</name>
</role>
</roles>
<method href="/api/space/user-management/users/66043/change-password"
rel="change-password" />
</user>

Note that the same 66043 user is described both by /api/space/user-management/users and /api/space/user-management/users/66043 resources. But, as the first resource represents a collection of all users, each single user inside has less fields returned to the reduce response size. Generally, each collection resource specifies a few important fields for every single member, and the collection member resource specifies all available fields.

Also, each <user> tag inside the <users> collection root tag has uri and href attributes, which value references another resource:

href="/api/space/user-management/users/66043"

In the case of the user's collection, each element tag's uri and href values reference a single user collection member. This allows both users and automated clients to discover collection element member resource URIs by sending a GET request at the collection URI. Generally, this approach implements the HATEOAS policy, defined earlier.

In the example, the <roles> tag references the uri="/api/space/user-management/users/66043/roles" user roles resource, which can be retrieved by a GET request to this URL.

This works for collections, as well as for any other resource. In the example,the GET request for the /api/space/user-management service URL results in the following document, where each href value references a collection inside the service:

<user-management>
      <collection href="/api/space/user-management/roles" rel="roles" />
      <collection href="/api/space/user-management/capabilities" rel="capabilities" />
      <collection href="/api/space/user-management/users" rel="users" />
      <collection href="/api/space/user-management/tasks" rel="tasks" />
      <collection href="/api/space/user-management/profiles" rel="remote-profiles" />
</user-management>

POST request by example

POST request requires both accept and content-type media type headers as well as an actual request body.

The following POST request to /api/space/user-management/users service URL adds a new user to the users collection:

POST /api/space/user-management/users HTTP/1.1
accept: application/vnd.net.juniper.space.user-management.user+xml;version=1
content-type: application/vnd.net.juniper.space.user-management.user+xml;version=1;charset=UTF-8

<?xml version="1.0" encoding="UTF-8"?>
<user>
  <name>john</name>
    <firstName>John</firstName>
    <lastName>Smith</lastName>
    <password>12345</password>
    <roles>
      <role href="/api/space/user-management/roles/65717"/>
    </roles>
</user>

Here, the accept header value is:

accept: application/vnd.net.juniper.space.user-management.user+xml;version=1

This specifies that the request contains an application/vnd.net.juniper.space.user-management.user object XML representation of version 1.

The content-type header value is:

content-type: application/vnd.net.juniper.space.user-management.user+xml;version=1;charset=UTF-8

This specifies that the client accepts an added user representation in the same format.

The request body contains a new user name, first name, last name, and password.

Also, the new user is assigned with a set of roles, containing a single role specified by the reference href="/api/space/user-management/roles/65717". This reference points to the role collection member resource, which can be retrieved by sending the GET request at the URL /api/space/user-management/roles/65717.

The server response contains a 200 OK status and the following body contents:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user uri="/api/space/user-management/users/262246">
      <id>262246</id>
      <name>john</name>
      <firstName>John</firstName>
      <lastName>Smith</lastName>
      <password>/gaX8RvvouRlF1Yjq+357g==</password>
      <roles uri="/api/space/user-management/users/262246/roles">
            <role uri="/api/space/user-management/users/262246/roles/65717"
                    href="/api/space/user-management/roles/65717">
                  <id>65717</id>
                  <name>jobsPurgeAdmin</name>
            </role>
      </roles>
      <method href="/api/space/user-management/users/262246/change-password"
              rel="change-password" />
</user>

Here, the request body contains the same user description as the one sent earlier, but with additional details: the <id> field assigned by the server, and the <method> HATEOAS reference pointing to a change-password method.

The subsequent GET request to the /api/space/user-management/users URL will result in the following response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<users total="2" size="2" uri="/api/space/user-management/users">
        <user key="262246" uri="/api/space/user-management/users/262246"
                href="/api/space/user-management/users/262246">
                <name>john</name>
                <firstName>John</firstName>
                <lastName>Smith</lastName>
        </user>
        <user key="66043" uri="/api/space/user-management/users/66043"
                href="/api/space/user-management/users/66043">
                <name>super</name>
                <primaryEmail>super@juniper.net</primaryEmail>
                <firstName>Open</firstName>
                <lastName>Space</lastName>
        </user>
</users>

The users collection now contains two users and includes the one added by the POST request.

PUT request by example

The following PUT request to the /api/space/user-management/users/262246 service URL replaces or updates a user added in the previous section:

PUT /api/space/user-management/users/262246 HTTP/1.1
accept: application/vnd.net.juniper.space.user-management.user+xml;version=1
content-type: application/vnd.net.juniper.space.user-management.user+xml;version=1;charset=UTF-8

<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>john</name>
<firstName>Peter</firstName>
<lastName>Doe</lastName>
<password>123456Pwd</password>
<roles>
<role href="/api/space/user-management/roles/65717"/>
</roles>
</user>

The PUT request for the collection member should be sent to the URL of the member itself, not the collection resource URL.

This request results in a 200 OK response with the following body contents:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user uri="/api/space/user-management/users/262246">
<id>262246</id>
<name>john</name>
<firstName>Peter</firstName>
<lastName>Doe</lastName>
<password>5VSNWljeqbxc6+s01qTGCA==</password>
<roles uri="/api/space/user-management/users/262246/roles">
<role uri="/api/space/user-management/users/262246/roles/65717"
href="/api/space/user-management/roles/65717">
<id>65717</id>
<name>jobsPurgeAdmin</name>
</role>
</roles>
<method href="/api/space/user-management/users/262246/change-password"
rel="change-password" />
</user>

Similar to the POST response, the PUT response provides the changed user details.

A subsequent GET request to /api/space/user-management/users URL reveals the changes to the users collection:

<users total="2" size="2" uri="/api/space/user-management/users">
<user key="262246" uri="/api/space/user-management/users/262246"
href="/api/space/user-management/users/262246">
<name>john</name>
<firstName>Peter</firstName>
<lastName>Doe</lastName>
</user>
<user key="66043" uri="/api/space/user-management/users/66043"
href="/api/space/user-management/users/66043">
<name>super</name>
<primaryEmail>super@juniper.net</primaryEmail>
<firstName>Open</firstName>
<lastName>Space</lastName>
</user>
</users>

DELETE request by example

DELETE request should be also sent to the collection member resource URL /api/space/user-management/users/262246:

DELETE /api/space/user-management/users/262246 HTTP/1.1

The response is:

204 No Content

Meaning the user was successfully deleted.

A subsequent GET request to /api/space/user-management/users URL confirms that:

<users total="1" size="1" uri="/api/space/user-management/users">
<user key="66043" uri="/api/space/user-management/users/66043"
href="/api/space/user-management/users/66043">
<name>super</name>
<primaryEmail>super@juniper.net</primaryEmail>
<firstName>Open</firstName>
<lastName>Space</lastName>
</user>
</users>

How to discover media types

To discover what accept and content-type media-types are supported by the resource in question, the GET request can be sent to the special INFO service, where the service URL can be constructed from the resource URL in question: /api/info?uri=<uri-in-question-path>

In the example, the users collection has the URL /api/space/user-management/users URL, while the INFO service URL is /api/info?uri=/api/space/user-management/users.

The GET request to that URL results in the following response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<XRD>
<Subject>/api/space/user-management/users</Subject>
<Link rel="describedBy" type="application/xrd+xml"
href="/api/info?type=vnd.net.juniper.space.user-management.user" />
<http-methods>
<http-method type="POST">
<primary-uri>/api/space/user-management/users</primary-uri>
<query-parameters />
<headers>
<header type="Accept">
<Link rel="describedBy" type="application/xrd+xml"
href="/api/info?type=vnd.net.juniper.space.user-management.user" />
<representations>
<representation>application/vnd.net.juniper.space.user-management.user+xml;version=1</representation>
<representation>application/vnd.net.juniper.space.user-management.user+json;version=1</representation>
</representations>
</header>
<header type="Content-Type">
<Link rel="describedBy" type="application/xrd+xml"
href="/api/info?type=vnd.net.juniper.space.user-management.user" />
<representations>
<representation>application/vnd.net.juniper.space.user-management.user+json;version=1;charset=UTF-8</representation>
<representation>application/vnd.net.juniper.space.user-management.user+xml;version=1;charset=UTF-8</representation>
</representations>
</header>
</headers>
</http-method>
<http-method type="GET">
<primary-uri>/api/space/user-management/users</primary-uri>
<query-parameters />
<headers>
<header type="Accept">
<Link rel="describedBy" type="application/xrd+xml"
href="/api/info?type=vnd.net.juniper.space.user-management.users" />
<representations>
<representation>application/vnd.net.juniper.space.user-management.users+json;version=1</representation>
<representation>application/vnd.net.juniper.space.user-management.users+xml;version=1</representation>
</representations>
</header>
</headers>
</http-method>
</http-methods>
</XRD>

Where each supported HTTP request type is represented by the <http-method> tag, and for each HTTP request type, <header type="Accept"> lists the available accept media type representations, and the <header type="Content-Type"> lists available content-type media type representations.

In the example, if we want to send a POST request to the /api/space/user-management/users URL, we can put one of the following accept headers into the request:

accept: application/vnd.net.juniper.space.user-management.user+xml;version=1

or

accept: application/vnd.net.juniper.space.user-management.user+json;version=1

depending on whether our request body contains an XML or JSON user representation.

The same values can be used to specify content-type header value.