
JSON Web Services
NOTE: This document is not maintained any more. It has been moved to official Liferay development guide: https://github.com/liferay/liferay-docs/tree/master/devGuide
The idea behind JSON Web Services is to provide portal service methods as JSON API. This makes services methods easily accessible using HTTP request not only from portals javascript, but also from any JSON-speaking client out there.
This is not a new idea and the portal already contains an implementation that allows users calling services. This time we went one step further and developed an enhanced and more convenient and developer-friendly solution. Yay!
There are following parts of JSON Web Services functionality: registration, invocation and results. Each part will be discussed in more details.
Registering JSON Web Services #
By default, all remote-enabled services (i.e. with remote-service="true" on entity in service.xml) will be exposed as JSON web services. When Service Builder creates some Service.java interface, it will also add @JSONWebService annotation on class level in that service.
Therefore, when service interface is annotated with @JSONWebService annotation, all service methods becomes registered and available as JSON Web Services.
Service interface is generated file and it is recommended not be modified by user directly. Therefore, if user wants to manually control which methods to expose and to hide, he can do that in ServiceImpl class of that service. When service implementation class (ServiceImpl) is annotated with @JSONWebService annotation, interface will be ignored and only service implementation will be considered for configuration.
In other words, @JSONWebService annotations in service implementation overrides default configuration from service interface.
And that’s all! Upon start, portal scans all classes on classpath for annotation usage (don’t worry, portal scans raw class content and only jars and classes of interest are loaded, not all of them). Each class that uses the annotation is examined and its methods becomes exposed as JSON API. Registration follows explained rule: ServiceImpl are examined first and then Service interface, if necessary.
For example, let’s look the DLAppService:
@JSONWebService public interface DLAppService { ...
It contains the annotation that will be found when portal is started. Notice the following lines in console output:
10:55:06,595 DEBUG [JSONWebServiceConfigurator:121] Configure JSON web service actions 10:55:06,938 DEBUG [JSONWebServiceConfigurator:136] Configuring 820 actions in ... ms
At this point, registration is done and all service methods of DLAppService (and of other services as well) are registered as JSON Web Services.
Portlets #
Of course, custom portlets can be scanned, too, and their service can become part of the JSON API. For this you must add the following in portlets web.xml:
<servlet> <servlet-name>JSON Web Service Servlet</servlet-name> <servlet-class>com.liferay.portal.kernel.servlet.PortalClassLoaderServlet</servlet-class> <init-param> <param-name>servlet-class</param-name> <param-value>com.liferay.portal.jsonwebservice.JSONWebServiceServlet</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JSON Web Service Servlet</servlet-name> <url-pattern>/api/jsonws/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>JSON Web Service Servlet</servlet-name> <url-pattern>/api/secure/jsonws/*</url-pattern> </servlet-mapping>
This enables the servlet that is responsible for scanning JSON web services configuration.
Do not forget: to access portlet JSON Web Services you must use portlet context path, for example: http://localhost:8080/<portlet>/api/..
List available JSON Web Services #
To overview and verify which service methods are registered and available, user can use following URL to list them all in browser (notice the ‘jsonws’ in the URL, it is relevant for all JSON Web Services calls - more on invocation later):
http://localhost:8080/api/jsonws
The result is a list of all registered and exposed service methods. For each method user can see more details, by clicking the method name. On method details page, user can even invoke the method for testing purposes.
To list registered services on portlet, don't forget to use portlet contenxt path:
http://localhost:8080/<portlet>/api/jsonws
More on registration #
As said, user can control registration by using annotations in the ServiceImpl class. This completely overrides interface configuration. Moreover, user can fine-tune which methods are visible using annotations on method level.
Ignoring a method #
To ignore a method from an exposed service class, just annotated method with:
@JSONWebService(mode = JSONWebServiceMode.IGNORE)
Such methods will be not available as JSON api.
HTTP method name and URL #
As expected, it is also possible to define custom HTTP method name and URL names, using the same annotation.
@JSONWebService(value = "add-file-wow", method = "PUT") public FileEntry addFileEntry(
In this example, method addFileEntry will be mapped to /dlapp/add-file-wow (actually to the: /api/jsonws/dlapp/add-file-wow) and can be accessed using HTTP method PUT.
If URL name starts with a slash character (‘/’), only method name is used to form service URL; class name is ignored.
@JSONWebService("/add-something-very-specific") public FileEntry addFileEntry(
Similarly, user can change the class name part of the URL, by setting the value in class-level annotation:
@JSONWebService("dla") public class DLAppServiceImpl extends DLAppServiceBaseImpl {
Now all service methods would have 'dla' in the URL instead of default 'dlapp'.
Manual registration mode #
Up to now, it is expected that most of the service methods is going to be exposed and allowed user to ignore few. But sometimes user wants a different behavior: to explicitly specify only methods that are going to be exposed. This is possible, too, using so-called manual mode on class-level annotation. User then annotates only methods that has to be exposed.
@JSONWebService(mode = JSONWebServiceMode.MANUAL) public class DLAppServiceImpl extends DLAppServiceBaseImpl { ... @JSONWebService public FileEntry addFileEntry(
Configuration #
JSON Web Services can be easily turned off in portal.properties by setting:
json.web.service.enabled=false
Strict HTTP methods #
JSON Web Service services will be binded to either GET or POST HTTP method. If service method is a read-only method, i.e. it which name starts with 'get', 'is' or 'has', service will be binded to GET method; otherwise it will be binded to POST.
By default, portal is not checking HTTP method for a service call, i.e. it works in "non-strict http method" mode. Meaning: services may be invoked using any HTTP method. If you need the strict mode, you can set it with:
jsonws.web.service.strict.http.method=true
Disabled HTTP methods #
When strict HTTP method is enabled, you can even filter the access based on HTTP methods. For example, you can set the portal JSON Web Services work only in read-only mode, by disabling POST (and other) HTTP methods. For example:
jsonws.web.service.invalid.http.methods=DELETE,POST,PUT
JSON Web Service Invocation #
All services are invoked through its Util classes, as you would use it from the java code.
JSON web services can be invoked in several ways depending on how parameters (i.e. method arguments) are sent.
Passing parameters as part of URL path #
Parameters can be passed as part of the URL path. After the service URL (defined by class and method annotations, as we've seen above) user can append method parameters (name/value pairs). Parameter names are formed form method argument names, by converting camel-case to lowercased separated-by-dash name. Example:
http://localhost:8080/api/secure/jsonws/dlapp/get-file-entries/repository-id/10172/folder-id/0
Parameters may be given in any order; it’s not necessary to follow arguments order as defined by methods.
When some method name is overloaded, the best match will be used - a method that contains the least of arguments undefined.
Passing parameters as URL query #
Parameters can be passed as request parameters, too. The difference is that now parameter names are equals to service method argument names:
http://localhost:8080/api/secure/jsonws/dlapp/get-file-entries?repositoryId=10172&folderId=0
Everything else remains the same, like order is not important, etc
Mixed way of passing parameters #
Parameters can be passed in mixed way: some can be part of the URL path and some can be specified as request parameters.
Sending NULL values #
To pass null value for some argument, simply prefix that parameter name with a dash '-'. For example:
.../dlsync/get-d-l-sync-update/company-id/10151/repository-id/10195/-last-access-date
Here the 'last-access-date' parameter will be null.
Null parameters, therefore, do not have a value specified. Of course, null parameters do not have to be the last one, as in this example.When passed as request parameter, its value will be ignored and null will be used instead:
<input type="hidden" name="-last-access-date" value=""/>
When using JSON RPC (see below), null values may be sent even without a prefix! For example:
"last-access-date" : null
Difference between URL and query encoding #
This is something often forgotten: there is a difference between URL and query (request parameters) encoding. Most illustrative difference is how space character is encoded. When space is part of the URL path it is encoded as %20, when it is part of the query it is encoded as plus sign (+).
Sending files as arguments #
Files can be uploaded using multipart forms and request. Example
<form action="http://localhost:8080/api/secure/jsonws/dlapp/add-file-entry" method="POST" enctype="multipart/form-data"> <input type="hidden" name="repositoryId" value="10172"/> <input type="hidden" name="folderId" value="0"/> <input type="hidden" name="title" value="test.jpg"/> <input type="hidden" name="description" value="File upload example"/> <input type="hidden" name="changeLog" value="v1"/> <input type="file" name="file"/> <input type="submit" value="addFileEntry(file)"/> </form>
JSON RPC #
JSON Web Service may be invoked using JSON RPC. Good part of JSON RPC 2.0 specification is supported. Parameters may be passed only as named parameters, since positional are not supported - there are too many overloaded method for convenient use of positional parameters. Example:
POST http://localhost:8080/api/secure/jsonws/dlapp {"method":"get-folders", "params":{"repositoryId":10172, "parentFolderId":0}, "id":123, "jsonrpc":"2.0"}
Default parameters #
If user is authenticated when accessing JSON web services, some parameters will be available by default. They do not need to be explicitly specified, only if we want to change their default value.
Default parameters are:
userId - id of authenticated user
user - full User object
companyId - users company
serviceContext - empty service context object
Object parameters #
Most services accept simple parameters: numbers, strings etc. However, sometimes we need to provide an object of non-simple type as an service parameter.
Simillary to null parameters and the '-' prefix, to create an instance of some parameter just prefix it with a plus sign, '+'. You don't have to specify any parameter at all. For example:
/jsonws/foo/get-bar/zap-id/10172/start/0/end/1/+type
or as request parameter (+ sign has to be encoded!):
/jsonws/foo/get-bar?zapId=10172&start=0&end=1&%2Btype
or
<input type="hidden" name="+type" value=""/>
If parameter is of abstract type or interface, it can't be created. We must specify the concrete implementation. This can be done by adding suffix to the parameter, for example:
/jsonws/foo/get-bar/zap-id/10172/start/0/end/1/+type:com.liferay.CoolType
or
<input type="hidden" name="+type:com.liferay.CoolType" value=""/>
But concerte implementation can be set as a value, too! For example:
<input type="hidden" name="+type" value="com.liferay.CoolType"/>
or in JSON RPC:
"+type" : "com.liferay.CoolType"
Map and List parameters #
Map and List parameters are also supported, by sending json objects and arrays. If parameters Map or List contains generic (e.g. Map<Locale, String>), then portal will generify the map/list before passing it to the method.
Inner Parameters #
In some cases it is needed to populate objects that are passed as parameters. A good example is a default parameter, serviceContext. Sometimes, we need to set some of its inner properties, such as: addGroupPermissions, scopeGroupId etc. to make a successful JSONWS call.
To pass inner parameters, just provide them using a 'dot' notation. For this example it means that you can provide: serviceContext.addGroupPermissions, serviceContext.scopeGroupId parameters, together with others. Our engine will recognize them as inner parameters (they have a dot in the name) and will inject their values into existing parameters, before API method is executed.
Inner parameters are not count as regular parameters for matching methods.
Note: use inner parameters with object parameters to set inner content of created parameter instances!
Matching service methods #
It is important to understand how service methods are matched from the url path, especially when some method is overloaded.
General rule is that besides the method name, user must provide all parameters as well. It is not important how parameters are provided (as part of the URL line or as request parameters...), neither the order is important. Even if some parameter will be null, you must provide it.
So, methods are matched by method and parameter names!
Note that inner parameters are ignored during matching.
Using hints #
It is also possible to add numeric hints that specifies how many method arguments service has. Hints are added as numbers separated by a dot in method name. For example:
/foo/method.2/param1/123/-param2
The '.2' here is a hint so only service methods with 2 arguments will be matched.
One important difference when hint is active is that now you do not have to specify all parameters. All missing arguments are treated as null. Therefore, previous example may be called as:
/foo/method.2/param1/123
since 'param2' is null.
Returned values #
No matter which way invoked, JSON web services return JSON string that represents service method result. Returned object is loosely serialized to JSON string and returned to calling client.
NOTE: This document is not maintained any more. It has been moved to official Liferay development guide: https://github.com/liferay/liferay-docs/tree/master/devGuide