To speed up the development and future upgrade, we split the huge
application into multiple AJAX services. Each AJAX service in running in
it's own application pool and it can be run on different server. The
design works perfectly. But, when you want to consume the AJAX services
through the browser, you bang your head: "Cross-Origin Request Blocked".
This is the error message that appeared in the Firefox:
Cross-Origin
Request Blocked: The Same Origin Policy disallows reading the remote
resource at http://localhost/schedule/q?code=tx&ts=1507099862873.
(Reason: CORS header ‘Access-Control-Allow-Origin’ does not match
‘(null)’).
Google Chrome returned an error message that is slightly different:
Failed
to load http://localhost/schedule/q?code=tx&ts=1507099946004: The
'Access-Control-Allow-Origin' header contains multiple values '*, *',
but only one is allowed. Origin 'http://localhost:56269' is therefore
not allowed access.
Now, if you are googling for the solution, you will end up with add the following settings in the web.config.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
But, the wild card origin is no longer supported. You ended up with adding the specific origin.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:56292" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>
Imagine
that you are hosting your AJAX services in multiple servers with
different sub-domains.... the above solution will not work. This is
because you are not allowed adding more than one domain name to
"Access-Control-Allow-Origin".
To solve the problem, we need to handle the OPTIONS verb by adding the following settings in the web.config:
<system.webServer>
<handlers>
<add verb="OPTIONS" name="check_opt" path="*" type="ajaxLib.CORS_OPTIONS" />
</handlers>
</system.webServer>
And below is the simplified code that allows CORS:
namespace ajaxLib {
public class CORS_OPTIONS : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (context.Request.HttpMethod.ToUpper() == "OPTIONS") {
string s = context.Request.Headers["Origin"];
if (!string.IsNullOrWhiteSpace(s))
{
context.Response.AppendHeader("Access-Control-Allow-Origin", s);
context.Response.AppendHeader("Access-Control-Allow-Headers", "Content-Type");
context.Response.AppendHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
}
}
}
public bool IsReusable { get { return false; }}
}}
Two possibilities if
you want to use the above code in the live environment,
1. If your service allows public access without any restriction, skip checking the Origin value.
2. If your service allows specific domain to access, you must check the Origin value before return it to the caller.