I am sending a request through fetch API in react application.
The fetch url contains the name argument, I should be able to create an account for the username which contains % in it. But when i tried to pass this url, i am getting the error message as unable to decode.
Note : This is http request
Format URL : http://localhost/users/api/service/username=test12%,cn=Administrators,cn=test administration,o=nmc
The space between test and administrator converts to %20 as expected.
But the % is not allowing me to pass this request.
let constructed_URL = 'http://localhost/users/api/service/username=test12%,cn=Administrators,cn=test administration';
fetch(constructed_URL, {
method: "get",
headers: urlParameter
})
I expect the browser to decode %, and pass this request.
fetch or console.error or Chrome somewhere automagically applies decodeURI (or -Component) if it thinks it was doubly encoded.
It doesn't behave that way if the URL is encoded thrice.
I wanted to use URIencoded file names in my file system directly. It simply is impossible to fetch these files at the moment.
Related
I am trying to create and post a tweet using the Twitter API. I have been using Postman to help create the API requests as per the instructions and walkthroughs on the Twitter API docs. When I am using Postman with hardcoded values everything works just fine and I can successfully post to twitter. The issue is when I try and create a unique signature to pass into the request, I get a 401(Unauthorized) from the twitter error response.
I follow the steps in the Twitter API docs at https://developer.twitter.com/en/docs/authentication/oauth-1-0a/creating-a-signature. And my outputs are identical in structure to the provided examples.
This is how I create the unique parameters:
To percentile encode the values I use encodeURIComponent()
oauth_nonce:
let OAUTH_NONCE = uuid.v1();
(uuid is one of many recommended way to create a random nonce, I have tried different ways but nothing works, also twitter says this can be any set of random alphanumerical strings)
oauth_timestamp:
let OAUTH_TIMESTAMP = Math.floor(Date.now() / 1000)
I create the parameter string like:
let parameterString = `include_entities=true&oauth_consumer_key=${CONSUMER_KEY}&oauth_nonce=${OAUTH_NONCE}&oauth_signature_method=${SIGNATURE_METHOD}&oauth_timestamp=${OAUTH_TIMESTAMP}&oauth_token=${ACCESS_TOKEN_KEY}&oauth_version=1.0&status=${encodedText}`
(The encoded text is percent encoded string that is being posted)
I then percent encode the parameter string and the base url which is ('https://api.twitter.com/2/tweets') to create a signature base string that looks like this
let signatureBaseString = `POST&${encodedURL}&${encodedParameterString}`
I create a signing key by encoding both the consumer secret token and access token secret
let signingKey = `${encodedConSecret}&${encodedOAuthSecret}`
I then use CryptoJS to create a hashed string:
let hash = CryptoJS.HmacSHA1(signatureBaseString, signingKey)
Then finally I create the signature like this:
let OAUTH_SIGNATURE = encodeURIComponent(Base64.stringify(hash))
I pass all that information into the config header for an axios post request.
Here is my config for the headers:
let config = {
method: 'post',
url: 'https://api.twitter.com/2/tweets',
headers: {
'Authorization': `OAuth oauth_consumer_key="${CONSUMER_KEY}",oauth_token="${ACCESS_TOKEN_KEY}",oauth_signature_method="HMAC-SHA1",oauth_timestamp="${OAUTH_TIMESTAMP}",oauth_nonce="${OAUTH_NONCE}",oauth_version="1.0",oauth_callback="https%3A%2F%2F127.0.0.1",oauth_signature="${OAUTH_SIGNATURE}"`,
'Content-Type': 'application/json',
},
data : data
};
Here are two exampled of the headers taken from the Network tab in the chrome dev tools. One is a success with hardcoded values from Postman and the fail is from the unique parameters that I created. The consumer key and oauth token are removed for security sake.
SUCCESS from hard coded Postman:
OAuth oauth_consumer_key="CONSUMER_KEY",oauth_token="OAUTH_TOKEN",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1637279149",oauth_nonce="Ry6ldroxEyM",oauth_version="1.0",oauth_callback="https%3A%2F%2F127.0.0.1",oauth_signature="G7AoS6gk1MyI3Eoc6o%2F%2Bp8dM4o8%3D"
FAIL from created parameters:
OAuth oauth_consumer_key="CONSUMER_KEY",oauth_token="OAUTH_TOKEN",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1637279767",oauth_nonce="a8qgwtye6tw",oauth_version="1.0",oauth_callback="https%3A%2F%2F127.0.0.1",oauth_signature="an%2BpRdqwrqLsx9%2BS%2BrCqXY1omEw%3D"
Now I do not think its an issue with the signature, as I used the Twitter Doc example and got the same output as them, so the calculations for that seem to work just fine. My guess is that the problem is with the parameter string value, but I have tried a few different values but nothing seems to work. Again it works just fine with hardcoded values from Postman and I followed the examples and received similar outputs so I am a bit confused on why I receive the 401(Unauthorized) error. Any insight would be much appreciated, Thanks!
Is there a way to get final redirect url without following it with Fetch API ?
Example:
http://some.link/1 redirects to http://some.link/2
fetch('http://some.link/1', someParams) => get 'http://some.link/2' url
without request to it.
Yes, you can use the response.url property:
fetch('http://some.link/1').then(function(response) {
console.log(response.url); // Final URL after redirections
});
The url read-only property of the Response interface contains the URL
of the response. The value of the url property will be the final URL
obtained after any redirects.
However, this will only work if the initial and all the URLs of the redirection chain have same origin.
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.
I currently have the following working piece of code (angular but applies to any JS framework):
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
The above is called after a form was completed and the user has clicked on "submit" (the real situation is a bit more complex than this but it is the same idea). I do the form check asynchronously, so there's no page reload.
If the request is successful, returns a binary (a pdf file), if not succesful, the request returns a 400 BadRequest with errors formatted in JS. So what I do is, if successful, I redirect to the same url to have the PDF otherwise I get the JSON error object and do something with it.
How can I refrain from making two requests if the requests is successful?
Note1: on the backend side I would like to keep only one route that does everything, check + return PDF
Note2: the current situation is pretty neat in my opinion, since I have an asynchronous form check and if successful the file downloads directly in the browser since I have "CONTENT-DISPOSITION" -> "attachment" in the HTTP header of the successful response
Update: additional information about the architecture as requested by Emile:
In my use case I have one endpoint that checks inputs (and other external requirements). For security reasons I cannot output the PDF if all requirements are not satisfied so I have to do the check prior to delivering the file ( the file is automatically generated) anyway. So having two endpoints would just be redundant and add some unnecessary complexity.
While writing I think an alternative solution could be to pass an argument on the endpoint while doing the check, so that if successful, it stops and does not generate the PDF, and then redirect to the same endpoint without the flag which will output the PDF.
So I do the check twice but only load (and generate - which is resource intensive) the file only once and I have only one endpoint...
Here's the adapted code:
var url = '/endpoint/to/my/file';
$http({
method: 'GET',
url: url+'?check'
})
.success(function(jdata) {
window.location = url;
})
.error(function(je){
// display errors on page
});
On the backend side (I use Play framework/Scala)
def myendpoint(onlyDoCheck: Boolean = false) = Action{implicit request =>
myForm.bindFromRequest.fold(
e => BadRequest(myErrors),
v => if(onlyDoCheck) Ok(simpleOkResponse) else Ok(doComputationgeneratefileandoutputfile)
)
}
The real deal
The best you could do is split your endpoint.
One for the form and the convenience of having errors without refresh.
Then, on success, redirect to your other endpoint which only downloads the file.
If the file was on the disk and wasn't auto-generated and required to be authenticated to be downloaded, you could hide the file behind a normal endpoint, do the checks, and return the file using X-Accel-Redirect header with nginx or X-Sendfile using apache.
The hack
Disclaimer: This is more of a hack than the best solution. As mention by #Iceman, Safari, IE, Safari-iOS, Opera-mini and some such browsers don't support this particular spec.
In your server-side endpoint, if the file is available without errors, you can set the header to the content-type of the file (like 'application/pdf') so the download will starts automatically.
If there are errors, don't set the header and return a json of the errors to inform the user through javascript.
Since we don't know what's behind, here's a python (Django) example:
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename=your_filename.pdf'
response.write(repport.printReport(participantId))
return response
You can handle the response in the ajax success callback:
$.ajax({
url: 'endpoint.php',
success: function(data) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "filename.pdf";
link.click();
}
});
You could also try the jQuery fileDownload plugin mentioned in this answer.
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|}