Configuring REST Channels for CORS in Zato
See how you can configure REST endpoints for CORS using Zato.
Join the DZone community and get the full member experience.Join For Free
With the rise of Single-Page Applications (SPA) in web frontends, it is often the case that backend REST APIs based on Zato need to be configured for CORS. This article will explore what CORS is and how to make Zato participate in scenarios using it.
CORS, as an acronym, has several parts:
In the context of REST APIs, which predominantly means JSON or XML served usually with POST or other calls, this can be simplified to the below.
Origin means a combination of a URL scheme, hostname, and port number. For instance, https://example.com is one origin whereas https://example.com:8443 is a different one (ports differ). Similarly, https://www.example.net is different than https://api.example.net (subdomains differ). Also, http:// vs. https:// is a different URL scheme and that means a different origin too, even if domains and ports were the same.
Sharing is the act of making resources available to XHR calls.
Putting it together, in the most commonly found case, we have a frontend web page served from one domain and a REST API served from another domain and we want for the frontend to make XHR calls to the REST endpoints. CORS is the technique employed to make it happen. Without CORS, we would encounter a restriction called Same-Origin Policy (SOP) which would prevent the calls from succeeding.
However, note that the restriction does not apply to, for instance, resources loaded via
<script src="..."> elements. This is why libraries like JQuery can be hot-linked to using CDNs or other locations but regular calls to one's REST APIs may require CORS configuration. Likewise, browser extensions may be exempt from SOP.
In short, in CORS, we deal with XHR and similar mechanisms specifically and other features that browsers have to offer may require it or not - do not be surprised if SOP and CORS are used in one place but not in another, outside of the realm of REST APIs invoked from pages served by web servers which is what this article covers.
Also, note that it is the web browser that CORS and SOP are enforced by - it means that if an endpoint is accessed via another HTTP client, such as curl, the restrictions will not apply.
Two Types of CORS Calls in REST APIs
A browser will issue two types of XHR calls:
- Simple ones, usually not used in REST APIs built around JSON or XML
- Complex ones, called pre-flighted ones, most often used with JSON or XML
With simple calls, an XHR call is made and certain specific headers will need to exist for the XHR caller to be able to read the response from the API server.
With complex ones, the browser first sends a short OPTIONS request (pre-flight), asking the server if the actual request can be sent. If the response is satisfactory, the browser will send a second request, this time with the real data that the XHR user meant to send.
Note that CORS is handled by browsers automatically. For instance, when an XHR POST call is issued, it is the browser that sends OPTIONS under the hood and the frontend developer does not control whether it happens or not.
At this point, it is best to read the Mozilla article about CORS. It goes into all the details and, in fact, given that it is concerned with the web as a whole, some parts of it do not translate directly into REST APIs and Zato but the knowledge is required nevertheless to make sure that the configuration is correct.
The rest of this blog post will assume that you now know what to configure and only a way to do it with Zato needs to be shown.
Configuring Zato for CORS
Configuration of Zato services can be condensed to two main choices:
- We can use the after_handle response hook
- We can add handle_OPTIONS
Consider the code below. It uses after_handle - this is a method that is invoked each time the handle method has just finished. We can take advantage of it to inject response HTTP headers.
Now, the same but using handle_OPTIONS.
The difference between the two is not large. Mainly, after_handle runs regardless of whether the channel is invoked using REST, SOAP, IBM MQ, AMQP, WebSockets, or any other possible which means that in some cases it may be superfluous.
On the other hand, with REST channels, after_handle runs no matter what input HTTP verb the request is using while handle_OPTIONS reacts to OPTIONS only so after_handle may be used for both simple and pre-flight CORS configuration.
All things considered, which one to use will be a choice to make based on a given integration scenario - both are applicable, depending on specific requirements.
Note also that in the code above each service has its own CORS configuration. Yet, it is likely that the same configuration should be shared by multiple services. Simple Python inheritance will work here nicely:
Or, with handle_OPTIONS:
Now, the same configuration is accessible to more than one service and if anything needs to be changed, it is done in the base service.
Observe that the code above uses 'Access-Control-Allow-Origin' only but CORS employs other headers as well - you just put them all in self.response.headers, it is a regular Python dictionary with keys mapping to HTTP header names and values to header values.
This is everything that is needed to configure Zato for CORS-using REST API calls - choose the method most suitable (after_handle or handle_OPTIONS), assign headers to self.response.headers, hot-deploy your service and that is it!
Opinions expressed by DZone contributors are their own.