I am trying to get API data from live server in ionic android app but it returns this error:
Access to XMLHttpRequest at 'https://example.com/api/categories/' from origin 'http://192.168.43.71:8100' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Server settings
Now I am using Laravel for live server which is giving the API here is how I set CORS in my laravel application:
/bootstrap/app.php
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Allow-Headers: *');
// rest of the file
due to my setup above I'm getting this result on CORS tester
Ionic settings
So I've been reading how to solve this issue and came cross lots of similar solutions and this is what I add to my ionic.config.json file
"proxies": [
{
"path": "/api/*",
"proxyUrl": "https://example.com/api/"
}
]
Get request (ionic services)
Here is how I request my get method
apiUrl = 'https://example.com/api/categories/';
constructor(private http: HttpClient) { }
getCategories(): Observable<any> {
return this.http.get(`${this.apiUrl}`).pipe(
map(categories => categories)
);
}
Any idea what else should I do to fix this issue?
SOLVED
Thanks to Stephen Romero for pointing the important part of this solution,
based on stephen answer I added this code to my function:
const httpOptions = {
headers: new HttpHeaders({
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json'
})
};
and used it in my get request like:
return this.http.get(`${this.apiUrl}`, httpOptions).pipe(
Now the for header permissions I used (installed) this package for on my laravel app and made config file set as code below:
return [
/*
|--------------------------------------------------------------------------
| Laravel CORS
|--------------------------------------------------------------------------
|
| allowedOrigins, allowedHeaders and allowedMethods can be set to array('*')
| to accept any value.
|
*/
'supportsCredentials' => false,
'allowedOrigins' => ['*'],
'allowedOriginsPatterns' => [],
'allowedHeaders' => ['*'],
'allowedMethods' => ['GET', 'OPTIONS'],
'exposedHeaders' => [],
'maxAge' => 0,
];
FOR those who doesn't use Laravel
Set your headers like this:
if($request_method = 'GET'){
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS');
header('Access-Control-Allow-Headers: Authorization, Expires, Pragma, DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range');
header("Access-Control-Expose-Headers: *");
}
The most important part of this headers is Access-Control-Allow-Headers part, if you simply use * it won't work! you need to set headers name.
Hope it helps.
Update
Forgot to mention in order to avoid error 301 you need to remove / from end of your api url.
// my api (before)
https://example.com/api/categories/
//changed to
https://example.com/api/categories
I solved my issue using these Headers for my API:
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Credentials: true ");
header("Access-Control-Allow-Methods:GET,POST");
header("Access-Control-Allow-Headers: Authorization, Content-Type, Depth, User-Agent, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control");
And Angular Http:
//GET data details
getData(authToken){
const httpOptions = {
headers: new HttpHeaders({
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
'Authorization': authToken
})
};
//console.log(authToken);
return this.http.get(this.apiGetUrl, httpOptions).retry(3);
}
Like the previous answer, an Options request automatically gets sent with the GET or POST. If you have apache servers, you can echo$headers = apache_request_headers(); to see what is all coming through. Comparison for $_SERVER and Apache here.
In my case, I run if statements:
if(isset($headers["Authorization"]) && isset($headers["Content-Type"])){
//handle get request
}
else{
//handle options request
echo " False,Re-routing Options Request";
}
I would test your HTTP call in the browser and look at dev tools to confirm the requests being sent. I hope this helps!
Perhaps at some point a preflight OPTIONS request is done by the client and since it isn't a listed method in your Access-Control-Allow-Methods it ends up in a CORS issue.
You should try to make a request to your server endpoint with OPTIONS method to check if this is the case, you can use POSTMAN to make this test.
Then try to add the OPTIONS method to the Access-Control-Allow-Methods and check the difference.
Related
I have a backend developed in netuno with api in java, and I want to send a pdf, I have no errors sending an excel, but for some reason I'm not getting it with pdf, my frontend is with react and redux.
The pdf is working when tested in postman (and yes, I know that in postman there are no cors problems),
In the browser if I send a corrupted pdf it is sent to the frontend but if the pdf has the right data it no longer passes in the cors.
I've already put the settings on the server side to receive the correct headers
_header.response.set('Access-Control-Allow-Origin', '*')
_header.response.set('Access-Control-Allow-Methods', 'GET, PUT, POST, DELETE, OPTIONS')
_header.response.set('Access-Control-Allow-Headers', 'Content-Type, Expires, Cache-Control, must-revalidate, post-check=0, pre-check=0, Pragma')
_header.response.set('Access-Control-Allow-Credentials', 'true')
On the java side the pdf is going with the following headers
HttpServletResponse res = proteu.getServletResponse();
res.setHeader("Expires", "0");
res.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
res.setHeader("Pragma", "public");
res.setContentType("application/pdf");
res.setContentLength(baos.size());
OutputStream out = res.getOutputStream();
baos.writeTo(out);
out.flush();
out.close();
My fetch call is like this right now:
import fetch from "cross-fetch";
const FileSaver = require("file-saver");
const qs = require("qs");
fetch("myEndPoint", {
headers: {
accept: "application/pdf",
"content-type": "application/x-www-form-urlencoded"
},
referrerPolicy: "no-referrer-when-downgrade",
method: "POST",
mode: "no-cors",
body: qs.stringify({
...action.value
})
})
.then(resp => resp.blob())
.then(blob =>
FileSaver.saveAs(
blob,
"myPdf" +
new Date(Date.now())
.toLocaleString()
.replace(new RegExp("/", "g"), "")
.split(",")[0] +
".pdf"
)
);
when I execute the code in the browser, that's the way it is:
fetch("myEndPoint", {
credentials: "omit",
headers: { accept: "application/pdf", "content-type": "application/x-www-form-urlencoded", "sec-fetch-mode": "cors" },
referrer: "http://localhost:3000/foo",
referrerPolicy: "no-referrer-when-downgrade",
body:
"myData=foo",
method: "POST",
mode: "cors"
});
And I get the following error code:
Access to fetch at 'myEndPoint' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
which is strange, considering that all other calls work except this one, and the only difference is the type of document I get.
I have no error on the backend side.
why is chrome editing the mode: "no-cors" for "mode: "cors" ??
if I try to use "sec-fetch-mode: no-cors" in the fetch call header chrome answers:
Refused to set unsafe header "sec-fetch-mode"
The problem was in HttpServletResponse, when you use this on the netuno, it rewrites the header options, so I wasn't sending my previously configured settings, so the correct way to do this is as follows:
BaseService service = new BaseService(proteu, hili);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//my pdf code
service.getHeader().contentTypePDF();
service.getHeader().noCache();
service.getOut().write(baos.toByteArray());
I'm using Reactjs with Laravel5.5 i'm working with League of legends API (Riot API), i want to send a get requests to retrieve some JSON data summoner name, summoner id etc...
i tried to send a normal axios get request inside my app.js like this one:
axios.get('/url_of_api').then(function (response){
console.log(response.data);
});
but i got this error:
ailed to load https://euw1.api.riotgames.com/lol/summoner/v3/summoners/by-name/skt%20ayech0x2?api_key=my_api_key: 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:8000' is therefore not allowed access. The response had HTTP status code 405.
Even i tried to add a custom header requests to my axios
let config = {
headers: {
"Origin": null,
"Accept-Charset": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Riot-Token": "my_api_key",
"Accept-Language": "en-US,en;q=0.5",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0"
}
}
axios.post('url_of_api',config).then(function (response){
console.log(response.data);
});
I'm facing the same problem, but i found a solution with creating a new Laravel middleware and adding this code into it
<?php
namespace App\Http\Middleware;
use Closure;
class Cors {
public function handle($request, Closure $next)
{
return $next($request)
->header('Access-Control-Allow-Origin', '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
}
}
So with this route i get the response
Route::get('/lol', ['middleware' => 'cors', function()
{
$unparsed_json = file_get_contents("https://euw1.api.riotgames.com /lol/summoner/v3/summoners/by-name/skt%20ayech0x2?api_key=my_api_key");
$json_object = json_decode($unparsed_json);
return response()->json($json_object);
}]);
But i want to make it with axios please any suggest? thanks in advance guys.
Riot Games API v3 doesn't support CORS anymore.*
You will need to make those calls from your server.**
I'm trying to get some data with Spotify / Musixmatch API in my Angular 4 app but it is not working. I keep getting this error:
XMLHttpRequest cannot load
http://api.musixmatch.com/ws/1.1/album.get?album_id=14250417&apikey=xyz010xyz.
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:9000' is therefore not allowed
access.
JS
let headers = new Headers();
headers.append('Content-Type','application/json');
headers.append('Accept', 'application/json');
headers.append('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PUT');
headers.append('Access-Control-Allow-Origin', '*');
headers.append('Access-Control-Allow-Headers', "X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding");
let options = new RequestOptions({ headers: headers });
console.log(options)
return this.http.get(this.url + 'album.get?album_id=' + album_id + '&apikey=' + this.apikey, options)
.map(res => res.json())
}
This is the problem with browser, typically its a security concern not to allow other requests which may lead to XSS attack easily.
If only for development I suggest you to install a plugin which will disable in your browser
plugin
If for production, then you need to configure your API.
First, you have to create a file called proxy.conf.json, then put the following code
{
"/ restapiserver / *": {
"target": "http: // localhost: 8075",
"secure": false,
"changeOrigin": true
}
}
In the service file, in the url of the rest service, delete the domain and leave the root of the service
private url: string = '/ restapiserver /';
PS: In AngularJS 4
Try this if not yet problem solved by setting header in js.
Add "no access control allow origin" plugin/add-on in chrome.
Yo can find here:NoAccessControlAllowOriginAddon
I use axios for calling API (in front-end).
I use the method "GET" :
import axios from 'axios';
import querystring from 'querystring';
var url = "mydomain.local",
token = "blablabla...blabla";
var configs = {
headers: {
'Authorization': 'Bearer ' + token,
'Agency': 'demo0'
}
};
var testapi = axios.create({
baseURL: 'http://api.' + url
});
testapi.get( '/relativeUrl', configs
).then(function (response) {
console.log(response);
}).catch(function (error) {
console.log(error);
});
I got a 405 Method Not Allowed. The method is "OPTIONS" but I use the method ".get()".
405 Method Not Allowed. Method OPTIONS
I test call api with postman and I get 200 OK :
postman 200 OK screenshot
Anyone has an idea ?
Like #Shilly says, OPTIONS method is pre-flight on modern browsers when Preflighted requests conditions (MDN) :
In the response header I had Allow:"GET, HEAD, POST, PUT, DELETE".
So OPTIONS method is not available and need to configure it on in the server (Apache).
I do the change on apache (/etc/apache2/sites-available/000-default.conf) :
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "*"
Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
In Request headers I have :
Origin: "null" is a problem. The cause is :
file:// URLs produce a null Origin which can't be authorized via
echo-back. Don't trying to perform a CORS request from a file:// URL (see this post for more details)
After put my javascript file on a apache server, the Origin was not null but I need to add NelmioCorsBundle to my Symfony project to allow preflight
So the way to solve this npm install qs.
Then:
import qs from 'qs'
function send(params) {
return axios.post('/api/create/', qs.stringify(params))
}
I built an API with the SLIM Micro-Framework. I setup some middleware that adds the CORS headers using the following code.
class Cors{
public function __invoke(Request $request, Response $response, $next){
$response = $next($request, $response);
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
}
}
For my front-end, I used VueJS. I setup VueResource and created a function with the following code.
register (context, email, password) {
Vue.http({
url: 'api/auth/register',
method: 'POST',
data: {
email: email,
password: password
}
}).then(response => {
context.success = true
}, response => {
context.response = response.data
context.error = true
})
}
In chrome, the following error is logged to the console.
XMLHttpRequest cannot load http://mysite:9800/api/auth/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://mysite' is therefore not allowed access.
Oddly enough, GET requests work perfectly.
You half 1/2 the solution here.
What you are missing is an OPTIONS route where these headers need to be added as well.
$app->options('/{routes:.+}', function ($request, $response, $args) {
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
});
This happens because preflight request is of OPTIONS type. You need to make an event listener on your request, which checks the type and sends a response with needed headers.
Unfortunately i don't know Slim framework, but here's the working example in Symfony.
First the headers example to be returned:
// Headers allowed to be returned.
const ALLOWED_HEADERS = ['Authorization', 'Origin', 'Content-Type', 'Content-Length', 'Accept'];
And in the request listener, there's a onKernelRequest method that watches all requests that are coming in:
/**
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
// Don't do anything if it's not the master request
if (!$event->isMasterRequest()) {
return;
}
// Catch all pre-request events
if ($event->getRequest()->isMethod('OPTIONS')) {
$router = $this->container->get('router');
$pathInfo = $event->getRequest()->getPathInfo();
$response = new Response();
$response->headers->set('Access-Control-Allow-Origin', $event->getRequest()->headers->get('Origin'));
$response->headers->set('Access-Control-Allow-Methods', $this->getAllowedMethods($router, $pathInfo));
$response->headers->set('Access-Control-Allow-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Expose-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->headers->set('Access-Control-Max-Age', 60 * 60 * 24);
$response->send();
}
}
Here i just reproduce the Origin (all domains are allowed to request the resource, you should probably change it to your domain).
Hope it will give some glues.
Actually CORS is implemented at browser level. and Even with
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
chrome and Mozilla will not set headers to allow cross origin. So, you need forcefully disable that..
Read more about disabling CORS
Disable same origin policy in Chrome
CORS can be hard to config. The key is that you need to set the special headers in your server and your client, and I don't see any Vue headers set, besides as far as I know http is not a function. However here is some setup for a post request.
const data = {
email: email,
password: password
}
const options = {
headers: {
'Access-Control-Expose-Headers': // all of your headers,
'Access-Control-Allow-Origin': '*'
}
}
Vue.http.post('api/auth/register', JSON.stringify(data), options).then(response => {
// success
}, response => {
// error
})
Notice that you need to stringify your data and you need to expose your headers, usually including the Access-Control-Allow-Origin header.
What I did in one of my own apps was to define interceptors so I don't worry to set headers for every request.
Vue.http.headers.common['Access-Control-Expose-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, x-session-token, timeout, Content-Length, location, *'
Vue.http.headers.common['Access-Control-Allow-Origin'] = '*'