Since Portofino 4.2, each page is potentially a REST API node - a collection of REST resources that share a base URL derived from the URL of the page. Some pages expose a REST API out of the box, e.g., the CRUD page. However, users can expose methods via REST in any page/action, using standard JAX-RS annotations. By default, Portofino uses Jersey as its JAX-RS provider; however, only standard JAX-RS APIs are used, so it's possible to write a module and use another implementation.
This section assumes a basic knowledge of both REST and JAX-RS. If you don't know what we're talking about, you can safely skip this section; it is not necessary to understand it in order to use Portofino. Experienced software developers can use REST APIs to build mobile applications, additional services, integrations with other systems and tools etc. on top of the functionality exposed by a Portofino application. If you're interested but you don't have the required skills at your disposal, please evaluate contracting ManyDesigns.
As we saw in the previous section, each page has a unique URL which is determined by the page's location inside the Portofino web application directory. In addition, each page exposes its API under, well, $webapp_url/api/$page_url. E.g. the page located at http://localhost:8080/demo-tt/projects/PRT/tickets will expose its API at http://localhost:8080/demo-tt/api/projects/PRT/tickets.
Currently, the only built-in page to have a dedicated REST API is the CRUD page. However, in a fresh Portofino application, you can see an example custom page making use of this feature - the FancyTree example at /samples/fancytree.
Differences from regular JAX-RS applications
In a typical JAX-RS application, you have a number of resource classes annotated with @Path. In Portofino, since the path is calculated dynamically by the framework, this is not the case. You can imagine each page as having an implicit @Path(url_of_the_page) annotation on it. Apart from that, everything works the same - you can even have sub-resources.
The default Jersey module comes with Jackson support out of the box, this means that Java and Groovy objects are automatically serialized to and deserialized from JSON. In addition, even if you don't use Jersey, Portofino ensures that any Elements Form, Stripes Resolution, and Elements XhtmlFragment are automatically written to the response if you return them from a JAX-RS annotated method. In particular, forms are serialized to JSON (in a format that can be used in reverse to write values to the form's fields), while XhtmlFragments and rendered as text/html and Resolutions are used to directly stream data to the client.
A note of caution regarding Resolutions: while in theory any Resolution can be used, most existing Resolutions in Portofino forward to JSP's that need a certain environment to function properly, an environment which is correctly set up in the Stripes lifecycle but not in the JAX-RS lifecycle. So, in general you cannot expose a regular Portofino JSP page as a REST resource.
Authentication to the REST API
Portofino tries to be pragmatic about authentication. There are many ways to do it and expert developers will undoubtedly want to have full control over it. So, the framework does not try to provide the ultimate solution for every scenario. Rather, it acknowledges that, in most applications, the typical client of the REST API will be a page of the same web application. So, the HTTP session and the regular login page are the primary method of authentication to the REST API as well. For clients outside Portofino, there are three options:
- POST username and password to /login and get back a session ID. Use the JSESSIONID cookie to maintain a session.
- Use HTTP BASIC authentication for each REST API call. This is inefficient since login must be performed each time, but if you do a call once in a while, it is ok.
- Implement your own authentication scheme, for example JSON Web Tokens. There are many options and tutorials available.
Portofino annotations for security apply to REST methods as well. If you call a method without having the required permissions, you will get either a 401 error code (authorization required) or a 403 error code (forbidden). Invoking a method when its guards are not satisfied results in a 409 error (conflict) instead.
Generic REST API for every page
Currently (version 4.2.1), all pages in Portofino expose at least these methods:
GET /:buttons- returns a JSON array with an object for each button available in the page, taking permissions into account.
GET /:configuration- returns a JSON object describing the configuration of the page. Since 4.2.1.
GET /:pages- returns a JSON object describing navigation information: the path from the root page to the current one, sibling and child pages. Since 4.2.1.
Going further - best practices, patterns, solutions
We cannot hope to cover a topic as big as REST API development here. There are many resources, tutorials, blog posts etc. around the Web. If you are stuck, you can look for help in our community channels or contact us for paid support.