XMLHttpRequest blocked by CORS Policy on axios.post - apache

The axios.post (code below) must send data to url api/add-todo, but I get these errors:
axios.post('http://localhost/vueoctober/todo/api/add-todo', todo).then(function (response) {
console.log(response);
}).catch(function(error) {
console.log(error);
});
The route api/add-todo is handled with October method Route::get() (https://octobercms.com/docs/services/router). Why is it not found?
If I change axios.post to axios.get it will be working! But I need post data, not get.
What I tried:
1) I tried to add these headers to .htaccess:
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTION"
It's working only for axios.get. The axios.post is still blocking.
2) I added Header set Access-Control-Allow-Origin "*" to httpd.conf.
Vue app is serving at port 8080, therefore axios.post url can't be relative.

I also stumbled and struggled with this on FF, even though I have this in the .htaccess:
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS".
After more searching I found a Gist by #marcomessa that fixed my issues.
https://gist.github.com/marcomessa/54d077a0208439f5340126ff629a3718

Look at the error message carefully, it says the response to the preflight request didn't have an HTTP ok status.
Clearly, your server-side code doesn't have a route handler for the OPTIONS request, so you need to add one.
As an aside, after the browser gets a successful OPTIONS response, it will make the POST request but you said:
The route api/add-todo is handled with October method Route::get()
You'll need to use Route::post() to handle that.

Hours of googling and I got answer...
1) Install plugin Cross-Origin Resource Sharing (CORS).
2) In htaccess of Vue app add:
Header set Access-Control-Allow-Origin '*'
Header set Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTION"
NOTICE! Write SET not ADD!
That's it.

So for clarification on this. There are always numerous ways to answer a problem. Here is what I did for mine. Check this for Preflight Request. The preflight request is created by the browser and is not for security. This function I created first will throw an okay message upon a request, then if the data contains any data it will then do what it is called (this is where you check for security). I don't have to mess with .htaccess files. Though I did install the CORS plugin because it is a nice plugin. Also the video from watch-learn does the author is making a cross-origin request in which he goes over how to correct the problem. I think he just filmed the video before preflight requests started to be a browser norm. Found routing information here.
Route::match(['POST', 'OPTIONS'],'api/update-todo', function(Request $req) {
$data = $req->input();
if (!empty($data)) {
Todo::where('id', $data['id'])
->update([
'name' => $data['name'],
'description' => $data['description'],
'status' => $data['status']
]);
return response()->json([
'Success' => $data,
]);
} else {
return response()->json([
'Success' => $req,
]);
}
});

