I'm doing a basic request to a backend in JS (only to check a user instance exists and will return a bool (true/false) to prevent returning full user data until needed)
From what I have been told by peers it's ill-advised to be passing sensitive data (in this case people's emails) via paths and should be via the body (I haven't looked into why) Anyway, so the way I configured it was to be contained within the body like this:
GET http://localhost:8080/User/check
Content-Type: application/json
{
"email" : "B.Nye#ABC.uk"
}
However when doing this call in JS:
if (isAuthenticated){
console.log("test -----------")
console.log(user.email)
fetch("http://###.###.###.###:8080/User/check", {
method: "GET",
headers:{"Content-Type":"application/json"},
body: JSON.stringify( {
email: "B.Nye#ABC.uk"
})
}).then((result)=> {
if (result.ok){
console.log("sucess")
}
else{
console.log("fail")
}})}
I get this error:
Unhandled Rejection (TypeError): Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.
Is there a way to bypass this or am I restricted to using either a POST method and reworking my backend method or containing the users email inside of the path?
Firstly if you want to use body, you should use POST method. This is why the Fetch API keeps sending errors to you.
The second question is, why do we not use GET method while we are sending sensitive datas like emails, passwords etc. The answer is, entire URL is cached user's browser history and it may expose user's sensitive data. And it will be much more secure if you use POST method because it prevents data leakage via the query string (you are sending data in the body).
Related
When I use insomnia to send a post request I get a 200 code and everything works just fine, but when I send a fetch request through javascript, I get a 405 'method not allowed error', even though I've allowed post requests from the server side.
(Server side code uses python).
Server side code
from pydantic import BaseModel
from typing import Optional
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = ["*"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["POST", "GET"],
allow_headers=["*"],
)
class data_format(BaseModel):
comment_id : int
username : str
comment_body : Optional[str] = None
#app.post('/post/submit_post')
async def sumbit_post(somename_3: data_format):
comment_id = somename_3.comment_id
username = somename_3.username
comment_body = somename_3.comment_body
# add_table_data(comment_id, username, comment_body) //Unrelated code
return {
'Response': 'Submission received',
'Data' : somename_3
}
JS code
var payload = {
"comment_id" : 4,
"username" : "user4",
"comment_body": "comment_4"
};
fetch("/post/submit_post",
{
method: "POST",
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(res){ return res.json(); })
.then(function(data){ alert( JSON.stringify( data ) ) })
The error
What should I do to get around this error?
Thanks in advance.
To start with, your code seems to be working just fine. The only part that had to be changed during testing it (locally) was the URL in fetch from /post/submit_post to (for instance) http://127.0.0.1:8000/post/submit_post, but I am assuming you already changed that using the domain name pointing to your app.
The 405 Method Not Allowed status code is not related to CORS. If POST was not included in the allow_methods list, the response status code would be 400 Bad Request (you could try removing it from the list to test it). From the reference above:
The HyperText Transfer Protocol (HTTP) 405 Method Not Allowed response
status code indicates that the server knows the request method, but
the target resource doesn't support this method.
The server must generate an Allow header field in a 405 status code
response. The field must contain a list of methods that the target
resource currently supports.
Thus, the 405 status code indicates that the POST request has been received and recognised by the server, but the server has rejected that specific HTTP method for that particular endpoint. Therefore, I would suggest you make sure that the decorator of the endpoint in the version you are running is defined as #app.post, as well as there is no other endpoint with the same path using #app.get. Additionally, make sure there is no any unintentional redirect happening inside the endpoint, as that would be another possible cause of that response status code. For future reference, when redirecting from a POST to GET request, the response status code has to change to 303, as shown here. Also, you could try allowing all HTTP methods with the wildcard * (i.e., allow_methods=['*']) and see how that works (even though it shouldn't be related to that). Lastly, this could also be related to the configurations of the hosting service you are running the application; thus, might be good to have a look into that as well.
It's and old issue, described here. You need Access-Control-Request-Method: POST header in your request.
I'm trying to use a Cloudflare Worker to proxy a POST request to another server.
It is throwing a JS exception – by wrapping in a try/catch blog I've established that the error is:
TypeError: A request with a one-time-use body (it was initialized from a stream, not a buffer) encountered a redirect requiring the body to be retransmitted. To avoid this error in the future, construct this request from a buffer-like body initializer.
I would have thought this could be solved by simply copying the Response so that it's unused, like so:
return new Response(response.body, { headers: response.headers })
That's not working. What am I missing about streaming vs buffering here?
addEventListener('fetch', event => {
var url = new URL(event.request.url);
if (url.pathname.startsWith('/blog') || url.pathname === '/blog') {
if (reqType === 'POST') {
event.respondWith(handleBlogPost(event, url));
} else {
handleBlog(event, url);
}
} else {
event.respondWith(fetch(event.request));
}
})
async function handleBlog(event, url) {
var newBlog = "https://foo.com";
var originUrl = url.toString().replace(
'https://www.bar.com/blog', newBlog);
event.respondWith(fetch(originUrl));
}
async function handleBlogPost(event, url) {
try {
var newBlog = "https://foo.com";
var srcUrl = "https://www.bar.com/blog";
const init = {
method: 'POST',
headers: event.request.headers,
body: event.request.body
};
var originUrl = url.toString().replace( srcUrl, newBlog );
const response = await fetch(originUrl, init)
return new Response(response.body, { headers: response.headers })
} catch (err) {
// Display the error stack.
return new Response(err.stack || err)
}
}
A few issues here.
First, the error message is about the request body, not the response body.
By default, Request and Response objects received from the network have streaming bodies -- request.body and response.body both have type ReadableStream. When you forward them on, the body streams through -- chunks are received from the sender and forwarded to the eventual recipient without keeping a copy locally. Because no copies are kept, the stream can only be sent once.
The problem in your case, though, is that after streaming the request body to the origin server, the origin responded with a 301, 302, 307, or 308 redirect. These redirects require that the client re-transmit the exact same request to the new URL (unlike a 303 redirect, which directs the client to send a GET request to the new URL). But, Cloudflare Workers didn't keep a copy of the request body, so it can't send it again!
You'll notice this problem doesn't happen when you do fetch(event.request), even if the request is a POST. The reason is that event.request's redirect property is set to "manual", meaning that fetch() will not attempt to follow redirects automatically. Instead, fetch() in this case returns the 3xx redirect response itself and lets the application deal with it. If you return that response on to the client browser, the browser will take care of actually following the redirect.
However, in your worker, it appears fetch() is trying to follow the redirect automatically, and producing an error. The reason is that you didn't set the redirect property when you constructed your Request object:
const init = {
method: 'POST',
headers: event.request.headers,
body: event.request.body
};
// ...
await fetch(originUrl, init)
Since init.redirect wasn't set, fetch() uses the default behavior, which is the same as redirect = "automatic", i.e. fetch() tries to follow redirects. If you want fetch() to use manual redirect behavior, you could add redirect: "manual" to init. However, it looks like what you're really trying to do here is copy the whole request. In that case, you should just pass event.request in place of the init structure:
// Copy all properties from event.request *except* URL.
await fetch(originUrl, event.request);
This works because a Request has all of the fields that fetch()'s second parameter wants.
What if you want automatic redirects?
If you really do want fetch() to follow the redirect automatically, then you need to make sure that the request body is buffered rather than streamed, so that it can be sent twice. To do this, you will need to read the whole body into a string or ArrayBuffer, then use that, like:
const init = {
method: 'POST',
headers: event.request.headers,
// Buffer whole body so that it can be redirected later.
body: await event.request.arrayBuffer()
};
// ...
await fetch(originUrl, init)
A note on responses
I would have thought this could be solved by simply copying the Response so that it's unused, like so:
return new Response(response.body, { headers: response.headers })
As described above, the error you're seeing is not related to this code, but I wanted to comment on two issues here anyway to help out.
First, this line of code does not copy all properties of the response. For example, you're missing status and statusText. There are also some more-obscure properties that show up in certain situations (e.g. webSocket, a Cloudflare-specific extension to the spec).
Rather than try to list every property, I again recommend simply passing the old Response object itself as the options structure:
new Response(response.body, response)
The second issue is with your comment about copying. This code copies the Response's metadata, but does not copy the body. That is because response.body is a ReadableStream. This code initializes the new Respnose object to contain a reference to the same ReadableStream. Once anything reads from that stream, the stream is consumed for both Response objects.
Usually, this is fine, because usually, you only need one copy of the response. Typically you are just going to send it to the client. However, there are a few unusual cases where you might want to send the response to two different places. One example is when using the Cache API to cache a copy of the response. You could accomplish this by reading the whole Response into memory, like we did with requests above. However, for responses of non-trivial size, that could waste memory and add latency (you would have to wait for the entire response before any of it gets sent to the client).
Instead, what you really want to do in these unusual cases is "tee" the stream so that each chunk that comes in from the network is actually written to two different outputs (like the Unix tee command, which comes from the idea of a T junction in a pipe).
// ONLY use this when there are TWO destinations for the
// response body!
new Response(response.body.tee(), response)
Or, as a shortcut (when you don't need to modify any headers), you can write:
// ONLY use this when there are TWO destinations for the
// response body!
response.clone()
Confusingly, response.clone() does something completely different from new Response(response.body, response). response.clone() tees the response body, but keeps the Headers immutable (if they were immutable on the original). new Response(response.body, response) shares a reference to the same body stream, but clones the headers and makes them mutable. I personally find this pretty confusing, but it's what the Fetch API standard specifies.
I have a resource to retrieve from endpoint. Passing filters to retrieve the resource is making URL to be long so I am having to pass filters in body . So I am using POST for retrieving a resource is it agains the REST standard ? Also shall I use status code 201 for POST or 200 for POST success is also fine ?Also in reponse body shall I pass message description or is there any standard for message description and error codes json.
200 should be the successful response header. If you respond with a json you could/should use a key named success to indicate if the operation was successful or not. The actual response data stays usually under the data key. If the success is false there should be and error code under the code key and a error message under msg key.
I don't say these above are STANDARD but they are a often used convention.
{
success: true.
data:[1,2,3],
total: 3
}
{
success: false,
code: 101,
msg: "Division by zero.'
}
For my GET request, my server is rejecting it with status: 403, "HTTP/1.1 403 Forbidden"
My GET request object as is follows :
"{"method":"GET","url":"api/myapi/GETstuff","params":{"from":"2017-06-02","to":"2017-06-02","myData":"DATA (AND SOME/MORE DATA)"}}"
The javascript code is:
function getMyData(params){
var url = 'myapi/getStuff';
var req = {
method: 'GET',
url: url,
params: params
};
$http(req) // details omitted for brevity
.success()
.error();
}
I believe the problem is special characters send to the IIS server (such as parenths, and / char), which I need to encode the params array before the GET request is sent via the Angular $http() service.
In order to encode the left/right parenths, and the / char, I'm trying:
request.params.myData = request.params.myData.replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\//g, '%2F')
However, my server is still rejecting the request.
And I doing something wrong?
You're getting a Forbidden error. Sure you don't need to authenticate or pass a token?
Before getting too manual, I'd suggest trying JSON.stringify(obj) and as a second pass, encodeURIComponent(obj)
If you're posting a valid JSON object, you might want to consider POST instead of GET, if you have backend access.
The service API I am consuming has a given GET method that requires the data be sent in the body of the request.
The data required in the body is a list of id's separated by hypen and could potentially be very large and thus it must be sent in the body otherwise it will likely foobar somewhere in the browsers/proxies/webservers etc chain. Note I don't have control over the service or API so please don't make suggestions to change it.
I am using the following jQuery code however observing the request/response in fiddler I can see that the "data" I am sending is ALWAYS converted and appended to the query string despite me setting the "processData" option to false...
$.ajax({
url: "htttp://api.com/entity/list($body)",
type: "GET",
data: "id1-id2-id3",
contentType: "text/plain",
dataType: "json",
processData: false, // avoid the data being parsed to query string params
success: onSuccess,
error: onError
});
Anyone know how I can force the "data" value to be sent in the body of the request?
In general, that's not how systems use GET requests. So, it will be hard to get your libraries to play along. In fact, the spec says that "If the request method is a case-sensitive match for GET or HEAD act as if data is null." So, I think you are out of luck unless the browser you are using doesn't respect that part of the spec.
You can probably setup an endpoint on your own server for a POST ajax request, then redirect that in your server code to a GET request with a body.
If you aren't absolutely tied to GET requests with the body being the data, you have two options.
POST with data: This is probably what you want. If you are passing data along, that probably means you are modifying some model or performing some action on the server. These types of actions are typically done with POST requests.
GET with query string data: You can convert your data to query string parameters and pass them along to the server that way.
url: 'somesite.com/models/thing?ids=1,2,3'
we all know generally that for sending the data according to the http standards we generally use POST request.
But if you really want to use Get for sending the data in your scenario
I would suggest you to use the query-string or query-parameters.
1.GET use of Query string as.
{{url}}admin/recordings/some_id
here the some_id is mendatory parameter to send and can be used and req.params.some_id at server side.
2.GET use of query string as{{url}}admin/recordings?durationExact=34&isFavourite=true
here the durationExact ,isFavourite is optional strings to send and can be used and req.query.durationExact and req.query.isFavourite at server side.
3.GET Sending arrays
{{url}}admin/recordings/sessions/?os["Windows","Linux","Macintosh"]
and you can access those array values at server side like this
let osValues = JSON.parse(req.query.os);
if(osValues.length > 0)
{
for (let i=0; i<osValues.length; i++)
{
console.log(osValues[i])
//do whatever you want to do here
}
}
Just in case somebody ist still coming along this question:
There is a body query object in any request. You do not need to parse it yourself.
E.g. if you want to send an accessToken from a client with GET, you could do it like this:
const request = require('superagent');
request.get(`http://localhost:3000/download?accessToken=${accessToken}`).end((err, res) => {
if (err) throw new Error(err);
console.log(res);
});
The server request object then looks like {request: { ... query: { accessToken: abcfed } ... } }
You know, I have a not so standard way around this. I typically use nextjs. I like to make things restful if at all possible. If I need to make a get request I instead use post and in the body I add a submethod parameter which is GET. At which point my server side handles it. I know it's still a post method technically but this makes the intention clear and I don't need to add any query parameters. Then the get method handles a get request using the data provided in the post method. Hopefully this helps. It's a bit of a side step around proper protocol but it does mean there's no crazy work around and the code on the server side can handle it without any problems. The first thing present in the server side is if(subMethod === "GET"){|DO WHATEVER YOU NEED|}