Pretty noobish question here, I do not wish to draw flak.
I have my frontend created with create-react-app and I'm using fetch to pass in the backend APIs to my frontend. The backend is running on localhost:8080 on the same machine as the frontend. Frontend is running on port 3000. I have hardcoded the URLs as "http://localhost:8080/getForm" and so on. It all works fine if I access the frontend on the same machine as it is hosted. However, if I access the frontend from a different machine, the API calls fail, which would make sense because the calls are being made to localhost.
Now, what would be the best approach to pass in machine-independent rest URLs? I do not want to set a static IP for my backend. I have tried:
Making a production build and bundling it with the backend. This again makes calls to localhost on the accessing machine, which fails.
Manipulating the URL with window.location.hostname+"getForm". This fails when I have different servers hosting frontend and backend.
Edit*
Okay, I managed to add a proxy to the node server by adding the following line to package.json.
"proxy":"http://localhost:8080/"
This forwards something like localhost:3000/api/getForm on the frontend to localhost:8080/api/getForm on the backend. This works pretty well, but now I am stuck on an issue which I presume is due to incorrect CORS setting. Proxied GET requests to the backend, which is a Spring Boot API, work fine, but proxied POST requests return a 403, with the response "Invalid CORS request".
I have added a #CrossOrigin(origins="http://localhost:3000") to the class-level of my spring application which should make all the apis CORS friendly. Also, I am using fetch on the frontend to make calls. Any leads on what I might be getting wrong?
POST /api/post HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 22
Origin: http://localhost:3000
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Content-Type: application/json
Accept: /
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-IN,en-GB;q=0.9,en-US;q=0.8,en;q=0.7
Body - {"title":"abc","body":"def"}
Response - 403, Invalid CORS request
GET /api/get HTTP/1.1
Host: localhost:3000
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36
Accept: /
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-IN,en-GB;q=0.9,en-US;q=0.8,en;q=0.7
Response - 200, [{"id":1,"title":"Post 1","body":"Backend is connected fine and dandy!"}]
When you dont serve your code using node, you can use webpack to add globals, but then you need to create separate build for each environment.
When using Node with SSR:
What I usually do is use a .env file with specific constants for hostname, port, .. and use those in a js config file. Then you can import that js file and use it for your fetch calls. You can use dotenv (https://www.npmjs.com/package/dotenv) for adding the .env variables to your node process.
config file:
const config = {
env: {
host: (envConfig && envConfig.API_HOST) || 'localhost',
port: (envConfig && envConfig.API_PORT) || '8000',
httpOrigin: (envConfig && envConfig.API_PROTOCOL) || 'http'
}
}
export default config;
.env file:
API_PROTOCOL=https
API_HOST=localhost
API_PORT=8000
To transfer the config to the frontend you can seriablize in the the body of your html and pick it up on your client side render.
<script dangerouslySetInnerHTML={{ __html: `window.__envConfig=${serialize(envConfig)};` }} charSet="UTF-8"/>
Related
Ill try to keep this short to save digital rain forest. Please ask If I missed any details.
I have an "asp .net 3.1 core + react"-project template in VS, with built in Identity server. This works ok, but I now want to do my react project in a separate project. So I started a new create-react-app-project.
So, from my new react project, when I call OidcConfigurationController. The controller method is called and I can step through the code on server side. Then I get a client error "Failed to fetch", which, by internet wizdom, seems to indicated CORS-error.
This is what I got when I inspect the header in chrome dev toolbar->network
Request URL: https://localhost:5001/authentication/_configuration/MyProject.Web
Referrer Policy: strict-origin-when-cross-origin
:authority: localhost:5001
:method: GET
:path: /authentication/_configuration/MyProject.Web
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9,sv;q=0.8
origin: http://localhost:3000
referer: http://localhost:3000/
sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
sec-ch-ua-mobile: ?0
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
These are relevant lines in startup.cs
ConfigureServices()
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
//builder.WithOrigins("http://localhost:3002/", "https://localhost:3001")
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.AddSingleton<ICorsPolicyService>((container) => {
var logger = container.GetRequiredService<ILogger<DefaultCorsPolicyService>>();
return new DefaultCorsPolicyService(logger)
{
AllowAll = true
};
});
Configure()
app.UseCors(MyAllowSpecificOrigins); // I also tried to switch order on these 2 rows
app.UseIdentityServer();
Nothing I do here seems to change the Referrer Policy in the header, still get the exact same message
The React-call is just a plain fetch(address-of-the-controller-that-it-hits).
I have also tried to start a new Server Side-project (asp net core api) and set same CORS-policy, I can call this api from my react client without getting any errors)
So, in the request, you see the origin: http://localhost:3000 header is used. That is the source for the CORS request. But the request is for this URL:
https://localhost:5001/authentication/_configuration/MyProject.Web
Could it not be that there's a redirect from insecure HTTP to HTTPS that is interfering?
Do make sure you set the CORS settings in IdentityServer as well.
See the CORS documentation for more details.
As side note, IIS might cause CORS issues as well, see this answer for details:
IIS hijacks CORS Preflight OPTIONS request
I have been trying to use the following code using React fetch to get a response from the OpenWeather API:
fetch('api.openweathermap.org/data/2.5/weather?q=Milwaukee&APPID=1234567890')
.then(response => response.text())
.then(data => {
console.log(data)
When I skip react and just copy the url (api.openweathermap.org/data/2.5/weather?q=Milwaukee&APPID=1234567890) into the browser I get a totally valid response. (For example ) When I use the fetch code in my React application, however, I get the following error:
Request URL: http://localhost:3000/api.openweathermap.org/data/2.5/weather?q=Milwaukee&APPID=1234567890
Request Method: GET
Status Code: 431 Request Header Fields Too Large
Remote Address: 127.0.0.1:3000
Referrer Policy: no-referrer-when-downgrade
I am running my react application off localhost from create-react-app. Why can I access that API just fine from my browser but get an error in my app?
Extra Information
In case it is useful here is the link to sign up for a free OpenWeather API
Here is the rest of the information from the response header:
HTTP/1.1 431 Request Header Fields Too Large
x-powered-by: Express
connection: close
date: Wed, 18 Sep 2019 15:45:21 GMT
transfer-encoding: chunked
Or from the request header:
GET /api.openweathermap.org/data/2.5/weather?q=Milwaukee&APPID=1234567890 HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Sec-Fetch-Mode: cors
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36
Accept: */*
Sec-Fetch-Site: same-origin
Referer: http://localhost:3000/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,es-US;q=0.8,es;q=0.7,ar-JO;q=0.6,ar;q=0.5
(My APPID is fake in all these examples, so the request won't work by just copying and pasting what I have)
It's a pretty "catchy" bug - you are missing http:// in front of your url, so:
fetch('https://api.openweathermap.org/data/2.5/weather?q=Milwaukee&APPID=1234567890')
Since you've missed it, url is resolved to this (you can see it in your Network tab):
http://localhost:3000/api.openweathermap.org/data/2.5/weather?q=Milwaukee&APPID=1234567890
I have a Google AppEngine project that works fine in production but not locally.
There is a React browser application running locally on port 3001 and a python api service running on 9090.
When I attempt to upload files via the React client, I first call an REST endpoint that returns the blobstore get_upload_url() to the client. This url is something like: http://localhost:9090/_ah/upload/aghkZXZ-... <-- note the port is that of the python service
When I fashion a POST request to that url from the browser client to actually upload the file, I get a 405 on the OPTIONS preflight check. So far as I understand, this is due to the ports being different. This only occurs in the local App Engine SDK since I am using dispatch.yaml settings in production to have everything on the same domain/port.
I had dug into the SDK code a while ago and put a hack in place. (https://gist.github.com/blainegarrett/4d3b3081d09b4ff7be00765eb32b0d94)
However, since upgrading Google Cloud to 218.0.0, the hack was overwritten and I'm back to square one.
Here are the headers to the blobstore upload url:
OPTIONS /_ah/upload/aghkZXZ-Tm9uZXIiCxIVX19CbG9iVXBsb2FkU2Vzc2lvbl9fGICAgICA77ALDA HTTP/1.1
Host: localhost:9090
Connection: keep-alive
Origin: http://localhost:3001
Access-Control-Request-Method: POST
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
I am currently using vanilla XMLHttpRequest() for the upload call specifically.
Does anyone have any suggestion on how to either get around the preflight check when the ports are different and/or to allow OPTIONS checks on the upload url in a less hacky way?
Update: I'd still like to hear an answer regarding the 405 on the SDK, but I was able to dodge the preflight check by getting rid of the xhr progress listener. My original assertion that the port difference was triggering the preflight check was incorrect. It was the progress callback.
xhr.upload.addEventListener('progress', function(e) { .. }
See research on: CORS request is preflighted, but it seems like it should not be
I'm writing the client-side of an app with ReactJS, I'm stuck at getting data from a certain api. I get "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access" error.
As I suppose the server is missing the "Access-Control-Allow-Origin" headers in order to enable CORS but what I don't understand is why when I'm trying to make an AJAX call with "Restlet Client - REST API testing" browser extension it actually works. I get a following success response. But It doesn't work inside my actual React Request. Is there a way I can to get rid of this error without any changes on the server side? Why does this extension actually work?
Connection: keep-alive
Content-Type: application/json; charset=UTF-8;
Date:
2017 Sep 22 22:45:40-1m 50s
Server: ..../1.6.2
Transfer-Encoding: chunked
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
My React code but nothing special in there, just testing if I'm getting a response
componentWillMount() {
const url = "https://myserver.ru/api/issue?perPage=10";
Request.get(url).then((response) => {
this.setState({
issues: response
});
});
}
If you are making a cross-site requests then browser will expect Access-Control-Allow-Origin headers from the server. The value of that header can be,
Access-Control-Allow-Origin: *
or
Access-Control-Allow-Origin: http://localhost:3000
When you are requesting from localhost, make sure it also need to add port number as well.
More about CORS documentation:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Hope it helps.
I'm in the process of building an internal application where I am using Angular 2 (CLI/Webpack) to call a CORS enabled service that I built using .NET Core. The service uses the user's Integrated Windows Authentication credentials to look up information about that user and return it to Angular. Everything works fine in Chrome and Firefox, but in IE 10 and 11, I receive a "401 Unauthorized" response with the message Origin http://localhost:4200 not found in Access-Control-Allow-Origin header.
In Angular, I'm making an HTTP call like so:
private options = new RequestOptions({withCredentials: true});
let getURL = `server:port/api/users/username`;
return this.http
.get(getURL, this.options)
.map((response: Response) => response.json()[0])
.catch(this.handleError);
and my .NET Core service uses the following code in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("policyAnyone",
builder => {
builder.AllowCredentials()
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("policyAnyone");
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseMvc();
}
The controller then uses the username it receives via User.FindFirst(ClaimTypes.Name).Value, runs a stored procedure, and returns the results.
For reference, Chrome has the following request headers:
Accept: application/json, text/plain, `*/*`
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Authorization: Negotiate %lotsOfEncodedText%
Cache-Control: max-age=0
Connection: keep-alive
content-type: text/plain
Host: server:port
Origin: http://localhost:4200
Referer: http://localhost:4200/dashboard
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
and IE has these:
Request: GET /api/users/username HTTP/1.1
Accept: application/json, text/plain, `*/*`
Content-Type: text/plain
Referer: http://localhost:4200/dashboard
Accept-Language: en-US
Origin: http://localhost:4200
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: server:port
Connection: Keep-Alive
Cache-Control: no-cache
It seems as though CORS is configured correctly, and my Angular setup is pretty simple, but IE doesn't even display the credentials box.
In case anyone runs across this, I think I was able to solve the problem. My issue is with my PC's Internet Options where the Logon Authentication option (Internet Options > Security > Custom level... > User Authentication > Logon) was set to "Automatic logon only in Intranet zone". While this is intended, IE wasn't seeing my site as an intranet site, but rather an internet site. The reason for this is the "dot rule" that IE uses.
"If the URI’s hostname doesn’t contain any periods (e.g. http://team/) then it is mapped to the Local Intranet Zone." [1]
Our local site's hostname contains periods and is therefore mapped to the Internet Zone. After we are able to cut through the red tape, manually adding my site to the Local Intranet Zone (Internet Options > Security > Local Intranet > Sites > Advanced) should fix my issue.
I'm not sure why IE decided to respond with the CORS ACAO error message that it did, but the 401: Unauthorized response was at least enough to tip me off.
[1] Info on the Internet Option: https://support.microsoft.com/en-us/help/258063/internet-explorer-may-prompt-you-for-a-password
[2] Info on the Intranet Zone determination: https://blogs.msdn.microsoft.com/ieinternals/2012/06/05/the-intranet-zone/
IE treats ports differently than other browsers. Other browsers say that if the port is different, then it is not the same "origin". In IE, if the ports are different but the domain is the same, then it is same origin and the headers are not used.
You can read more here : https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#IE_Exceptions
However you should still see the returned data via Angular (Since IE would just treat them as same origin). So are you seeing response data at all from your ajax call?