I have an assignment to: use the method GET to fetch api info from http://api.example.com/. I'm also told to only use: Javascript/Reactjs
However, everywhere I search, I can only fetch the data. Are these two the same thing? Or am I looking at the wrong tutorials?
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
items: json,
})
});
}
render() {
var { isLoaded, items } = this.state;
if (!isLoaded) {
return <div>Loading...</div>
}
else {
return (
<div className="App">
<ul>
{items.map(item => (
<li key={item.uid}>
Name: {item.name} | Email:{item.email}
</li>
))};
</ul>
</div>
);
}
}
}
export default App;
they are interchangeable, as a technical jargon from the field, "get data from api" and "fetch data from the api" means the same thing.
in javascript, nowadays, we use the Fetch API to interact with an api, using HTTP request methods like GET, HEAD, POST, DELETE.
unfortunately i don't know a page or repository listing these jargons
Fetch is a javascript API used for making XHR requests across the web (normally for interacting with an API).
let promise = fetch(url, {
method: "GET", // POST, PUT, DELETE, etc.
headers: {
// the content type header value is usually auto-set
// depending on the request body
"Content-Type": "text/plain;charset=UTF-8"
},
body: undefined // string, FormData, Blob, BufferSource, or URLSearchParams
referrer: "about:client", // or "" to send no Referer header,
// or an url from the current origin
referrerPolicy: "no-referrer-when-downgrade", // no-referrer, origin, same-origin...
mode: "cors", // same-origin, no-cors
credentials: "same-origin", // omit, include
cache: "default", // no-store, reload, no-cache, force-cache, or only-if-cached
redirect: "follow", // manual, error
integrity: "", // a hash, like "sha256-abcdef1234567890"
keepalive: false, // true
signal: undefined, // AbortController to abort request
window: window // null
});
But GET is an HTTP verb. The primary or most-commonly-used HTTP verbs (or methods, as they are properly called) are POST, GET, PUT, PATCH, and DELETE. These correspond to create, read, update, and delete (or CRUD) operations, respectively.
To understand more about these methods read Architectural Styles and
the Design of Network-based Software Architectures by Dr Roy Fielding or popularly know as roy fielding paper which describes about the RESTful nature of the web and the use of these HTTP verbs. https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
Related
i have a problem integrating paypals payment gateway. I am using javascript for the client, python for the backend and the checkouts v2 api.
Creating a order on the backend works without trouble, but while waiting for my servers response the createOrder function raises a error:
unhandled_error
Object { err: "Expected an order id to be passed\nLe/</<#https://www.sandbox.paypal.com/smart/buttons?style.layout=vertical&style.color=blue&style.shape=rect&style.tagline=false&components.0=buttons&locale.country=NO&locale.lang=no&sdkMeta=eyJ1cmwiOiJodHRwczovL3d3dy5wYXlwYWwuY29tL3Nkay9qcz9jbGllbnQtaWQ9QWJmSjNNSG5oMkFIU1ZwdXl4eW5lLXBCbHdJZkNsLXpyVXc1dzFiX29TVUloZU01LXNMaDNfSWhuTnZkNUhYSW5wcXVFdm5MZG1LN0xOZ1gmZGlzYWJsZS1mdW5kaW5nPWNyZWRpdCxjYXJkIiwiYXR0cnMiOnt9fQ&clientID=AbfJ3MHnh2AHSVpuyxyne-pBlwIfCl-zrUw5w1b_oSUIheM5-sLh3_IhnNvd5HXInpquEvnLdmK7LNgX&sessionID=e2ea737589_mtc6mtu6mdi&buttonSessionID=de4bfb3626_mtc6mjm6mtk&env=sandbox&fundingEligibility=eyJwYXlwYWwiOnsiZWxpZ2libGUiOnRydWV9LCJjYXJkIjp7ImVsaWdpYmxlIjpmYWxzZSwiYnJhbmRlZCI6dHJ1ZSwidmVuZG9ycyI6eyJ2aXNhIjp7ImVsaWdpYmxlIjp0cnVlfSwibWFzdGVyY2FyZCI6eyJlbGlnaWJsZSI6dHJ1ZX0sImFtZXgiOnsiZWxpZ2libGUiOnRydWV9LCJkaXNjb3ZlciI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJoaXBlciI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJlbG8iOnsiZWxpZ2libGUiOmZhbHNlfSwiamNiIjp7ImVsaWdpYmxlIjpmYWxzZX19…", timestamp: "1593537805136", referer: "www.sandbox.paypal.com", sessionID: "e2ea737589_mtc6mtu6mdi", env: "sandbox", buttonSessionID: "de4bfb3626_mtc6mjm6mtk" }
Error: Expected an order id to be passed
Error: Expected an order id to be passed
12V21085461823829 // ticks in a few seconds later
Console screenshot
The problem seems to be that createOrder does not wait for the promise before raising the error, or that the promise is not given in the correct way. Something like that. Anyways here is the client side code:
paypal.Buttons({
// button styling removed for clarity
createOrder: function() {
// purchase information
var data = {
'track_id': vm.selectedTrack.id,
'lease_id': vm.selectedLease.id,
}
// post req to api with lease and track ids
// create payment on server side
fetch('http://localhost:5000/api/paypal/create-purchase', {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(data),
}).then(function(res) {
return res.json();
}).then(function(data) {
console.log(data.order_id)
return data.order_id
})
}
// conatiner element to render buttons in
}).render('#paypal-button');
And the server side:
#app.route('/api/paypal/create-purchase', methods=['POST'])
def paypal_create_purchase():
# cart validation removed for clarity
# create paypal purchase
environment = SandboxEnvironment(client_id=app.config['PAYPAL_PUBLIC'], client_secret=app.config['PAYPAL_PRIVATE'])
client = PayPalHttpClient(environment)
paypal_request = OrdersCreateRequest()
paypal_request.prefer('return=representation')
paypal_request.request_body (
{
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": lease.price
}
}
]
}
)
try:
# Call API with your client and get a response for your call
response = client.execute(paypal_request)
order = response.result
print(order.id)
except IOError as ioe:
print (ioe)
if isinstance(ioe, HttpError):
# Something went wrong server-side
print(ioe.status_code)
# note that it is the same key as on the client
return jsonify(success=True,order_id=order.id)
I found this similar thread, but i dont consider the origin of the error to be the same as in that thread (incorrect json key on client)
Also see this relevant page in the docs which supplies this code:
createOrder: function() {
return fetch('/my-server/create-paypal-transaction', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.orderID; // Use the same key name for order ID on the client and server
});
}
Damn, just as i was typing out the last part of the post i noticed the error. A missing return before my fetch call. Will leave this up for other people with the same mistake.
Good Day developers.In this app i'm building im having a little issue with a rating process i want to implement. Being the app working on local host is perfect but once i posted in Heroku , its error log throws me this error on 500 Internal Error.
I just posted this videos in order you could see either cases i try to apply:
1rst case (Either passing object or integer to request body parameter with app working in local , perfect performance)
https://youtu.be/IdpHQg4FW7k
2nd case(Passing integer to request body parameter with app in heroku, error 500)
https://youtu.be/eJHSA_h8R3Y
3rd case(Passing object to request body parameter with app in heroku ,error 500)
https://youtu.be/_l-Z0I3hNZU
Guess with this videos i expose more on detail what's going on, notwithstanding , i'm going to leave the code below before this current edit.Thanks for your support
Basically from my front end i just send through a request body parameter a rate(selected by user among 1-5 integer-numbers) :
COMPONENT in VUE
functionx(){
this.$store.dispatch("rateUpdater", {
ratePack: {
productRate: 4,
},
product_id: this.ProductCard.product_id,
});
}
VUEX Process
rateUpdater({ dispatch }, { product_id, ratePack }) {
fetch(url+"mini/all_products/user/product_rated/" + product_id, {
credentials: "include",
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify(ratePack),
})
.then((newData) => {
return newData.json();
})
.then((data) => {
if (data.Error) {
data.Error
} else {
data;
dispatch("fetchAllProducts");
}
})
.catch((error)=> {
alert("Request on Rate: ", error);
});
},
Thus then receiving that data in my back end , i just initially get initialized that variable on my request body as Integer , cause is the way it comes from front-end, then basically i change it to a Double believing that could work :
CONTROLLER
importations
package com.miniAmazon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
#RequestMapping(value="/mini/all_products/user/product_rated/{id}",method = RequestMethod.POST)
public ResponseEntity <Map<String,Object>>rateProductOnBuy(#PathVariable("id") Long id, Authentication authentication
,#RequestBody Integer productRated
){
SOME CODE.....
Product product=productRepository.getOne(id);
Double rate=new Double(productRated)----------->transforming integer of request body to double
---------------process of calculating average rate--------------------
Double deframingRate=product.getProductRate()*product.getPeopleRating()+rate;
Integer deframingPeopleVoting=product.getPeopleRating()+1;
Double finalAverage=deframingRate/deframingPeopleVoting;
-----------setting new Rate-------------
product.setProductRate(finalAverage);
---------setting new amount of voters-----------
product.setPeopleRating(deframingPeopleVoting);
---------saving repository----
productRepository.save(product);
return new ResponseEntity<>(makeMapResponse("Success","Product Rated"), HttpStatus.CREATED);
}
but Basically that little change didn't work .....Any idea about how could i deal with this problem ?
thanks in advance
When you write #RequestBody Integer productRated that means that the accepted request body is just one single number. It cannot be an object containing a key-value pair for the number.
You may be able to fix this by changing the client so it only sends a number, not an object:
rateUpdater({ dispatch }, { product_id, ratePack }) {
fetch(url+"mini/all_products/user/product_rated/" + product_id, {
credentials: "include",
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: ratePack.productRate, // just a number!
})
This is not very extensible though, because you can't send more than this one number in the body.
To make this extensible, you can for example create a class that represents the request body:
class YourRequestType {
Integer productRate; // add getters, setters, if you want
}
and use that class in your controller method declaration.
#RequestBody YourRequestType productRated
I'm trying to use apollo RESTDataSource to wrap my rest api. I need to pass some headers to the api call.
I'm following the example from the docs: https://www.apollographql.com/docs/apollo-server/features/data-sources#intercepting-fetches
This is my code:
willSendRequest(request: RequestOptions) {
console.log(`request 1: ${JSON.stringify(request)}`);
request.headers.set('Authorization', this.context.authorization);
console.log(`request 2: ${JSON.stringify(request)}`);
}
I'm expecting the headers to contain 'Authorization'. But it's always empty.
The log from the above code:
request 1: {"method":"POST","path":"partnerinvoices","body":{"command": "input","params":{},"headers":{}}
request 2: {"method":"POST","path":"partnerinvoices","body":{"command":"input","params":{},"headers":{}}
I can override body and params in willSendRequest method without any problem.
There are few ways that you could implement this,
within your Datasources class that extends RESTDataSource set the headers before request is being made
willSendRequest(request) {
request.headers.set('Authorization', 'Bearer .....')
}
or as a third argument in the datasource method (post, get, put, ...)
this.post('endpoint', {}, { headers: { 'Authorization': 'Bearer ...' } })
It is super important, if you are using Typescript, that you match the original signature of the willSendRequest method:
protected willSendRequest?(request: RequestOptions): ValueOrPromise<void>;
(Link to the docs)
So, make sure the method looks like this:
protected willSendRequest?(request: RequestOptions): ValueOrPromise<void> {
request.headers.set("Authorization", this.context.authorization);
}
You need to use request.headers.get('Authorization') to get your desired data. Using JSON.stringify will not give you the headers values as it is not a object literal.
willSendRequest(request: RequestOptions) {
console.log(`request 1: ${request.headers.get('Authorization')}`);
request.headers.set('Authorization', this.context.authorization);
console.log(`request 2: ${request.headers.get('Authorization')}`);
}
I'm trying the Wikipedia client login flow depicted in the API:Login docs, but something wrong happens:
1) I correctly get a token raised with the HTTP GET https://en.wikipedia.org/w/api.php?action=query&meta=tokens&type=login&format=json
and I get a valid logintoken string.
2.1) I then try the clientlogin like:
HTTP POST /w/api.php?action=clientlogin&format=json&lgname=xxxx&lgtoken=xxxx%2B%5C
and the POST BODY was
{
"lgpassword" : "xxxxx",
"lgtoken" : "xxxxx"
}
But I get an error:
{
"error": {
"code": "notoken",
"info": "The \"token\" parameter must be set."
},
"servedby": "mw1228"
}
If I try to change lgtoken to token I get the same result.
2.2) I have then tried the old method i.e. action=login and passing the body, but it does not work, since it gives me back another login token: HTTP POST https://en.wikipedia.org/w/api.php?action=login&format=json&lgname=xxxx
and the same POST BODY
I then get
{
"warnings": {}
},
"login": {
"result": "NeedToken",
"token": "xxxxx+\\"
}
where the docs here states that
NeedToken if the lgtoken parameter was not provided or no session was active (e.g. your cookie handling is broken).
but I have passed the lgtoken in the json body as showed.
I'm using Node.js and the built-in http module, that is supposed to pass and keep session Cookies in the right way (with other api it works ok).
I have found a similar issue on a the LrMediaWiki client here.
[UPDATE]
This is my current implementation:
Wikipedia.prototype.loginUser = function (username, password) {
var self = this;
return new Promise((resolve, reject) => {
var cookies = self.cookies({});
var headers = {
'Cookie': cookies.join(';'),
'Accept': '*/*',
'User-Agent': self.browser.userAgent()
};
// fetch login token
self.api.RequestGetP('/w/api.php', headers, {
action: 'query',
meta: 'tokens',
type: 'login',
format: 'json'
})
.then(response => { // success
if (response.query && response.query.tokens && response.query.tokens['logintoken']) {
self.login.logintoken = response.query.tokens['logintoken'];
self.logger.info("Wikipedia.login token:%s", self.login);
return self.api.RequestPostP('/w/api.php', headers, {
action: 'login',
format: 'json',
lgname: username
},
{
lgpassword: password,
lgtoken: self.login.logintoken
});
} else {
var error = new Error('no logintoken');
return reject(error);
}
})
.then(response => { // success
return resolve(response);
})
.catch(error => { // error
self.logger.error("Wikipedia.login error%s\n%#", error.message, error.stack);
return reject(error);
});
});
}//loginUser
where this.api is a simple wrapper of the Node.js http, the source code is available here and the api signatures are like:
Promise:API.RequestGetP(url,headers,querystring)
Promise:API.RequestPostP(url,headers,querystring,body)
If the currently accepted answer isn't working for someone, the following method will definitely work. I've used the axios library to send requests. Any library can be used but the key lies in formatting the body and headers correctly.
let url = "https://test.wikipedia.org/w/api.php";
let params = {
action: "query",
meta: "tokens",
type: "login",
format: "json"
};
axios.get(url, { params: params }).then(resp => {
let loginToken = resp.data.query.tokens.logintoken
let cookie = resp.headers["set-cookie"].join(';');
let body = {
action: 'login',
lgname: 'user_name',
lgpassword: 'password',
lgtoken: loginToken,
format: 'json'
}
let bodyData = new URLSearchParams(body).toString();
axios.post(url, bodyData, {
headers: {
Cookie: cookie,
}
}).then(resp => {
// You're now logged in!
// You'll have to add the following cookie in the headers again for any further requests that you might make
let cookie = resp.headers["set-cookie"].join(';')
console.log(resp.data)
})
})
And you should be seeing a response like
{
login: { result: 'Success', lguserid: 0000000, lgusername: 'Username' }
}
The second post request was where I got stuck for several hours, trying to figure out what was wrong. You need to send the data in an encoded form by using an API like URLSearchParams, or by just typing up the body as a string manually yourself.
I think from what you are saying you have lgtoken and lgname in the URL you are using, and then lgpassword and lgtoken (again!) in a JSON-encoded POST body.
This is not how the Mediawiki API works.
You submit it all as POST parameters. JSON is never involved, except when you ask for the result to come back in that format. I can't help you fix your code as you don't provide it, but that's what you need to do. (If you edit your question with your code, I'll do my best to help you.)
After seeing your code, I'll presume (without knowing the detail of your code) that you want something like this:
return self.api.RequestPostP('/w/api.php', headers, {
action: 'login',
format: 'json',
lgname: username,
lgpassword: password,
lgtoken: self.login.logintoken
});
Is it possible to alter the headers of the Request object that is received by the fetch event?
Two attempts:
Modify existing headers:
self.addEventListener('fetch', function (event) {
event.request.headers.set("foo", "bar");
event.respondWith(fetch(event.request));
});
Fails with Failed to execute 'set' on 'Headers': Headers are immutable.
Create new Request object:
self.addEventListener('fetch', function (event) {
var req = new Request(event.request, {
headers: { "foo": "bar" }
});
event.respondWith(fetch(req));
});
Fails with Failed to construct 'Request': Cannot construct a Request with a Request whose mode is 'navigate' and a non-empty RequestInit.
(See also How to alter the headers of a Response?)
Creating a new request object works as long as you set all the options:
// request is event.request sent by browser here
var req = new Request(request.url, {
method: request.method,
headers: request.headers,
mode: 'same-origin', // need to set this properly
credentials: request.credentials,
redirect: 'manual' // let browser handle redirects
});
You cannot use the original mode if it is navigate (that's why you were getting an exception) and you probably want to pass redirection back to browser to let it change its URL instead of letting fetch handle it.
Make sure you don't set body on GET requests - fetch does not like it, but browsers sometimes generate GET requests with the body when responding to redirects from POST requests. fetch does not like it.
You can create a new request based on the original one and override the headers:
new Request(originalRequest, {
headers: {
...originalRequest.headers,
foo: 'bar'
}
})
See also: https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
Have you tried with a solution similar to the one in the question you mention (How to alter the headers of a Response?)?
In the Service Worker Cookbook, we're manually copying Request objects to store them in IndexedDB (https://serviceworke.rs/request-deferrer_service-worker_doc.html). It's for a different reason (we wanted to store them in a Cache, but we can't store POST requests because of https://github.com/slightlyoff/ServiceWorker/issues/693), but it should be applicable for what you want to do as well.
// Serialize is a little bit convolved due to headers is not a simple object.
function serialize(request) {
var headers = {};
// `for(... of ...)` is ES6 notation but current browsers supporting SW, support this
// notation as well and this is the only way of retrieving all the headers.
for (var entry of request.headers.entries()) {
headers[entry[0]] = entry[1];
}
var serialized = {
url: request.url,
headers: headers,
method: request.method,
mode: request.mode,
credentials: request.credentials,
cache: request.cache,
redirect: request.redirect,
referrer: request.referrer
};
// Only if method is not `GET` or `HEAD` is the request allowed to have body.
if (request.method !== 'GET' && request.method !== 'HEAD') {
return request.clone().text().then(function(body) {
serialized.body = body;
return Promise.resolve(serialized);
});
}
return Promise.resolve(serialized);
}
// Compared, deserialize is pretty simple.
function deserialize(data) {
return Promise.resolve(new Request(data.url, data));
}
If future readers have a need to also delete keys in the immutable Request/Response headers and also want high fidelity to the immutable headers, you can effectively clone the Header object:
const mutableHeaders = new Headers();
immutableheaders.forEach((value, key, parent) => mutableHeaders.set(key, value));
mutableHeaders.delete('content-encoding');
mutableHeaders.delete('vary');
mutableHeaders['host'] = 'example.com';
// etc.
You can then create a new Request and pass in your mutableHeaders.
This is preferred to the accepted answer because if you have the need to proxy a Request, you don't want to manually specify every possible header while including the Cloudflare, AWS, Azure, Google, etc. custom CDN headers.
Background Info
The reason why the headers are immutable or read-only in a Request is because:
interface Request extends Body {
readonly cache: RequestCache;
readonly credentials: RequestCredentials;
readonly destination: RequestDestination;
readonly headers: Headers;
readonly integrity: string;
...
The interface for Headers is:
interface Headers {
append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
has(name: string): boolean;
set(name: string, value: string): void;
forEach(callbackfn: (value: string, key: string, parent: Headers) => void, thisArg?: any): void;
}