I can not resolve it via axios, I wasted a lot of hours, but I resolved it very easy by this way.
Let's think we are posting:
{name:"Cynthia Merk", age:"22"}
I did the next function to send the last JSON (any JSON structure works):
const PostFunction = (data, letFunction, errorHandle) => {
let uri = "http://.../create.php";
let xhr = new XMLHttpRequest();
xhr.overrideMimeType("application/json");
xhr.open("POST", uri, true);
xhr.addEventListener("readystatechange", function() {
if(xhr.readyState === 4 && xhr.status === 200) {
letFunction(this.responseText);
}else{
errorHandle(this.responseText);
}
});
xhr.send(JSON.stringify(data));
}
You can invoke this function, it needs changes the "uri" variable value and it's required to use JSON.stringify to send the data.
In PHP the API is very easy too, for dummies:
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token, X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE");
header("Allow: POST, GET, OPTIONS, PUT, DELETE");
class DB extends PDO {
private $host = 'localhost';
private $dbname = 'zamuSysScheme';
private $user = 'root';
private $password = 'admin';
private $charset = 'utf8';
public function __construct(){
try{
$dns = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
parent::__construct($dns, $this->user, $this->password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
} catch(PDOException $e){
echo 'Error: ' . $e->getMessage();
exit;
}
}
}
$method = $_SERVER['REQUEST_METHOD'];
$pdo = new DB();
if($_SERVER['REQUEST_METHOD'] == 'POST'){
$json = file_get_contents('php://input');
$decoded = json_decode($json, true);
try{
$Name = $decoded["name"];
$Age = $decoded["age"];
if(!isset($Name)){
header("HTTP/1.1 402 FAIL");
echo "The paramenter Name is not present";
exit;
}
if(!isset($Age)){
header("HTTP/1.1 403 FAIL");
echo "The paramenter Age is not present";
exit;
}
$sqlStatement = "INSERT INTO Client (Name, Age) VALUES";
$sqlStatement .= "(:Name, :Age)";
$stmt = $pdo->prepare($sqlStatement);
$stmt->bindValue(':Name', $Name, PDO::PARAM_INT);
$stmt->bindValue(':Age', $Age, PDO::PARAM_INT);
$stmt->execute();
$Client_Id = $pdo->lastInsertId();
if($Client_Id){
header("HTTP/1.1 200 OK");
echo $Pago_Id;
exit;
}
}catch(Exception $except){
header("HTTP/1.1 400 FAIL");
echo "Error: " . $json . " /// " . $except;
exit;
}
}
header("HTTP/1.1 401 BAD REQUEST");
?>
I hope it can help you, any question is allowed and if I have the answer I'll glad to help.

Related

How to remove these swagger warnings, about HEAD and OPTIONS?

I used swagger on express js and when I do a query I am getting this warnings.
WARNING! Unable to find a Swagger operation that matches HEAD ... - this will show if I will try to use curl.
WARNING! Unable to find a Swagger operation that matches OPTIONS ... - while this one is when accessed from a webpage.
I already add helmet and this to the code.
app.use((_, res: Response, next: NextFunction) => {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
res.header(
'Access-Control-Allow-Headers',
'Content-Type, api_key, Authorization',
);
next();
});
What I miss?
Finally got it working, I mean the warning is not showing anymore. The solution is just a one liner, sending HTTP status of 200 telling the browser that the request is supported.
export function optionCORS(req: Request, res: Response, next: NextFunction): void {
// I am just setting the headers if the request is an OPTION or HEAD
// For now I didn't interfere with the other request like POST, etc.
// I have a guess that swagger is is doing it.
if (req.method === 'OPTIONS' || req.method === 'HEAD') {
const origin: string = req.headers.origin as string;
// On my angular I have interceptor that will set the ```withCredentials```
// option to true, so I cant use * on Allow-Origin so I grab the
// request.headers.origin an used it.
// Also we need to set the Allow-Credentials to true.
res.header('Access-Control-Allow-Origin', origin);
res.header('Access-Control-Allow-Credentials', 'true');
// Then the usual headers.
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS');
res.header(
'Access-Control-Allow-Headers',
'Accept, Authorization, Content-Type, Content-Length, Origin, ' +
'X-Requested-With',
);
// And this what I miss, just the HTTP 200 status.
// So the client will know that the request is supported.
res.sendStatus(200);
return;
}
// If the request is not an OPTIONS or HEAD continue as usual,
// it look like swagger is handling it.
next();
}
Then you can use it as middleware.
app.use(optionCORS);
If you using swagger-express-middleware set WARN=off environment variable
https://github.com/APIDevTools/swagger-express-middleware/blob/ed73f82e57adb868b10e1989dac555b8d943dab8/lib/helpers/util.js#L27

CodeIgniter 3 - set_header does not work

This issue occurs when I'm trying to build CORS enabled RESTful API.
When I put this inside my view file (I'm using CodeIgniter)
// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
$this->output->set_header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}", true);
$this->output->set_header('Access-Control-Allow-Credentials: true', true);
$this->output->set_header('Access-Control-Max-Age: 86400', true); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
$this->output->set_header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS", true);
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
$this->output->set_header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}", true);
}
$this->output->set_header("Content-type: text/x-json", true);
None of the code above that sets the header works. Even when I used PHP's own header() function.
I had to set the headers from .htaccess files to make CORS work.
What is the issue here? I'd like to be able to set the headers from CodeIgniter's view file.
Help.
PS : I'm using CI 3.1.2
please use something:
$response = (new \CI_Output())
->set_status_header(200)
->set_header('Content-Type: application/json')
->set_header('Access-Control-Allow-Origin: *')
->set_header('Access-Control-Max-Age: ');
$response->_display();
session_write_close();
exit;

CORS issue between subdomains in Laravel5

