This question already has answers here:
XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header
(11 answers)
Closed 2 years ago.
I'm doing a ReactJS frontend App and get data from an API created with PHP Rest API, but my react is host on localhost:3000, but my php file is hosted on localhost:80. so not sure how to write the baseurl in react, cause it always got some error until now.
May I know how to solve it? Thank you.
error:
Access to XMLHttpRequest at 'http://localhost/reacttest/src/api/read.php' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
xhr.js:184 GET http://localhost/reacttest/src/api/read.php net::ERR_FAILED
ReactJS:
import React from 'react';
// import logo from './logo.svg';
import './App.css';
import axios from "axios";
const baseUrl = "http://localhost:80/reacttest/src/api";
const sampleGet = async () => {
const result = await axios.get(baseUrl + "/read.php");
console.log(result);
};
const samplePost = async () => {
const result = await axios.post(baseUrl + "/posts", {
sampleData: "nabezap"
});
console.log(result);
};
const sampleDelete = async () => {
const result = await axios.delete(baseUrl + "/posts/4");
console.log(result);
};
function App() {
return (
<div className="App">
<button onClick={sampleGet}>GET</button>
<button onClick={samplePost}>POST</button>
<button onClick={sampleDelete}>DELETE</button>
</div>
);
}
export default App;
read.php:
<?php
header('Content-Type: application/json;charset=utf-8');// all echo statements are json_encode
include('se.php');
include('db.php');
session_start();
$doctordb = new doctorModel; //instantiate database to start using
$result = $doctordb->showDoctorinfo();
if($result == false) {
http_response_code(204); // no content
} elseif(is_array($result)) {
http_response_code(200); //success
echo json_encode($result);
}
?>
api.php:
The base url is correct (80 is default) and if you check your network tab in dev tools, you’ll see the request did in fact go out and received the expected response.
The issue is with your REST API. The clue is in the error:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
This means the server received the request, processed it and returned a response— but it didn’t attach a Access-Control-Allow-Origin: ‘http://localhost:3000’ header. When your browser receives the response from the API, it checks for this header and refuses javascript access to the response data if it’s missing. This is normal.
Setting up CORS on your REST API is the way to go. What framework (if any) are you using? I’ll edit this answer with more info once I know.
Related
I'm trying to make a GET method to my PHP API. In order to do that, I have this code:
export class PerfilComponent {
perfil: any;
constructor(private http: HttpClient) { }
ngOnInit() {
const token:string | null = localStorage.getItem('token')
console.log(token)
const headers = new HttpHeaders({'api_key': token!})
this.http.get("http://localhost:8000/api/usuario/mi-usuario", {headers})
.subscribe(
resultado => {
this.perfil = resultado;
}
);
console.log(this.perfil)
}
The API needs the token to be send through header.
This is the error I get everytime I try to send the GET request:
Access to XMLHttpRequest at 'http://localhost:8000/api/usuario/mi-usuario' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I'll put the PHP code, maybe the error is in there, but I don't think so since while trying on Postman it did work well:
#[Route('/api/usuario/mi-usuario', name: 'app_mi_usuario', methods: ['GET'])]
#[OA\Tag(name: 'Usuario')]
#[Security(name: "apikey")]
#[OA\Response(response:200,description:"successful operation" ,content: new OA\JsonContent(type: "array", items: new OA\Items(ref:new Model(type: UsuarioDTO::class))))]
public function miUsuario(UsuarioRepository $usuarioRepository,
Request $request,Utilidades $utils): JsonResponse
{
if ($utils->comprobarPermisos($request,1)) {
$apikey = $request->headers->get("apikey");
$id_usuario = Token::getPayload($apikey)["user_id"];
$usuario = $usuarioRepository->findOneBy(array("id"=>$id_usuario));
return $this->json($usuario, 200, [], [
AbstractNormalizer::IGNORED_ATTRIBUTES => ['__initializer__', '__cloner__', '__isInitialized__'],
ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER=>function ($obj){return $obj->getId();},
]);
} else {
return $this->json([
'message' => "No tiene permiso",
]);
}
}
Thanks!
Cross-Origin Resource Sharing (CORS) is a mechanism that supports secure requests and data transfers from outside origins (domain, scheme, or port).
For example, example.com uses a text font that’s hosted on fonts.com. When visiting example.com, the user’s browser will make a request for the font from fonts.com. Because fonts.com and example.com are two different origins, this is a cross-origin request. If fonts.com allows cross-origin resource sharing to example.com, then the browser will proceed with loading the font. Otherwise, the browser will cancel the request.
CORS is blocked in modern browsers by default (in JavaScript APIs). You have to handle cors error in your backend. for this follow this link.
I'm trying to forward a request made from the client for a stream, where it keeps the requests originally made from the video player intact:
Content-Type: Keep-Alive;
Range: 0-
...
What I'm Using:
Frontend: Web - ReactJS
Backend: PHP REST API
CDN: AWS CloudFront
Storage: AWS S3
Architecture Graphic
Reason:
I need to be able to authenticate the user with our own JWT middleware through the REST to validate if they can access the file.
Constraints:
Cannot use nginx to forward the request, unless there is still a way to authenticate it with the PHP Middleware.
What I've Looked Into:
aws php sdk
I've look at the AWS PHP, but the documentation on this specific functionality seems to be missing.
guzzle + php curl
I'm afraid my knowledge is lacking in terms of what I would need to pass onto the CloudFront for this to work.
cloudfront signed url/signature
Unless I'm mistaken, this would not be helpful because the video expiration for access would be set by AWS and not by the App's REST API, so if they refresh their JWT it would not be updated with the signature.
why not s3 directly?
S3 doesn't support headers for chunks like, Range: 0-100 bytes.
Any help or recommendations would be appreciated, even if it means recommending to buy something pre-built to look at how they implemented it.
======= UPDATE: June 29, 2020 =======
After the recommendation from #ChrisWilliams, I ended up creating a script on AWS Lambda#Edge with the following configurations:
Trigger: CloudFront - viewer request
The reason for viewer request was because it's the only way to get the GET query parameters from the user's original request.
Function Code:
(Please forgive the very rough code to get things working)
File: index.js
// IMPORTS
const zlib = require('zlib');
const https = require('https');
// HTML ERROR TEMPLATE
const content = `
<\!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Unauthorized Page</title>
</head>
<body>
<p>Unauthorized!</p>
</body>
</html>
`;
// TRIGGER FUNCTION
exports.handler = async (event, context, callback) => {
// Getting request and response
const originalResponse = event.Records[0].cf.response;
const request = event.Records[0].cf.request;
// Setup for html page (cont content)
const buffer = zlib.gzipSync(content);
const base64EncodedBody = buffer.toString('base64');
// Response Templates
var response401 = {
headers: {
'content-type': [{key:'Content-Type', value: 'text/html; charset=utf-8'}],
'content-encoding' : [{key:'Content-Encoding', value: 'gzip'}]
},
body: base64EncodedBody,
bodyEncoding: 'base64',
status: '401',
statusDescription: "OK"
};
var response500 = {
headers: {
'content-type': [{key:'Content-Type', value: 'text/html; charset=utf-8'}],
'content-encoding' : [{key:'Content-Encoding', value: 'gzip'}]
},
body: base64EncodedBody,
bodyEncoding: 'base64',
status: '500',
statusDescription: "OK"
};
// Perform Http Request
const response = await new Promise((resolve, reject) => {
// Expected ?token=ey...
const req = https.get(`https://myauthserver.com/?${(request && request.querystring) || ''}`, function(res) {
if (res.statusCode !== 200) {
return reject(response401);
}
return resolve({
status: '200'
});
});
req.on('error', (e) => {
reject(response500);
});
}).catch(error => error);
// Get results from promise
const results = await response;
if (results.status === '200') {
// Successful request - continue with the rest of the process
callback(null, request);
}
// Not successful, show the errors results (401 or 500)
callback(null, results);
};
NOTE: You will have to try this a few times in case any typos or syntax errors arise because of the caching. I also recommend trying this with different IP addresses to validate access to the content. Not to mention you will get scenarios of 502 if the returned request isn't formatted correctly with the base64EncodedBody.
DOUBLE NOTE: This was after looking at the tutorials from AWS that weren't working or outdated and looking at the comments of multiple devs not getting things working.
I would suggest using a Lambda#Edge function rather than adding a third stage in front of your CloudFront.
By adding a proxy in front of your CloudFront it could lead to issues with debug, and allows someone to bypass the proxy to reach your CloudFront origin without locking it down.
Using a Lambda#Edge function guarantees that the solution validates the authenticity of the JWT token, it could be configured to either validate the JWT token with the Lambda function directly or have the Lambda call an endpoint you build to validate. If the JWT is invalid it can reject the request.
Amazon have a great article with a demo stack that demonstrates how you can make use of this.
I've got a very strange issue.
local hosted PHP Slim App using XAMPP (localhost:4040)
local hosted Angular 4 App using CLI (localhost:4200)
Making API Requests using "Postman" and browser is no problem, everything works fine.
Now I'm integrating the requests into my Angular app using import { Headers, Http } from '#angular/http'; and observables.
const requestUrl = 'http://localhost:4040/register';
const headers = new Headers({
'content-type': 'application/x-www-form-urlencoded'
});
this.http
.get(requestUrl, {headers: headers})
.map(response => response.json())
.subscribe(result => {
console.log(result);
}, error => {
console.log(error);
});
The request always fails with:
Failed to load http://localhost:4040/register: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4200' is therefore not allowed access.
But: I am definitely sending these headers!
public static function createJsonResponseWithHeaders($response, $requestedData)
{
// Add origin header
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
$response = $response->withHeader('Access-Control-Allow-Methods', 'GET');
// Add json response and gzip compression header to response and compress content
$response = $response->withHeader('Content-type', 'application/json; charset=utf-8');
$response = $response->withHeader('Content-Encoding', 'gzip');
$requestedData = json_encode($requestedData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
$response->getBody()->write(gzencode($requestedData), 9);
if (!$requestedData || (count($requestedData) === 0)) {
return $response->withStatus(404)->write('Requested data not found or empty! ErrorCode: 011017');
}
return $response;
}
What I already tried for solving:
Run Slim App inside a Docker Container to get a different origin than localhost - same behaviour
Add allow-origin-header right on top of the index.php
header('Access-Control-Allow-Origin: *'); - same behaviour
Your requests are blocked because of CORS not being set up properly. There are other questions that address this, e.g. How to make CORS enabled requests in Angular 2
What you should ideally look at using is a proxy that forwards your requests to the API, the latest Angular CLI comes with support for a dev proxy (see https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md) out of the box. You set it up with a proxy.conf.json that could look like this:
{
"/api": {
"target": "http://localhost:4040",
"secure": false,
"pathRewrite": {"^/api" : ""}
}
}
What this piece of code does is any requests from Angular to a URI matching /api will be forwarded to localhost:4040.
Note that you will also need to figure out how your app will talk to the API server in a non-dev environment. I have been happy with using Nginx to serve Angular files, and act as proxy for the API.
Sorry, my bad. The solution is simple:
The "Cache-control" header in the request seems to be not allowed, although it worked fine when testing the api with Postman.
I removed the header from the request and everything worked well.
I am working on Angular4 Project.
For testing, first I had done the get request to any json api and its worked well.
Now I am working to connect the php file and testing it. In test.php I had
echo "string";
Now in console I am getting this error:
XMLHttpRequest cannot load http://localhost/php-file/test/test.php.
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4200' is therefore not allowed
access.
I also attached screenshot of it. I had googled it but unable to get any solution for this.
then I had edited the test.php like below. Is it right way to do so?
<?php
header("Access-Control-Allow-Origin: *");
$data = ['news','dfdf','ddd'];
echo json_encode($data);
?>
This is how you need to do. You need to include headers in angular as well.
import { Http, Headers, Response, RequestOptions } from '#angular/http';
headers = new Headers();
requestOptions = new RequestOptions();
this.headers.append('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
this.headers.append('Cotent-Type', 'application/json');
this.requestOptions.headers = this.headers;
return this.http.post("http://localhost/php-file/test/test.php", this.requestOptions).map(res => res.json());
This is how you should make post request. Make sure to subscribe this call and then you can get data from return.
I hope it helps.
I am trying to fetch some JSON data. I can access the data just fine in a regular web browser, like this: http://www.ebrent.net/apis/tsp.php?fund=G+Fund&start=2003-01-01&end=2004-01-01, but I cannot get it to work in jQuery. What am I doing wrong?
Please take a look at my jsFiddle: https://jsfiddle.net/MrSnrub/mq31hwuj/
var tsp_api = '//www.ebrent.net/apis/tsp.php?start=2003-01-01&end=2004-01-01';
$.getJSON( tsp_api, function(json) {
// This alert never gets called.
alert("Success!");
// Set the variables from the results array
var data = json;
// console.log('Data : ', data);
// Set the div's text
$('#div-data').text(data);
});
You cannot get the result because the remote site doesn't have CORS enabled:
If you look at the console, you'll see:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at
http://www.ebrent.net/apis/tsp.php?start=2003-01-01&end=2004-01-01.
(Reason: CORS header 'Access-Control-Allow-Origin' missing).
You can bypass CORS by using something like anyorigin.com, i.e.:
$.getJSON('http://anyorigin.com/get/?url=http%3A//www.ebrent.net/apis/tsp.php%3Fstart%3D2003-01-01%26end%3D2004-01-01&callback=?', function(data){
$('#div-data').html(data.contents);
});
This works if you run the your server without using https. Note fetchApi was used instead of jquery Library as its not readily available in the browser
var tsp_api = 'https://www.ebrent.net/apis/tsp.php?start=2003-01-01&end=2004-01-01';
function fetchData(url) {
return fetch(url, {
method: 'get'
}).then(function(response) {
return response.json();
}).catch(function(err) {
console.log(error);
});
}
fetchData(tsp_api).then((data)=> console.log(data)).catch((err)=> console.log(err));
This won't work on jsfiddle using HTTPS, the browser will refuse to load any resources over HTTP. As you've tried, changing the API URL to have HTTPS instead of HTTP typically resolves this issue. However, your ebrent.net did not allow CoRS for HTTPS connections. Because of this, you won't be able to get your result for jsfiddle