Cross-Origin Requests
Modern web browsers all allow REST interfaces to external services depends on using a request-response standard called Cross-Origin Resource Sharing.
This mode is available by setting the
mode
attribute of a JavaScript
Request
object. The following is
an example using the
Fetch API
async function sct_postData(url, data={}) { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, mode: 'cors', body: JSON.stringify(data) }); return response.json(); }
Specifying
'no-cors'
does not mean relax the protocol, it means
don't follow the protocol which might allow external resource.
Uncomprehending Options
The first step to enabling CORS may be to prevent the web framework from
sending deprecated headers intended to prevent cross-site scripting.
For example to prevent Sinatra from setting the
X-Xss-Protection
header
disable :protection
(It is also possible that a similar option needs to be removed from a reverse proxy.)
Preflight Rules
Before making a cross-origin request browsers use an
OPTIONS
request to find out what parameters are permitted
options "*" do response.headers["Access-Control-Allow-Origin"] = "*" response.headers["Access-Control-Allow-Methods"] = "POST, PATCH" response.headers["Access-Control-Allow-Headers"] = "Content-Type" 200 end
For the example above
Content-Type
needs to be added to the list of allowed headers since
application/json
is not one of the types allowed by default.
Enabling a Public Endpoint
The following is a simplified version of the route used in sidecomment.io :
post '/ticket' do response.headers["Access-Control-Allow-Origin"] = env['HTTP_ORIGIN'] response.headers["Access-Control-Allow-Methods"] = "POST" content_type :json create_ticket JSON.parse(request.body.read) end
These three headers match the broader allow list defined by the
OPTIONS
reply. Testing this can be tricky because data returned over the wire is
not nessesarily accessible through JavaScript.
(Chrome will show "Failed to show response data" in the developer tools
window.)
It probably makes sense to provide some error handling that provides a hint as to why the reply is empty
if (response.body) return response.json(); else return {'error': 'empty reply (cross-origin request blocked?)'};
Once these components are lined up you should be able to observe a successful request and response!