I built an API on Laravel5 and had CORS issues on localhost in development enviroment, so I developed this middleware and used on the API's routes, which solved the issue:
class Cors
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$response = $next($request)
->header('Access-Control-Allow-Origin', 'http://zekrishna.com')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
return $response;
}
}
When I depolyed the app the CORS problems started again. I used the same server to host the API and front-end, where the front-end is on the server's root and the API on a subdomain.
Here's my .htacess on public/:
...
<IfModule mod_headers.c>
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
</IfModule>
What am I doing wrong?
Here's the error:
XMLHttpRequest cannot load
http://api.zekrishna.com/api/agenda/?api_token=9BuDRcUxUbl9jW2FDnMxToijR8plDsT4xkVRg7m3AThJHJWXqsNSOGy94x1B.
Redirect from
'http://api.zekrishna.com/api/agenda/?api_token=9BuDRcUxUbl9jW2FDnMxToijR8plDsT4xkVRg7m3AThJHJWXqsNSOGy94x1B'
to
'http://api.zekrishna.com/api/agenda?api_token=9BuDRcUxUbl9jW2FDnMxToijR8plDsT4xkVRg7m3AThJHJWXqsNSOGy94x1B'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin
'http://zekrishna.com' is therefore not allowed access.
EDIT
I deleted my .htaccess rules and changed my middleware: (due to duplicated Access-Control-Allow-Origin on response header)
public function handle($request, Closure $next)
{
$response = $next($request)
->header('Access-Control-Allow-Origin', 'http://zekrishna.com')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers: *')
->header('Access-Control-Allow-Credentials: true')
->header('Access-Control-Allow-Credentials: true');
return $response;
}
I'm using jQuery $.ajax method to call the API
const API_TOKEN = '9BuDRcUxUbl9jW2FDnMxToijR8plDsT4xkVRg7m3AThJHJWXqsNSOGy94x1B';
const API = 'http://api.zekrishna.com/api';
...
$.ajax({
url: API + '/agenda/',
type: 'GET',
crossDomain: true,
data: {api_token: API_TOKEN}
})
.done(setConcerts)
I can access this via an HTTP GET, and there appears to be two headers sent. Your htaccess is setting one, as is your middleware.
XMLHttpRequest cannot load http://api.zekrishna.com/api/agenda?api_token=9BuDRcUxUbl9jW2FDnMxToijR8plDsT4xkVRg7m3AThJHJWXqsNSOGy94x1B. The 'Access-Control-Allow-Origin' header contains multiple values 'http://zekrishna.com, *', but only one is allowed. Origin is therefore not allowed access
Is it possible that your local environment htaccess isn't kicking in?
I removed the last slash on my request and worked!
The server was redirecting and these request was not allowed.
Before:
$.ajax({
url: API + '/agenda/',
...
})
then:
$.ajax({
url: API + '/agenda',
...
})

AngularJS : Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource

