Handling CORS
What is CORS request?
Cross-Origin Resource Sharing (CORS) request is HTTP request which Origin header
does not same with Host header. By default browser blocks such requests due to same origin policy.
This may pose some problem for backend API which, sometime, configured to run on different host or port.
To allow such requests, backend API needs to tells browser that it acknowledges such request by sending appropriate response header.
Handling CORS with middleware
Fano Framework provides built-in middleware class TCorsMiddleware which is to simplify task for sending appropriate CORS headers. Read Middlewares for more information about working with middlewares.
Constructor of TCorsMiddleware expects ICors interface instance which responsible to handle CORS request.
Built-in ICors implementation
Fano Framework provides two built-in ICors implementation.
TNullCors,ICorsimplementation which simply allow all CORS request without restriction.TCorsclass is implementation which can be configured to selectively apply restriction.
Register CORS middleware with container
Fano Framework has TCorsMiddlewareFactory or TNullCorsMiddlewareFactory class
which allows you to register TCorsMiddleware with TCors or TNullCors with service container.
Both factory classes are derived from TBaseCorsMiddlewareFactory.
container.add('globalMiddlewares', TMiddlewareListFactory.create());
container.add(
'cors',
(TCorsMiddlewareFactory.create())
.allowedOriginsPatterns(['http\:\/\/localhost\:[0-9]{1,5}'])
.allowedMethods(['GET', 'POST', 'OPTIONS'])
.allowedHeaders(['X-My-Custom-Header'])
);
To use TNullCorsMiddlewareFactory, just replace TCorsMiddlewareFactory above.
Register dispatcher with middleware support
Because we need to execute middlewares, we cannot use TSimpleDispatcher class which
by default is already registered when we use, for example, TBasicAppServiceProvider class.
So we need to use TDispatcher class which support middlewares.
var globalMiddlewares : IMiddlewareLinkList;
...
globalMiddlewares := container.get('globalMiddlewares') as IMiddlewareLinkList;
container.add(
GuidToString(IDispatcher),
TDispatcherFactory.create(
globalMiddlewares,
container.get(GuidToString(IRouteMatcher)) as IRouteMatcher,
TRequestResponseFactory.create()
)
);
Attach CORS middleware to application middleware
Attach CORS middleware instance to application middleware collection to ensure CORS middleware is executed for all application routes.
globalMiddlewares.add(container.get('cors') as IMiddleware);
or with array-like syntax
globalMiddlewares.add(container['cors'] as IMiddleware);
Configure CORS settings
TBaseCorsMiddlewareFactory class provides several methods to help configure CORS
settings.
Allowed origins
To allow request from http://cors.fano and http://my.fano.
var factory : TBaseCorsMiddlewareFactory;
...
factory := TCorsMiddlewareFactory.create();
factory.allowedOrigins([ 'http://cors.fano', 'http://my.fano']);
To allow request from any origin
factory.allowedOrigins(['*']);
Allowed origins patterns
You can also configure origin to allow using regex pattern. Following code will cause
any request coming from http://localhost:port where port is integer value.
factory.allowedOriginsPatterns(['http\:\/\/localhost\:[0-9]{1,5}']);
So any requests from http://localhost:9000 or http://localhost:9100 are allowed but not
http://127.0.0.1:9000.
Allowed methods
To allowed only HTTP GET and POST
factory.allowedMethods(['GET', 'POST']);
To allowed all method
factory.allowedMethods(['*']);
Allowed headers
To allowed custom HTTP header
factory.allowedHeaders(['X-My-Custom-Header']);
To allowed all custom headers
factory.allowedHeaders(['*']);
Exposed headers
To list HTTP headers exposed by application that browser allowed to access.
factory.exposedHeaders(['X-My-Custom-Header']);
To expose all headers
factory.exposedHeaders(['*']);
Credentialed request
To allow credentialed request which aware of HTTP Cookies and HTTP Authentication.
factory.supportCredentials(true);
Max age
To set number of seconds, preflight request can be cached by browser, set it as follows
factory.maxAge(3600);
Testing CORS feature
Create a backend application that handle CORS, for example, you can use Fano Cors example application as base.
Create a simple web page to call our API via ajax, for example
<html>
<head><title>CORS Test</title></head>
<body>
<div id="content"></div>
<button id="btnLoad">Load</button>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$(document).ready(function(){
$('#btnLoad').click(function(){
$.ajax({
url: "http://fano-cors.fano/",
headers: { 'x-my-custom-header': 'some value' }
}).then(function(resp){
$('#content').text(resp.hello);
});
});
});
</script>
</body>
</html>
Save code above as client.html in your web server document root. It is assumed that our application is at http://fano-cors.fano/.
Open browser and go to http://localhost/client.html, click Load button
to execute ajax request to our API.
Browser will send ajax request with header Origin equals to http://localhost/client.html and Host equals http://fano-cors.fano/, so request is a CORS request.