how do you make a request from client to server locally using fetch without getting an opaque response? - reactjs

I'm running a react app on localhost:3000, and a go server on localhost:8000.
When I make a request from the client using fetch - the response is opaque so I'm not able to access the data.
How do I make a valid cross-origin request?
client:
componentWillMount() {
const url = 'https://localhost:8000/api/items'
fetch(url, { mode: 'cors' })
.then(results => {
return results.json()
}).then(data => {
let items = data;
this.setState({items})
})
}
server:
func GetItems(w http.ResponseWriter, r *http.Request) {
items := getItems()
w.Header().Set("Access-Control-Allow-Origin", "*")
json.NewEncoder(w).Encode(items)
}
From what I've read - it's expected that requests made across resources should be opaque - but for local development - how do you get access to the JSON?
After looking at the definitions for request types I found this:
cors: Response was received from a valid cross-origin request. Certain
headers and the body may be accessed.
I think I need to set up a valid cross-origin request.
I got it!
This question helped resolve how to set up CORS in golang: Enable CORS in Golang
3 key things here:
Set the mode in the client request to cors
Set the Access-Control-Allow-Origin header on the server to *
Call .json() on the result in the client, and in a
following promise you can access the data.

w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Add("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("content-type", "application/json")
You can try to add them in the handleFunc

Related

Proxy doesn't work with fetch() in React.js

I've made a simple project in React; the client is running at port 3000, server at 3001.
If I launch localhost:3001/api/visitator/cars it works correctly, but when I make the GET Request on Client I have this error, on console http://localhost:3000/api/visitator/cars 404 (Not found).
I don't know why, but the request is done on port 3000 and not 3001, even if on package.json is present
"proxy": "http://localhost:3001".
This is the code in client/api:
async function askForCars(){
let url = '/api/visitator/cars'
const response = await fetch(url);
const carJson = await response.json();
if(response.ok){
console.log(carJson)
return carJson;
} else {
let err = {status: response.status, errObj:carJson};
throw err; // An object with the error coming from the server
}
}
There are two ways to solve this:
You have give the full path rather than relative path as your server lies on a different domain as ports are different. So your url variable value should be the domain name + uri + i.e. http://localhost:3001/api/visitator/cars.
The second way to solve this would be you need to add redirect rules on the server where you are hosting the app so that your every request having http://localhost:3000/api uri should be redirected to http://localhost:3001/api.
I think the quick solution would be the first one for now incase you don't have requirement to redirect api calls to actual server. Hope it helps.

Uncaught (in promise) Error: Request failed with code 405 POST AXIOS

I am trying to post data in my database but every time I do try to dod it I get a 405 error. Also python has an error saying that I am submitting an empty list. Please point me in the right direction to solve this problem.
const axios = require('axios')
let URL = 'http://127.0.0.1:5000/Walls/saveComments'
let HEADERS = { 'Content-Type': 'application/json' }
let data = {
'post': post,
'time': time
}
axios.post(URL,data, HEADERS)
.then(function (response) {
console.log(response);
})
// Axios Call to Save A Post in Backend
The HTTP 405 error means that the server does not allow the HTTP request method that the client sent.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
The HTTP method you're using in the code example you shared is POST. Therefore, it seems that your server does not accept POST requests.
In order to fix this, either change the request method to something that is supported, or change the server to allow POST requests.

CORS AWS using Lambda (JAVA)

I am using AWS API Gateway with a Java Lambda Backend.
Everything is peachy until a friend using Angular 4 is trying to make requests. He keeps getting:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at URL (Reason: CORS header
'Access-Control-Allow-Origin' missing).
I have enabled CORS via the gateway:
Despite this the error remains. What should I modify ?
Thanks.
Ian's Comments:
I use Output/Input Streams so my output, as per your comment I am trying as below but still no success. Any ideas ?
private void sendResponse(JSONObject body, int statusCode, OutputStream outputStream)
{
OutputStreamWriter writer;
JSONObject responseJson = new JSONObject();
JSONObject responseHeadersJson = new JSONObject();
responseHeadersJson.put("Access-Control-Allow-Origin","*");
responseHeadersJson.put("Access-Control-Allow-Headers","Content-Type");
responseJson.put("headers",responseHeadersJson);
responseJson.put("statusCode", statusCode);
responseJson.put("body", body.toJSONString());
try {
writer = new OutputStreamWriter(outputStream, "UTF-8");
writer.write(responseJson.toJSONString());
writer.close();
} catch (IOException e) {
System.out.println("Outputstream Error "+e);
}}
I can see that you are using Proxy Resource.
That means you are controlling the response thats going back as well from your Lambda. CORS needs to be configured on the response as well by adding the origin header.
When you build the response you need to add the cors headers by passing the domain or *.
I have built a ResponseBuilder that you can use as an example:
https://github.com/ahpoi/commons-utils-sdk/blob/master/src/main/java/com/ahpoi/commons/utils/aws/lambda/model/proxy/response/ResponseBuilder.java
public ResponseBuilder originHeader(String domain) {
headers.put(ACCESS_CONTROL_ALLOW_ORIGIN, domain);
return this;
}
private void initDefaultHeaders() {
headers.put(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type");
}
public Response build() {
this.initDefaultHeaders();
return new Response(statusCode, headers, body);
}
If you didn't use Proxy Resource, your configuration would have been enough.

Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter..?

I am trying to send a PUT request to an amazonS3 presigned URL. My request seems to be called twice even if I only have one PUT request. The first request returns 200 OK, the second one returns 400 Bad Request.
Here is my code:
var req = {
method: 'PUT',
url: presignedUrl,
headers: {
'Content-Type': 'text/csv'
},
data: <some file in base64 format>
};
$http(req).success(function(result) {
console.log('SUCCESS!');
}).error(function(error) {
console.log('FAILED!', error);
});
The 400 Bad Request error in more detail:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>InvalidArgument</Code>
<Message>Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified</Message>
<ArgumentName>Authorization</ArgumentName>
<ArgumentValue>Bearer someToken</ArgumentValue>
<RequestId>someRequestId</RequestId>
<HostId>someHostId</HostId>
</Error>
What I don't understand is, why is it returning 400? and What's the workaround?
Your client is probably sending an initial request that uses an Authorization header, which is being responded with a 302. The response includes a Location header which has a Signature parameter. The problem is that the headers from the initial request are being copied into the subsequent redirect request, such that it contains both Authorization and Signature. If you remove the Authorization from the subsequent request you should be good.
This happened to me, but in a Java / HttpClient environment. I can provide details of the solution in Java, but unfortunately not for AngularJS.
For the Googlers, if you're sending a signed (signature v4) S3 request via Cloudfront and "Restrict Bucket Access" is set to "Yes" in your Cloudfront Origin settings, Cloudfront will add the Authorization header to your request and you'll get this error. Since you've already signed your request, though, you should be able to turn this setting off and not sacrifice any security.
I know this may be too late to answer, but like #mlohbihler said, the cause of this error for me was the Authorization header being sent by the http interceptor I had setup in Angular.
Essentially, I had not properly filtered out the AWS S3 domain so as to avoid it automatically getting the JWT authorization header.
Also, the 400 "invalid argument" may surface as a result of wrong config/credentials for your S3::Presigner that is presigning the url to begin with. Once you get past the 400, you may encounter a 501 "not implemented" response like I did. Was able to solve it by specifying a Content-Length header (specified here as a required header). Hopefully that helps #arjuncc, it solved my postman issue when testing s3 image uploads with a presigned url.
The message says that ONLY ONE authentication allowed. It could be that You are sending one in URL as auth parameters, another - in headers as Authorization header.
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:scavenger_inc_flutter/utils/AuthUtils.dart';
import 'package:scavenger_inc_flutter/utils/URLS.dart';
class ApiClient {
static Dio dio;
static Dio getClient() {
if (dio == null) {
dio = new Dio();
dio.httpClientAdapter = new CustomHttpAdapter();
}
return dio;
}
}
class CustomHttpAdapter extends HttpClientAdapter {
DefaultHttpClientAdapter _adapter = DefaultHttpClientAdapter();
#override
void close({bool force = false}) {
_adapter.close(force: force);
}
#override
Future<ResponseBody> fetch(RequestOptions options,
Stream<List<int>> requestStream, Future<dynamic> cancelFuture) async {
String url = options.uri.toString();
if (url.contains(URLS.IP_ADDRESS) && await AuthUtils.isLoggedIn()) {
options.followRedirects = false;
options.headers.addAll({"Authorization": await AuthUtils.getJwtToken()});
}
final response = await _adapter.fetch(options, requestStream, cancelFuture);
if (response.statusCode == 302 || response.statusCode == 307) {
String redirect = (response.headers["location"][0]);
if(!redirect.contains(URLS.IP_ADDRESS)) {
options.path = redirect;
options.headers.clear();
}
return await fetch(options, requestStream, cancelFuture);
}
return response;
}
}
I disallowed following redirects.
Used the response object to check if it was redirected.
If it was 302, or 307, (HTTP Redirect Codes), I resent the request after clearing the Auth Headers.
I used an additioal check to send the headers only if the path contained my specific domain URL (or IP Address in this example).
All of the above, using a CustomHttpAdapter in Dio. Can also be used for images, by changing the ResponseType to bytes.
Let me know if this helps you!
I was using django restframework. I applied Token authentication in REST API. I use to pass token in request header (used ModHeader extension of Browser which automatically put Token in Authorization of request header) of django API till here every thing was fine.
But while making a click on Images/Files (which now shows the s3 URL). The Authorization automatically get passed. Thus the issue.
Link look similar to this.
https://.s3.amazonaws.com/media//small_image.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXXXXXXXXXXXXXXXXX%2F20210317%2Fap-south-XXXXXXXXFaws4_request&X-Amz-Date=XXXXXXXXXXXXXXX&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
I lock the ModHeader extension to pass Authorization Token only while making rest to REST API and not while making resquest to S3 resources. i.e. do not pass any other Authorization while making request to S3 resource.
It's a silly mistake. But in case it helps.
Flutter: if you experience this with the http dart package, then upgrade to Flutter v2.10!
Related bugs in dart issue tracker:
https://github.com/dart-lang/sdk/issues/47246
https://github.com/dart-lang/sdk/issues/45410
--> these has been fixed in dart 2.16, which has been shipped with Flutter v2.10!
https://medium.com/dartlang/dart-2-16-improved-tooling-and-platform-handling-dd87abd6bad1

How to avoid preflight OPTIONS request with node request package for CORS?

I simply wish to post some json but by default request does a preflight OPTIONS request.
I would like to avoid this as users often have unreliable connections, an extra request further reduces reliability and results in cryptic error messages like 'CORS rejected'.
var request = require('request');
function (data, cb) {
if (!cb) cb = function () {};
request({
method: "POST",
url: "someurl",
json:true,
body: data
}, function (err, response, body) {
if (err) cb(err);
else if (response.statusCode != 200) {
cb(new Error("log satus code: " + response.statusCode));
} else {
cb(null, body);
}
})
To clarify I am doing an actual CORS and wish to avoid the preflight OPTIONS request. I also have control over the serve (though that shouldn't matter).
The prefight OPTIONS request is a required part of the CORS flow. There is no way around it. However, the client can cache the preflight response so it only needs to actually make the preflight request once instead of every time it POSTs.
To enable preflight request caching, the preflight request must respond with the Access-Control-Max-Age header. The value of this header is the number of seconds the client is allowed to cache the response.
For example, the following response header will allow the client to cache the preflight response for 5 minutes.
Access-Control-Max-Age: 300
You will have to choose a value that is appropriate for your application. It is usually a good idea to set this value to something that isn't too large in case you need to change the preflight response in the future. If you allow the preflight request to be cached for a month, users might not get your changes until their cache expires a month later.
Simple requests don't need a preflight request. I'm guessing the json: true sets a custom Content-Type header (most likely application/json).
The simple values for Content-Type are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Anything outside of those values will trigger a preflight request.

Resources