Is it possible, using the fetch API, to set default headers for every single request?
What I want to do is set an Authorization header whenever there is a json web token in the localStorage. My current solution is to set the headers with this function:
export default function setHeaders(headers) {
if(localStorage.jwt) {
return {
...headers,
'Authorization': `Bearer ${localStorage.jwt}`
}
} else {
return headers;
}
}
Setting the headers in a fetch request would then look like this:
return fetch('/someurl', {
method: 'post',
body: JSON.stringify(data),
headers: setHeaders({
'Content-Type': 'application/json'
})
})
But there has to be a better way to do this. I'm currently developing a React/Redux/Express app if that is of any help.
Creating a fetch wrapper could solve your problem:
function updateOptions(options) {
const update = { ...options };
if (localStorage.jwt) {
update.headers = {
...update.headers,
Authorization: `Bearer ${localStorage.jwt}`,
};
}
return update;
}
export default function fetcher(url, options) {
return fetch(url, updateOptions(options));
}
You also get the added benefit of being able to switch your request client easily for all the calls in your application if you decide you like Axios or other package better. And you can do other things like check if options.body is an object and add the 'Content-Type: application/json header.
You could use Axios instead of fetch, with Interceptors
const setAuthorization = (token) => {
api.interceptors.request.use((config) => {
config.headers.Authorization = 'Bearer ' + token;
return config;
});
}
Where Api is an axios Object with a base URL
const api= axios.create({
baseURL: 'http://exemple.com'
});
And when you get your token, u just have to call the function setAuthorization.
Source: Axios README.md
Andri Möll created a FetchDefaults.js mixin for fetch that sets fetch defaults:
var Url = require("url")
var assign = require("oolong").assign
var merge = require("oolong").merge
var PARSE_QUERY = false
var PROTOCOL_RELATIVE = true // Enable //example.com/models to mimic browsers.
exports = module.exports = function(fetch, rootUrl, defaults) {
if (typeof rootUrl === "string") rootUrl = parseUrl(rootUrl)
else defaults = rootUrl, rootUrl = null
return assign(exports.fetch.bind(null, fetch, rootUrl, defaults), fetch)
}
exports.fetch = function(fetch, rootUrl, defaults, url, opts) {
if (rootUrl != null) url = rootUrl.resolve(url)
if (typeof defaults === "function") defaults = defaults(url, opts)
return fetch(url, opts == null ? defaults : merge({}, defaults, opts))
}
function parseUrl(url) {
return Url.parse(url, PARSE_QUERY, PROTOCOL_RELATIVE)
}
Distributed under AGPL-3.0-only license
A quick and unrecommended hack is to redefine the default .fetch() function:
const oldFetch = window.fetch;
window.fetch = function() {
arguments[1].headers = { 'blahblah' : 'blabla' };
return oldFetch.apply(window, arguments);
}
Code is untested and unfinished. If you decide to use this answer, check arguments.length, add code to preserve existing headers, etc. etc. I'm just providing the direction for further exploration.
You can override default fetch api:
var originalFetch = window.fetch;
window.fetch = function (input, init) {
if (!init) {
init = {};
}
if (!init.headers) {
init.headers = new Headers();
}
// init.headers could be:
// `A Headers object, an object literal,
// or an array of two-item arrays to set request’s headers.`
if (init.headers instanceof Headers) {
init.headers.append('MyHeader', 'Value');
} else if (init.headers instanceof Array) {
init.headers.push(['MyHeader', 'Value']);
} else {
// object ?
init.headers['MyHeader'] = 'Value';
}
return originalFetch(input, init);
};
References:
https://fetch.spec.whatwg.org/#fetch-method
https://fetch.spec.whatwg.org/#requestinit
Related
In action file the code:
...
const config = {
headers:{
'Content-type': 'application/json'
}
}
const {data} = await axios.post('http://localhost:8000/api/register/',
{'email':email, 'password':password}, config)
...
It's working; then localhost:8000 moved to package.json as a proxy, after that got an issue CSRF token missing or incorrect, how to fix that, thanks.
Application was restarted with no changes. Furthermore, the request has changed to localhost:3000 instead of 8000.
Django can provide the CSRF token in your cookies with a decorator. Then you can get it from your cookies and add it as a HTTP header:
views.py:
from django.views.decorators.csrf import ensure_csrf_cookie
# add this decorator to your main view
# (the one which serves your first html/javascript code, not the /api/register one)
#ensure_csrf_cookie
def index(request):
...
javascript:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
...
const config = {
headers:{
'Content-type': 'application/json',
'X-CSRFToken': getCookie("csrftoken") // added the csrf cookie header
}
}
const {data} = await axios.post('http://localhost:8000/api/register/',
{'email':email, 'password':password}, config)
i can't access to a key of a json response from a restful web service.
{"_body":"{\"values\": {\"user_id\":\"1\",\"name\":\"fred test\",\"email\":\"fred#test.test\",\"username\":\"fredtest\",\"token\":\"d5f66a06ec809d70d0c52842df8dc0011d7d1ad0f2d56f50d3123da17a2489fe\"}}","status":200,"ok":true,"statusText":"OK","headers":{"pragma":["no-cache"],"content-type":["text/html;charset=UTF-8"],"cache-control":["no-store"," no-cache"," must-revalidate"],"expires":["Thu"," 19 Nov 1981 08:52:00 GMT"]},"type":2,"url":"http://localhost/PHP-Slim-Restful/api/login"}
I would like to acces to 'values' in this function: (this.responseData.values)
login(){
console.log('login'+ this.userData);
// Your app login API web service call triggers
this.authService.postData(this.userData,'login').then((result) => {
this.responseData = result;
console.log('userdata : '+ temp);
if(this.responseData.values){
console.log('response: ' + this.responseData);
localStorage.setItem('userData', JSON.stringify(this.responseData));
this.navCtrl.push(TabsPage);
}
else{
this.showToastWithCloseButton()
}
}, (err) => {
console.log('erreur : '+err);
});
}
I have an error undifined!
Can you help me?
I have used Observable to return json data and using the subscribe function in my method and using response.json() to convert the JSON reponse from RESTful webservices.
My component method,
import {Http, Headers, Response, RequestOptions} from '#angular/http';
import {Observable} from 'rxjs/Rx';
var response = this.service.post('deleteUserDetails/'+this.selectedUserId, null);
response.subscribe((res) => {
var response = res.json();
});
Service Post method,
post(url: string, data : any): Observable<any> {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let options = new RequestOptions({ headers: headers});
return this.http.post(url, data,{headers: headers});
}
I think this might be helpful for your query.
You can make a for in your JSON and access the return values of your post. Something like that.
"this.responseData = result.json();" -> Return JSON. Make a for.
Example:
public postData(data, url: string) {
this.http.post(url, data).toPromise().then(res => {
let responseData = res.json();
if (responseData) {
for (var item of responseData) {
//Implments
}
}
}, (err) => {
});
}
In my application I'm trying to send some header parameters from the angular2 application to my node server:
var token = localStorage.getItem('token');
var headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Auth-Token', token);
var url = this.baseUrl + '/initdata';
return this._http.get( url, {headers: headers, body : {}}).toPromise()
.then(response => {
const status = response.json().status;
if(status == SERVER_RESPONSE_STATUS.SUCCESS)
{
return response.json().result;
}
else if( status == SERVER_RESPONSE_STATUS.FAILED)
{
throw new Error(response.json().message);
}
})
.catch(this.handleError);
}
But the problem is when I'm trying to read the value from node, the value for "auth-token" cannot be extracted (saying undefined)
router.use('/', function (req, res, next) {
tokenGenerator.verify(req.header('auth-token'), Constants.AUTH_PRIVATE_KEY, function (err, decoded) {
});
});
in angular2, I'm importing Headers from http as well:
import {Http, Headers} from "#angular/http";
Can someone please help me what's the issue here?
Thanks
You can read tokens from header like this
if(req.headers.hasOwnProperty('token')) {
req.headers.authorization = 'Bearer ' + req.headers.token;
token = req.headers.token;
}
To send the token from Ng2
headers.append('token', token);
This us how I do it.
I have a complex object parameter that I need to send as post, as it could be too long for querystring. The post call is asking to have an excel file dynamically generated and then downloaded asynchronously. But all of this is happening inside of a react application. How does one do this using axios.post, react, and webapi? I have confirmed that the file does generate and the download up to the response does come back, but I'm not sure how to actually open the file. I have a hidden iframe that I'm trying to set the path, src, of the file to, but I dont know what response property to use.
// webapi
[HttpPost]
public HttpResponseMessage Post([FromBody]ExcelExportModel pModel)
{
var lFile = ProductDataModel.GetHoldingsExport(pModel);
var lResult = new HttpResponseMessage(HttpStatusCode.OK);
lResult.Content = new ByteArrayContent(lFile);
lResult.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "HoldingsGridExport.xls"
};
lResult.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return lResult;
}
// client side api
static getHoldingsExport({ UserConfigurationID, UserID, Configurations, ViewName, SortModel, FilterModel, UserConfigType, IsDefault, LastPortfolioSearchID = null, ProductId }) {
const filterModel = JSON.stringify(FilterModel); // saving as string as this model is dynamically generated by grid out of my control
const sortModel = JSON.stringify(SortModel);
let params = JSON.stringify({
UserConfigurationID,
UserID,
Configurations,
ViewName,
filterModel,
sortModel,
UserConfigType,
IsDefault,
LastPortfolioSearchID,
ProductId
});
return axiosInstance.post("/api/HoldingsExport", params);
}
// client side app call to get file
HoldingsApi.getHoldingsExport(config)
.then(function(response) {
debugger;
let test = response;
})
.catch(error => {
toastr.success('Failed to get export.');
});
This is how I've achieved file downloads by POSTing via Axios:
Axios.post("YOUR API URI", {
// include your additional POSTed data here
responseType: "blob"
}).then((response) => {
let blob = new Blob([response.data], { type: extractContentType(response) }),
downloadUrl = window.URL.createObjectURL(blob),
filename = "",
disposition = response.headers["content-disposition"];
if (disposition && disposition.indexOf("attachment") !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, "");
}
}
let a = document.createElement("a");
if (typeof a.download === "undefined") {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
}).catch((error) => {
// ...
});
Just in case the above solution does not serve you quite well, here is how I could be able to download videos that are hosted on S3 AWS buckets,
const handleDownload = () => {
const link = document.createElement("a");
link.target = "_blank";
link.download = "YOUR_FILE_NAME"
axios
.get(url, {
responseType: "blob",
})
.then((res) => {
link.href = URL.createObjectURL(
new Blob([res.data], { type: "video/mp4" })
);
link.click();
});
};
And I trigger handleDownload function in a button with onClick.
The url in the function has the video URL from S3 buckets
I'm trying to upload a csv file using ng-file-upoad. Here is my code snippet:
Upload.upload({
url: baseUrl + '/file-upload',
data: {
file: file
}
})
.then(function(res) {
console.log('success: ===> ', res);
}, function(err) {
console.log('erroir: ===> ', err);
}, function() {
console.log('progress: ', arguments);
});
And in node environment I'm parsing the file and inserting the data in database. I don't want to close the connection. That's why I used "response.write". Here is my code snippet:
var path = req.files.file.path,
currentIndex = 0;
fs.readFile(path, 'utf8', function(err, data) {
if(err) {
// handle error
} else {
// making array (dataArray) from data
dataArray.forEach(function(eachData){
newEntry = new app.db.models.SomeCollection(eachData);
newEntry.save(function(err, data) {
if (currentIndex === dataArray.length) {
res.end('DONE!');
} else {
currentIndex++;
res.write(JSON.stringify({
total: dataArray.length,
done: currentIndex
}));
}
});
})
}
});
My question is how I will get the data I'm passing in "res.write"? I don't want to use socket for only this purpose. Am I missing something?
As already explained here:
response.send(msg) is equal to response.write(msg);response.end();
Which means, send can only be called once, write can be called many times, but you must call end yourself.
You are probably not receiving the response because response.end() is missing.
Once you end() your response you should be able to access the response data in your angular controller in the Upload.upload promise that is returned.
It's not like close a connection as you said. This is not a socket-ish like implementation (such as ws or socket.io). Once a request is made it should have a response even if it is to provide error details about that request (i.e. status 401, 403, 404, etc).
in your angular component:
...
constructor(private incrementalService: IncrementalService) {}
incrementalTest() { //activate with a button or whatnot
this.incrementalService.increment().subscribe( (result:any) => {
if (result.partialText) {
console.log(partialText); //do whatever you need to do with your partial results here!
}
})
}
your angular service:
import { HttpClient, HttpHeaders } from '#angular/common/http';
public class IncrementalService {
constructor(private http: HttpClient) {}
increment(): Observable<ArrayBuffer> {
const options = {
reportProgress: true,
responseType: 'text',
observe: 'events'
}
return this.http.request('get', 'http://someURL', { ...this.addRawHeaderOptions(), ...options});
}
private addRawHeaderOptions() {
const authHeaders = new HttpHeaders({
'Content-Type': 'application/json',
//authorization, Cache-Control: 'no-cache, Pragma:'no-cache', et al. }
return { headers: authHeaders }
}
}
Finally, your back-end service (this is express, but should work similarly for raw node):
async function(request, response) {
const increments = [ 1,2,3,4 ];
response.set('Content-Type', 'text/html');
for (const value of increments) { //contains async call - not switch-outable for a forEach.
response.write(`increment - ${value} `);
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
await delay(1000)
}
response.status(200).end()
}
browser console output when run:
increment - 1
increment - 1 increment - 2
increment - 1 increment - 2 increment - 3
increment - 1 increment - 2 increment - 3 increment - 4
!!Sorry for any typos - i had to transcribe this from a locked-down machine.