I am setting up admin on rest, and now I am getting this error when I try to fetch data, even though I receive all the data needed from the server:
The Content-Range header is missing in the HTTP Response. The simple REST client expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare Content-Range in the Access-Control-Expose-Headers header?
Is there a way to solve it without making changes to the API? I was doing authorization based on the tutorial, here is my app.js:
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const token = localStorage.getItem('token');
options.headers.set('Authorization', `Bearer ${"token"}`);
return fetchUtils.fetchJson(url, options);
}
const restClient = simpleRestClient('https://mywebsite.com', httpClient);
const App = () => (
<Admin restClient={restClient} authClient={authClient}>
<Resource name="posts" list={PostList} edit={PostEdit} create={PostCreate}/>
<Resource name="users" list={UserList}/>
</Admin>
);
The issue is not on the React-App but rather your REST server.
In my case, I was using the SimpleRestClient and in their documentation it reads
import simpleRestProvider from 'ra-data-simple-rest';
Note: The simple REST client expects the API to include a
Content-Range header in the response to GET_LIST calls. The value must
be the total number of resources in the collection. This allows
admin-on-rest to know how many pages of resources there are in total,
and build the pagination controls.
Content-Range: posts 0-24/319 If your API is on another domain as the
JS code, you’ll need to whitelist this header with an
Access-Control-Expose-Headers CORS header.
Access-Control-Expose-Headers: Content-Range
So, from your server/the REST server it has to return(include in response) two headers
Access-Control-Expose-Headers: Content-Range
Content-Range: posts 0-24/319
In my flask-server here's what i did
Add the 'content-range' header in your responses.
response.headers.add( 'Access-Control-Expose-Headers', 'Content-Range')
Add the header 'Content-Range' and assign it a range value(usually in bytes)
response.headers.add('Content-Range','bytes : 0-9/*')
Finally: I noticed that when either of the headers is omitted from your response you'd get the same error
Error: The Content-Range header is missing in the HTTP Response
Ensure your server returns these headers
'Access-Control-Expose-Headers', 'Content-Range'
or
'Content-Range','bytes : 0-9/*'
I hope this helps as it's my ever first response to a SO question
If you are using fastapi with react admin you need to add this to route
response.headers['X-Total-Count'] = '30'
response.headers['Access-Control-Expose-Headers'] = 'Content-Range'
``
You need to add custom headers to your requests, you can just wrap the fetchJson() call inside your own function:
For instance:
import { fetchUtils, Admin, Resource } from 'react-admin';
import simpleRestProvider from 'ra-data-simple-rest';
const fetchJson = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
// add your own headers here
options.headers.set('X-Custom-Header', 'foobar');
return fetchUtils.fetchJson(url, options);
}
const dataProvider = simpleRestProvider('http://path.to.my.api/', fetchJson);
const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="posts" list={PostList} />
</Admin>
);
For the most common usage of custom headers is for authentication. fetchJson has built-on support for the Authorization token header:
const fetchJson = (url, options = {}) => {
options.user = {
authenticated: true,
token: 'SRTRDFVESGNJYTUKTYTHRG'
};
return fetchUtils.fetchJson(url, options);
};
const dataProvider = simpleRestProvider('http://path.to.my.api/', fetchJson);
Now all the requests to the REST API will contain the Authorization: SRTRDFVESGNJYTUKTYTHRG header.
Related
So I am trying to fetch data from an API but it keeps showing a CORS error no matter what I try...
It is important to know that It is an external API and I don't have access to the server code. Only to my front-end code.
My code is the following:
`
const [contaminacion, setcontaminacion] = useState([]);
const obtenerDatos = async (url) => {
const datan = await fetch(url, {
mode: "cors",
method:"GET",
headers: {
"Access-Control-Allow-Origin": "*",
"cache-control": "no-cache",
},
});
console.log(datan);
const dataParsed = await datan.json();
setcontaminacion(dataParsed.results);
};
useEffect(() => {
obtenerDatos(
"https://opendata.aemet.es/opendata/api/red/especial/contaminacionfondo/estacion/07"
);
}, []);
`
I read in an old post (more than 5 years old) that I could use a proxy with Heroku, but in the comments they say that Heroku doesn't serve to this purpose anymore.
I have tried to set a proxy in my vite.config.js folder but it is not working for me and I don't know if I am doing it properly or not.
That's what I wrote:
`
import { defineConfig } from 'vite'
import react from '#vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
server:{
proxy:{
'/api': {
target: 'https://opendata.aemet.es/opendata/api/red/especial/contaminacionfondo/estacion/07',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
plugins: [react()]
})
`
What am I missing here? Any help would be appreciated.
You cannot use https://opendata.aemet.es/centrodedescargas/inicio APIs without an API key.
You will have to register on the site, get an API key and using that API make the request. When registering for the API key, you will be asked to provide the domain of your application. This domain will be returned in the Access-Control-Allow-Origin header and your client will not get the CORS error anymore.
Error message: No Firebase ID token was passed as a Bearer token in the Authorization header. Make sure you authorize your request by providing the following HTTP header: Authorizaiton: Bearer or by passing a "__session" cookie
There actually is a valid token. This same setup works in other functions but not here. The main difference is that this is a delete instead of a post.
firebase
.auth()
.currentUser.getIdToken(true)
.then((token) => {
console.log(token)
return axios.delete(
`${FunctionsDir}/deleteMessage`,
{
messageID: messageID,
},
{
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/octet-stream',
},
}
)
})
and the cors setup...
var corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
},
allowedHeaders: 'Content-Type,Authorization',
methods: 'GET,POST,DELETE',
preflightContinue: false,
optionsSuccessStatus: 200,
}
app.options('*', cors())
app.use(cors(corsOptions))
Update: Fixed, turned out to be the cors thing plus the axios.delete() sig was wrong.
This is likely a CORS issue that needs to be solved on the server.
This is because DELETE requests need to make a pre-flight request to the server asking what is acceptable to send. Because you got a response from your server, it is likely that you have already added DELETE to your Access-Control-Allow-Methods header. However, Authorization isn't considered a "safe header" by default. So you must also explicitly allow it, otherwise browsers will remove it from the request as they send it off. You can do this by adding Authorization to your Access-Control-Allow-Headers header.
If you are using Express on your server, you can allow the cors package to do this for you:
import express from "express";
import cors from "cors";
const app = express();
app.options("/deleteMessage", cors()); // enable preflight request for DELETE
app.use(cors({ origin: ["https://yourapp.com"] })); // enable CORS for all routes
// ...
If you don't like making use of third-party dependencies, you can manually add the headers as shown in this answer.
For non-express requests, like a HTTP Request Cloud Function, you can use the following code:
import express from "express";
import cors from "cors";
const corsMiddleware = cors({ origin: ["https://yourapp.com"] }); // or your manual (req, res, next) function
export const someFunction = functions.https.onRequest((req, res) => {
corsMiddleware(req, res, (err) => {
if (err) {
res.status(500).send('CORS check failed');
return;
}
// your code here
}
})
I have set up my CORS policy using Django-cors-headers with the following settings:
APPEND_SLASH=False
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'localhost:8000',
'localhost:3000',
'localhost'
)
I have also added it to installed_apps and middleware.
Now I am making a React app for the front end and using AXIOS for my API requests. When I make an API request to log in to my app the CORS policy allows it. But, if I make an API request that requires a Token, I get:
Access to XMLHttpRequest at 'localhost:8000/api/TestConnection/' from origin 'http://localhost:3000' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
It seems that I need to allow XMLHttpRequest for supported protocol schemes but I cannot find anything in the pypi documentation about this.
EDIT:
Here is the AXIOS Request:
axios.post("localhost:8000/api/TestConnection/",
{headers:
{
'Authorization': "Bearer " + localStorage.getItem('JWTAccess')
}
},
{
testString: 'Hello API'
})
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error);
})
Thank you!
I Have had a similar issue with a ReactNative app which was happening due to ReactNative using IP 10.0.2.2 for localhost (I do not remember the details or why). I solved it by adding to my class.
componentWillMount() {
axios.defaults.baseURL = 'http://10.0.2.2:8000/api/';
axios.defaults.timeout = 1500;
}
I do not know if this is the right IP but may be worth looking at.
EDIT
handleRequest() {
const payload = { username: this.state.username, password: this.state.password }
axios
.post('login/', payload)
.then(response => {
const { token, user } = response.data;
// We set the returned token as the default authorization header
axios.defaults.headers.common.Authorization = `Token ${token}`;
// Navigate to the home screen
Actions.main();
})
.catch(error => {
console.log(error)
});
}
By saving the Token within my headers it is always sent.
The error says "from origin 'http://localhost:3000'" and to "check the cors policy"
I see your CORS policy is
CORS_ORIGIN_WHITELIST = (
'localhost:8000',
'localhost:3000',
'localhost'
)
maybe try providing the full http url. so
CORS_ORIGIN_WHITELIST = (
'localhost:8000',
'http://localhost:3000',
'localhost'
)
I solved it! The solution was very simple(of course),
For the request I needed to use part of #HenryM 's solution.
First I needed to establish the default url:
axios.defaults.baseURL = 'http://127.0.0.1:8000/api/';
Then I save the payload and header to const variables:
const header = {
headers:{
'Authorization': "Bearer " + localStorage.getItem('JWTAccess')
}
}
const payload = {
testValue: "Hello API"
}
Finally, the main issue was that my parameters were in the wrong order:
axios.post("TestConnection/", payload, header)
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error);
Apparently the propper order, at least when using Django Rest Framework, is payload then header!!!
Thank you to everyone who tired to help!
This was the article that ended up helping me: https://www.techiediaries.com/django-vuejs-api-views/
I'm deploying and angular 6 application that works with a tomcat server in localhost, when I try to execure this http request
this.http
.post<LoginResult>( API_URL + '/login', JSON.stringify(json)/*, { headers: myHeader }*/).pipe(
catchError(this.handleError('get-token', []))).subscribe((response) => {
if(response['result_code'] == 'result_ok') {
this.auth.doSignIn(response['token'], response['name']);
this.router.navigate(['user_manager']);
return true;
}else {
return false;
}
});
everitying works well, but when I add header field
let myHeader = new HttpHeaders().append("Authorization", 'Basic' + this.session.getAccessToken());
this.http
.post<LoginResult>( API_URL + '/login', JSON.stringify(json), { headers: myHeader }).pipe(
catchError(this.handleError('get-token', []))).subscribe((response) => {
if(response['result_code'] == 'result_ok') {
this.auth.doSignIn(response['token'], response['name']);
this.router.navigate(['user_manager']);
return true;
}else {
return false;
}
});
this is my output error:
Access to XMLHttpRequest at 'http://localhost:8095/login' from origin 'http://localhost:4200' 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.
HttpErrorResponse
I checked also that the request doesn't arrive to my tomcat server, it is blocked before, that oesn't allow angular to check response headers
Thank you for your help
I'm providing you a generic answer as you have not mention that your server side code is written in which language. You should provide a header from your server code. Provide Access-Control-Allow-Origin header with value as localhost:4200 which will resolve your issue. Or if you want to allow every origin then change its value from localhost:4200 to *.
After reading all the comments I have change something for you.
change your this code let myHeader = new HttpHeaders().append("Authorization", 'Basic' + this.session.getAccessToken()); with const myHeader = new HttpHeaders({'Authorization': 'Bearer ' + localStorage.getItem('api_token')});
and make your post request as
this.http
.post<LoginResult>( API_URL + '/login', json, myHeader).pipe(
catchError(this.handleError('get-token', []))).subscribe((response) => {
if(response['result_code'] == 'result_ok') {
this.auth.doSignIn(response['token'], response['name']);
this.router.navigate(['user_manager']);
return true;
}else {
return false;
}
});
You need to configure CORS on your tomcat server.
You need to tell tomcat which headers the application is allowed to send, so it can include it in the preflight response:
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>Authorization,Content-Type,...</param-value>
</init-param>
Take a look at
cors.allowed.methods under CORS Filter section here:
https://tomcat.apache.org/tomcat-7.0-doc/config/filter.html
I'm trying to send a post request from my Ionic 3 (Angular 5) app to my REST api, but I'm getting HTTP 404 (Not found) or HTTP 400 (Bad request).
When I send the post request using Postman it is successful. And also, my GET request in Ionic 3 app works successfully. You can see success request below, it hasn't an Authorization:
Here is my request method:
sendConfirmationCode() {
let mybody = new FormData();
mybody.append('msisdn', '1234567');
let myheaders = new HttpHeaders({
'Content-Type': 'application/json'
});
this.http.post('http://mydomain/methodname', mybody, {headers: myheaders})
.subscribe(data => {
console.log(JSON.stringify(data));
}, error => {
console.log(JSON.stringify(error));
})
}
With headers I get HTTP 404 (Not found) but without HTTP 400 (Bad request). So, I tried different body objects with and without using headers. Here is my usings instead of FormData body object:
let mybody= new HttpParams();
mybody.append('msisdn', '1234567');
-----
let mybody= new URLSearchParams()
mybody.append('msisdn', '1234567');
-----
//SubscriberDataInput is my class for to use as input body model of api's method
let mybody = new SubscriberDataInput();
mybody.msisdn = '1234567';
-----
let mybody = JSON.stringify({ "msisdn": "1234567" });
And tried these cases for sending header instead of above header:
let Options = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
-----
let headers = { 'Content-Type': 'application/json' }
None of them works successfully. Can you please tell the right way?
I found the solution. The problem was my RESTful api prevents ajax post requests. This is a solution in Asp.Net WebApi 2 which related Cors:
Add a constant into Startup class of Startup.cs :
private const string DefaultCorsPolicyName = "localhost";
Add Cors into ConfigureServices method of Startup class:
services.AddCors(options =>
{
options.AddPolicy(DefaultCorsPolicyName, builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Enable Cors in Configure method of Startup class:
app.UseCors(DefaultCorsPolicyName); //Enable CORS!
Delete first custom header in web.config:
<add name="Access-Control-Allow-Origin" value="*"/>