Here is my AngularJS code, (it works fine if I remove the header option).
$http.get(env.apiURL()+'/banks', {
headers: {
'Authorization': 'Bearer '+localStorageService.get('access_token')
}
})
Here is the request:
OPTIONS /banks HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://localhost:8081
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36
Access-Control-Request-Headers: accept, authorization
Accept: */*
Referer: http://localhost:8081/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,vi;q=0.6
And response:
HTTP/1.1 404 Not Found
Access-Control-Allow-Headers: Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin: http://localhost:8081
Content-Type: text/plain; charset=utf-8
Date: Mon, 17 Mar 2014 11:05:20 GMT
Content-Length: 19
I added both Accept and Authorization header but the request still fails?
Does the capitalization (I mean authorization vs Authorization) result in that failure? If yes, how can I make AngularJS stop doing that?
if origin := req.Header.Get("Origin"); origin == "http://localhost:8081" {
rw.Header().Set("Access-Control-Allow-Origin", origin)
rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
rw.Header().Set("Access-Control-Allow-Headers",
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
Go server routing code:
r := mux.NewRouter()
r.HandleFunc("/banks", RetrieveAllBank).Methods("GET")
http.ListenAndServe(":8080", r)
OK, the issue because I fotgot to handle the "OPTIONS" request (to make a CORS browser will send a preflight OPTIONS request first and then the 'real' request if accepted by the server).
I only need to modify my Go server (see the comment):
func main() {
r := mux.NewRouter()
r.HandleFunc("/banks", RetrieveAllBank).Methods("GET")
http.ListenAndServe(":8080", &MyServer{r})
}
type MyServer struct {
r *mux.Router
}
func (s *IMoneyServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if origin := req.Header.Get("Origin"); origin == "http://localhost:8081" {
rw.Header().Set("Access-Control-Allow-Origin", origin)
rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
rw.Header().Set("Access-Control-Allow-Headers",
"Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
// Stop here if its Preflighted OPTIONS request
if req.Method == "OPTIONS" {
return
}
// Lets Gorilla work
s.r.ServeHTTP(rw, req)
}
Related
I have an Nginx with a simple index.thml running within a Docker. Everything works fine if I call it from a browser with http://localhost:8979/index.html, but when I call it from JQuery Ajax in another server (https://localhost:8447/) like this
$.ajax({
type: "GET",
url: "http://localhost:8979/index.html",
crossDomain: true,
success: function (data) {
$("#myDiv").html(data);
}
});
I obtain this error:
Access to XMLHttpRequest at 'http://localhost:8979/index.html' from origin 'https://localhost:8447' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
My Nginx configuration is this:
server {
listen 80;
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE, PUT';
add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type';
}
}
I can see in Devtools this two requests:
General
Request URL: http://localhost:8979/index.html
Referrer Policy: strict-origin-when-cross-origin
Provisional headers are shown
Accept: */*
Access-Control-Allow-Origin: *
Referer
sec-ch-ua: "Chromium";v="86", "\"Not\\A;Brand";v="99", "Google Chrome";v="86"
sec-ch-ua-mobile: ?0
User-Agent: M
General
Request URL: http://localhost:8979/index.html
Request Method: OPTIONS
Status Code: 405 Not Allowed
Remote Address: [::1]:8979
Referrer Policy: strict-origin-when-cross-origin
Response Headers
Connection: keep-alive
Content-Length: 559
Content-Type: text/html
Date: Thu, 12 Nov 2020 17:54:48 GMT
Server: nginx/1.19.4
Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: es-ES,es;q=0.9,zh-CN;q=0.8,zh;q=0.7
Access-Control-Request-Headers: access-control-allow-origin
Access-Control-Request-Method: GET
Connection: keep-alive
Host: localhost:8979
Origin: https://localhost:8447
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 1....
My Docker file:
FROM nginx
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
COPY ./src/static /usr/share/nginx/html
Is the problem in the Ajax request or is a configuration issue?
Try this one:
map $request_method $options_content_type {
OPTIONS "text/plain";
}
map $request_method $options_content_length {
OPTIONS 0;
}
server {
listen 80;
location / {
if ($request_method = OPTIONS) { return 204; }
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, DELETE, PUT';
add_header 'Access-Control-Allow-Headers' 'User-Agent,Keep-Alive,Content-Type';
add_header Content-Type $options_content_type;
add_header Content-Length $options_content_length;
}
}
Beware! You are declaring GET, POST, OPTIONS, DELETE and PUT as allowed HTTP methods, but in fact nothing except GET (and now OPTIONS) will work with this configuration. Do you really need all the other methods?
I am dealing with an external api. I want to post some data so i set a token in the headers to be able to access the api.
I am told that my test origin has been whitelisted http://127.0.0.1:8081/
However i get the following error.
Failed to load
https://external-api.com/api/transactions/ad2d7a69-f723-4798-9fa5-a95a76d65324/document:
Response to preflight request doesn't pass access control check: The
value of the 'Access-Control-Allow-Origin' header in the response must
not be the wildcard '*' when the request's credentials mode is
'include'.
async submitDocument(transationId, token, base64) {
const url = host + "/api/transactions/" + transationId + "/document"
const body = {
"image": base64,
}
let headers = new Headers();
headers.set('Content-type', 'application/json');
headers.set('token', token);
const request = {
method: 'POST',
body: JSON.stringify(body),
mode: 'cors',
headers: headers,
credentials: 'include'
}
const data = await fetch(url, request);
const response = await data.json();
return response;
}
This function call is being made browser side on the following page. http://127.0.0.1:8081/
Response from server
Request URL: https://externalapi.com/api/transactions/f400aaec-3fde-4458-a36e-fe03d550fc00/document
Request Method: OPTIONS
Status Code: 200
Remote Address: 54.194.37.150:443
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Headers: content-type, token
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 0
Connection: keep-alive
Content-Length: 0
Date: Fri, 22 Jun 2018 15:29:27 GMT
Server: nginx
Vary: Origin
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Access-Control-Request-Headers: content-type,token
Access-Control-Request-Method: POST
Cache-Control: no-cache
Connection: keep-alive
Host: externalapi.com
Origin: http://127.0.0.1:8081
Pragma: no-cache
Referer: http://127.0.0.1:8081/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
just read the error message carefully! Your domain must not be whitelistet with '*'.
It has to be 'http://127.0.0.1:8081'. You have to ask external-api.com to recheck it.
In your request you have credentials set in a token header and the Origin of your request is:
Origin: http://127.0.0.1:8081
The request in this case will proceed only if the server answers with:
Access-Control-Allow-Origin: http://127.0.0.1:8081
Otherwise the request is blocked by the browser
Check here for more details:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Examples_of_access_control_scenarios
In particular the section "Requests with credentials"
The AJAX request works fine, but the moment I add a header via beforeSend or headers, an OPTIONS pre-flight request is made and the GET request is aborted.
Code: $.ajax({
type: "GET",
crossDomain: true,
beforeSend: function (xhr)
{
xhr.setRequestHeader("session", $auth);
},
url: $url,
success: function (data) {
$('#something').html(data);
},
error: function (request, error) {
$('#something').html("<p>Error getting values</p>");
}
});
Similar AJAX Request w/o headers specified (the moment I add/modify header, an OPTIONS call is made)
Request GET /api/something?filter=1 HTTP/1.1
Referer http://app.xyz.dj/dashboard
Accept application/json, text/javascript, */*; q=0.01
Accept-Language en-US
Origin http://app.xyz.dj
Accept-Encoding gzip, deflate
User-Agent Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; MASMJS; rv:11.0) like Gecko
Host 162.243.13.172:8080
DNT 1
Connection Keep-Alive
Cache-Control no-cache
Similar Server Response Header (for GET request)
Response HTTP/1.1 200 OK
Server Apache-Coyote/1.1
Access-Control-Allow-Origin *
Access-Control-Allow-Methods GET, POST, DELETE, PUT, OPTIONS, HEAD
Access-Control-Allow-Headers Content-Type, Accept, X-Requested-With
Access-Control-Allow-Credentials true
Content-Type application/json
Transfer-Encoding chunked
Date Thu, 09 Jan 2014 14:43:07 GMT
What I am doing wrong?
Solved.
Thanks #JasonP for pointers. Changed Server Response Headers from
Access-Control-Allow-Headers:*
to specific ones
Access-Control-Allow-Headers: Content-Type, Accept, X-Requested-With, Session
and now it works!
Server side setup
JsonRoutes.setResponseHeaders({
"Cache-Control": "no-store",
"Pragma": "no-cache",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With"
});
Client side setup
var getRecentPosts = function (token) {
$.ajax({
url: "http://localhost:3000/publications/recentPostsAndComments",
method: "GET",
headers: {
"Authorization": "Bearer " + token,
"Content-Type": "application/json"
},
// contentType: "application/json",
success: function (data, status, xhr) {
debugger
},
error: function (xhr, status, err) {
debugger
}
});
}
I always get caught inside the error callback because of the following error:
XMLHttpRequest cannot load http://localhost:3000/publications/recentPostsAndComments. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:5000' is therefore not allowed
access.
Is there anything noticeable that I am missing here?
Update:
in the network tab, I see one option request that succeeds, but I don't see a GET request that's supposed to be sent.
Remote Address:127.0.0.1:3000
Request URL:http://localhost:3000/publications/recentPostsAndComments
Request Method:OPTIONS
Status Code:200 OK
Response Headers
view source
connection:keep-alive
content-type:text/html; charset=utf-8
date:Sat, 19 Sep 2015 23:53:48 GMT
transfer-encoding:chunked
vary:Accept-Encoding
Request Headers
view source
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8,ko;q=0.6
Access-Control-Request-Headers:accept, authorization, content-type, x-requested-with
Access-Control-Request-Method:GET
Cache-Control:no-cache
Connection:keep-alive
Host:localhost:3000
Origin:http://localhost:5000
Pragma:no-cache
Referer:http://localhost:5000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36
This is what I sometimes use and it works great.
//Server Side PHP
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
header('Access-Control-Max-Age: 2000');
header('Access-Control-Allow-Headers: Content-Type, Authorization, X- Requested-With');
?>
As you know, it should be placed before any code....
I have literally seen thousands of these "WCF" questions on the internet so far, but I am beginning to think that it is impossible. Please someone tell me I am wrong...
Background: I am working with a Self Hosted WCF Service (therefore Global.asax.cs won't help here). Also the endpoints are defined programatically. The contract is decorated with WebInvoke(Method="POST") and I am making a JQuery call to the service.
The preflight works initially with the OPTIONS method but the POST method fails with 405 Method Not Allowed. Also GET functions work perfectly.
I have been searching the internet and experimenting for about a month now and it just will not budge. This service already responds fine to another client calling it through TCP... Please could some genius help me out. Thanks
PS: What I thought was really weird about the the POST response, is the Allow: OPTIONS... Surely that should not be there?
CORS
public class CORSEnablingBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
var requiredHeaders = new Dictionary<string, string>();
requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "Origin, Cache-Control, Connection, Pragma, Content-Length, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, User-Agent");
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CORSHeaderInjectingMessageInspector(requiredHeaders));
}
app.config
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="SOAPDemoEndpointBehavior">
</behavior>
<behavior>
<webHttp/>
<crossOriginResourceSharingBehavior/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<extensions>
<behaviorExtensions>
<add name="crossOriginResourceSharingBehavior" type="Application.Host.CORSEnablingBehavior, Application.Host, Version=1.0.0.0, Culture=neutral"/>
</behaviorExtensions>
</extensions>
<bindings>
<basicHttpBinding>
<binding name="OrdersMappingSoap"/>
</basicHttpBinding>
<!--2015-08-26-->
<webHttpBinding>
<binding name="webHttpBindingWithJson"
crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
Interface
[OperationContract(Name = "Relational")]
[FaultContract(typeof(ValidationFault))]
[WebInvoke(Method = "POST", UriTemplate = "GetCustomerRelational", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
CustomerFullModel GetCustomerRelational(int clientHandle, object customerID, bool loadRelationalData);
JQuery
jQuery.ajax({
crossDomain: true,
type: "POST",
contentType: "application/json",
url: "http://localhost:8086/CustomerService/rest/GetCustomerRelational/",
data: JSON.stringify({
"clientHandle": 1824,
"customerID": "ABB029",
"loadRelationalData": true
}),
dataType: "json",
success: function(result) {
console.log("Success...");
document.getElementById("lblResponse").innerHTML = "Success: " + JSON.stringify(result.NormalResult);
},
error: function(x, s, t) {
console.log("Error...");
document.getElementById("lblResponse").innerHTML = x.responseText;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Preflight Request
OPTIONS http://localhost:8086/CustomerService/rest/GetCustomerRelational/ HTTP/1.1
Host: localhost:8086
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://stacksnippets.net/js
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Preflight Response
HTTP/1.1 200 OK
Content-Length: 0
Server: Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, Cache-Control, Connection, Pragma, Content-Length, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, User-Agent
Date: Wed, 26 Aug 2015 13:13:59 GMT
POST Request
POST http://localhost:8086/CustomerService/rest/GetCustomerRelational/ HTTP/1.1
Host: localhost:8086
Connection: keep-alive
Content-Length: 69
Accept: application/json, text/javascript, */*; q=0.01
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
Content-Type: application/json
Referer: http://stacksnippets.net/js
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
{"clientHandle":1824,"customerID":"ABB029","loadRelationalData":true}
POST Response
HTTP/1.1 405 Method Not Allowed
Allow: OPTIONS
Content-Length: 1565
Content-Type: text/html; charset=UTF-8
Server: Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, Cache-Control, Connection, Pragma, Content-Length, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, User-Agent
Date: Wed, 26 Aug 2015 13:14:02 GMT
<p>Method not allowed.</p>
I figured it out.
The main difference between this WCF and everyone else's, is the fact that mine is self hosted, while everything else is hosted on IIS (mostly).
Thanks to this article ASP.NET Compatibility Mode, the answer lies in the interception of the preflight request. IIS hosted WCF requires the interception be done in the global.asax file as follows:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
//These headers are handling the "pre-flight" OPTIONS call sent by the browser
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
HttpContext.Current.Response.End();
}
}
This however, is not possible in self hosted WCF. BUT, we can still make use of ASP.NET functionality using this line in the app.config
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
Then finally utilize this in the service class:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class TestService : ValidationModel, ITestService
{
I realized that it doesn't help just to set this to "Allowed", it must be "Required".
Final finally, in order for the preflight to start, the following code needs to be in the interface and the service:
[OperationContract]
[FaultContract(typeof(ValidationFault))]
[WebInvoke(Method = "OPTIONS", UriTemplate = "*")]
void GetOptions();
public void GetOptions()
{
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
}
Now, POST methods with complex parameters will pass the preflight test and execute the method with a response.
If you are dealing with this case in a self hosted service, the following procedure has worked for me:
Add OPTIONS method into your Interface (the Browser, before calling your POST method, calls OPTIONS to verify the CORS)
[OperationContract]
[WebInvoke(Method = "OPTIONS", UriTemplate = "*")]
void GetOptions();
Implement in your ServiceBehavior class
public void GetOptions()
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept");
WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
}
The class may have to have this attribute:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]