here is my code :
angular.module('option')
.factory('optionListService', ['$resource', function($resource) {
return $resource(HOST+'option/action/:id', {}, {
'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'}
});
}]);
and this work for GET requests and not for POST !
I'm using Apache as a server and configured it with :
<Limit GET HEAD POST PUT DELETE OPTIONS>
Order Allow,Deny
Allow from all
</Limit>
Header set Access-Control-Allow-Origin "*"
and in my angularjs I include in config of module app:
delete $httpProvider.defaults.headers.common['X-Requested-With'];
delete $httpProvider.defaults.headers.post['Content-type'];
but the request POST still not working !!
I hope that someone can give any idea.
Add those headers on the server side:
Access-Control-Request-Headers: X-Requested-With, accept, content-type
Access-Control-Allow-Methods: GET, POST
If still not working post the details of the preflight OPTIONS request which the browser is sending.
Why is this required?
If it is not a simple request (e.g. GET or POST of form data) the browser sends a preflight HTTP OPTIONSrequest to the server to check if CORS is allowed. This request contains some Access-Control-Request headers (can differ based on the specific request):
Access-Control-Request-Headers: accept, content-type
Access-Control-Request-Method: POST
Now it is important that the server references the same Access-Control-Allow header in the response:
Access-Control-Allow-Headers: accept, content-type
Access-Control-Allow-Methods: POST
Access-Control-Allow-Origin: *
Otherwise the request is rejected by the browser.
#ilyas : finaly after 3hours of reseach I sovelved this problem
//Part added by ilyas :
if (isset($_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
//End of part.
I hope this help others.
Add Header into your file which you hitting from ajax call as follows
<? php header('Access-Control-Allow-Origin: *'); ?>
I found great example and explanation here http://www.codingpedia.org/ama/how-to-add-cors-support-on-the-server-side-in-java-with-jersey/
#GET
#Path("{id}")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response getPodcastById(#PathParam("id") Long id, #QueryParam("detailed") boolean detailed)
throws IOException, AppException {
Podcast podcastById = podcastService.getPodcastById(id);
return Response.ok() //200
.entity(podcastById, detailed ? new Annotation[] {PodcastDetailedView.Factory.get()} : new Annotation[0])
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS").build();
}
Here is a problem on server side. If your application is using spring framework. You can fix it by using the filter method
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER,Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");
res.addHeader("Access-Control-Expose-Headers", "xsrf-token");
if ("OPTIONS".equals(req.getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
By the way, you can dig deeper it via post angularjs spring cross-origin request blocked

CAS authentication and redirects with jQuery AJAX

I've got an HTML page that needs to make requests to a CAS-protected (Central Authentication Service) web service using the jQuery AJAX functions. I've got the following code:
$.ajax({
type: "GET",
url: request,
dataType: "json",
complete: function(xmlHttp) {
console.log(xmlHttp);
alert(xmlHttp.status);
},
success: handleRedirects
});
The request variable can be either to the CAS server (https://cas.mydomain.com/login?service=myServiceURL) or directly to the service (which should then redirect back to CAS to get a service ticket). Firebug shows that the request is being made and that it comes back as a 302 redirect. However, the $.ajax() function isn't handling the redirect.
I wrote this function to work around this:
var handleRedirects = function(data, textStatus) {
console.log(data, textStatus);
if (data.redirect) {
console.log("Calling a redirect: " + data.redirect);
$.get(data.redirect, handleRedirects);
} else {
//function that handles the actual data processing
gotResponse(data);
}
};
However, even with this, the handleRedirects function never gets called, and the xmlHttp.status always returns 0. It also doesn't look like the cookies are getting sent with the cas.mydomain.com call. (See this question for a similar problem.)
Is this a problem with the AJAX calls not handling redirects, or is there more going on here than meets the eye?
There is indeed more going on than meets the eye.
After some investigation, it appears that jQuery AJAX requests made in this way fail if they're not made to the same subdomain. In this example, requests are being made to cas.mydomain.com from a different server. Even if it is also on mydomain.com, the request will fail because the subdomain doesn't match.
jQuery AJAX does handle redirects properly. I did some testing with scripts on the same subdomain to verify that. In addition, cookies are also passed as you would expect. See my blog post for this research.
Also keep in mind that the protocols must be the same. That is, since cas.mydomain.com is using HTTPS, the page from which you are calling it must also be on HTTPS or the request will fail.
Cross domain calls are not allowed by the browser. The simplest way would be to use JSONP on the mobile application end and use a CAS gateway to return a ticket.
You can make such cross-domain AJAX calls with a PHP proxy. In the following example the proxy is capable of calling REST web services that return a JSON string.
wsproxy.php
<?php
if (!isset($_POST["username"]) || !isset($_POST["password"]))
die("Username or password not set.");
$username = $_POST["username"];
$password = $_POST["password"];
if (!isset($_GET['url'])
die("URL was not set.");
//Rebuild URL (needed if the url passed as GET parameter
//also contains GET parameters
$url = $_GET['url'];
foreach ($_GET as $key => $value) {
if ($key != 'url') {
$url .= "&" . $key . "=" . $value;
}
}
//Set username and password for HTTP Basic Authentication
$context = stream_context_create(array(
'http' => array(
'header' => "Authorization: Basic " . base64_encode("$username:$password")
)
));
//Call WS
$json = file_get_contents($url, false, $context);
// Read HTTP Status
if(isset($http_response_header[0]))
list($version,$status_code,$msg) =
explode(' ',$http_response_header[0], 3);
// Check HTTP Status
if($status_code != 200) {
if($status_code == 404) {
die("404 - Not Found");
} else {
die($status_code . " - Error");
}
}
//Add content header
header('Content-Type: application/json');
print $json;
?>
URL usage
http://yourDomain.com/wsproxy.php?url=https://wsToCall.com/ws/resource?param1=false&param2=true
jQuery $.ajax or $.post
Note that if you don't need to pass username and password, then a GET request is sufficient.
$.ajax({
type : "POST",
url : "http://" + document.domain +
"/wsproxy.php?url=http://wsToCall.com/ws/resource?param1=false&param2=true",
dataType : "json",
success : handleRedirects,
data: { username: "foo", password: "bar" }
});