Downloading a file via Axios from Django - django

I'm trying to get filename from blob request using Axios and Django.
Django's view:
with open(full_file_path, 'rb') as f:
response = HttpResponse(f, content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
file_expr = "filename*=utf-8''{}".format(quote(file_name_with_ext))
response['Content-Disposition'] = 'attachment; {}'.format(file_expr)
return response
Frontend:
axios
.request({
url,
method,
responseType: 'blob',
})
.then(({ data }) => {
console.log(data);
const downloadUrl = window.URL.createObjectURL(new Blob([data]));
const link = document.createElement('a');
link.href = downloadUrl;
link.setAttribute('download', 'file.xlsx');
document.body.appendChild(link);
link.click();
link.remove();
});
But parameter 'data' doesn't contain headers to allow me to get filename.
Is there any solution?

instead of this .then(({ data }) => { Use this:.then(response =>{
and response.data is your data from response that you can see with console.log(response.data);
Hope it helps<3.

Related

Difference between fetch and postman's results

I have a simple login backend which has two routes login and get_user. After a user is logged in, a cookie is set such that it enables other routes like get_user. I tested this backend with Postman and after correct login, the cookie is set and get_user responds with user's data.
However, when I try to use fetch or axios in React and JS, I get problems. After I fetch login, I can see the cookie is sent, however, fetch get_user acts like cookie is not set at all.
I provided a minimal example to show that server side session somehow doesn't work with fetch:
Frontend:
<!DOCTYPE html>
<html>
<body>
<h1> Set value: </h1> <h2 id="set_value"></h2>
<h1> Get value: </h1> <h2 id="get_value"></h2>
</body>
<script>
// ..\..\Flask_general_framework\backend\venv\Scripts\Activate.ps1
async function Set_user_fetch()
{
// Get user
let user = await fetch('http://127.0.0.1:5000/set/gggg', {
'method': 'GET',
//credentials: 'include',
mode: 'cors',
credentials: "same-origin",
headers: {'Content-type': 'application/json', 'Accept': 'application/json',
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS},
'Access-Control-Allow-Headers': 'Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name',
}
})
user = await user.json();
console.log("await user:", user);
document.getElementById("set_value").innerHTML = user.value;
}
async function Get_user_fetch()
{
let user = await fetch('http://127.0.0.1:5000/get', {
'method': 'GET',
//credentials: 'include',
credentials: "same-origin",
mode: 'cors',
headers: {'Content-type': 'application/json', 'Accept': 'application/json',
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS},
'Access-Control-Allow-Headers': 'Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name',
}
})
user = await user.json();
console.log("await user:", user);
document.getElementById("get_value").innerHTML = user.value;
}
Set_user_fetch().then( () => {
Get_user_fetch();
});
</script>
</html>
Backend:
from re import I
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS
import redis
import datetime as dt
app = Flask(__name__)
CORS(app, supports_credentials=True)
app.config['SECRET_KEY'] = 'super secret key'
#app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_PERMANENT'] = True
#app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:9876')
app.config['PERMANENT_SESSION_LIFETIME'] = dt.timedelta(days=7).total_seconds()
server_session = Session()
server_session.init_app(app)
#app.route('/set/<value>', methods=['GET', 'POST'])
def set_value(value):
session['value'] = value
return {"value": value}
#app.route('/get', methods=['GET', 'POST'])
def get_value():
return {"value": session.get('value', 'None')}
app.run(host='127.0.0.1', port=5000, debug=True)
Server Side
In order to support cross-site cookies in modern browsers, you need to configure your server to use the Set-Cookie attribute SameSite=None (see Flask-specific example here). Unfortunately, this also requires the Secure attribute and an HTTPS enabled server.
For local development, you can get around this by serving your client and server on the same hostname, eg localhost with SameSite=Lax (or omitting SameSite which defaults to "Lax").
By "same hostname" I mean that if your frontend code makes requests to localhost:5000, you should open it in your browser at http://localhost:<frontend-port>. Similarly, if you make requests to 127.0.0.1:5000, you should open it at http://127.0.0.1:<frontend-port>.
Lax same-site restrictions don't come into play if only the ports differ.
Client Side
You have a few problems here...
You're sending headers in your request that do not belong there. Access-Control-Allow-* are response headers that must come from the server.
You set credentials to same-origin but are sending a request to a different host. To use cookies, you need to set credentials to "include". See Request.credentials.
You have no error handling for non-successful requests.
You're also setting a lot of redundant properties and headers and can trim down your code significantly.
async function Set_user_fetch() {
const res = await fetch("http://127.0.0.1:5000/set/gggg", {
credentials: "include",
});
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
const user = await res.json();
console.log("await user:", user);
document.getElementById("set_value").innerHTML = user.value;
}
async function Get_user_fetch() {
const res = await fetch("http://127.0.0.1:5000/get", {
credentials: "include",
});
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
const user = await res.json();
console.log("await user:", user);
document.getElementById("get_value").innerHTML = user.value;
}
If you were using Axios, you would set the withCredentials config to true
axios.get("http://127.0.0.1:5000/set/gggg", {
withCredentials: true
});

Cannot get values from request in Django - Empty QueryDict

I’m new to ViewSets and am trying to get the values sent from the front-end fetch method to Django’s request object in the create function. I don’t know whether it’s just a simple syntax error or whether the data isn’t being sent properly from the front-end, but I think it’s a back-end issue.
The stringified data in the post method seems to log correctly at the front-end like with this test:
{"title":"test","type":"landing","slug":"","notes":""}
Printing variables in the ViewSet’s create function however shows these:
print(request.POST["title"]) # fails with keyerror: 'title' MultiValueDictKeyError(key) django.utils.datastructures.MultiValueDictKeyError: 'title'
print(request["title"]) # fails with TypeError: 'Request' object is not subscriptable
print(request.POST.get("title", “test”)) # fails as print test
print(request.POST.get("title")) # fails as it just returns None
print(request.get("title")) # fails with AttributeError: 'WSGIRequest' object has no attribute 'get'
print(self.request.query_params.get("title", None)) # prints None
print(self.request.query_params) # prints empty QueryDict: <QueryDict: {}>
Here’s the create function:
class PagesViewSet(viewsets.ViewSet):
def create(self, request):
# printing went here
page = Page.objects.create(
title="testing", type="other", slug="test-slug", notes="test notes"
)
serializer = PageSerializer(page)
return Response(serializer.data)
I’ve just chucked in demo data inside the page create method to ensure it works, which it does, and now want to use the real data which should be in the request.
Does anyone know what might be the issue here?
For visibility, here’s the front-end API-request function:
const createPage = async (data: CreatePageFormInputs) => {
console.log('stringified: ', JSON.stringify(data)); // logs correctly
const res = await fetchCreatePage('http://localhost:8000/api/pages/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
};
Maybe irrelevant but in case you're wondering what fetchCreatePage is, it's just this part of a custom react hook:
const fetchCreatePage: FetchDataFn = async (url, options?) => {
const setFailed = () => {
setFetchError(true);
setLoading(false);
};
const setSuccess = (data: any) => {
setData(data);
setLoading(false);
};
try {
setLoading(true);
const res = await fetch(url, options);
if (!res.ok) {
console.log('Error detected. Returning...');
setFailed();
return;
}
if (res.status === 204) {
setSuccess({
success: true,
info: 'Item successfully deleted',
});
return;
}
const data = await res.json();
setSuccess(data);
} catch (e) {
setFailed();
}
}
I assume the POST method is correct. Any help would be appreciated, thanks.
You wrote the data as body of the request in a JSON format. You thus should decode the JSON format to a dictionary with:
import json
data = json.loads(request.body)
print(data['title'])
If you are using a request from the Django REST framework, you work with request.data:
import json
print(request.data['title'])
request.data will look for POST parameters and a JSON body.

Not able to connect to the translation API

url: https://translatorappeagle.cognitiveservices.azure.com/
I'm also using my key from my resources page. I get a an error 404: not found. I copied the code from the getting started section for the translation API.
Please let me know what to do.
import os, requests, uuid, json
key_var_name = 'TRANSLATOR_TEXT_SUBSCRIPTION_KEY'
if not key_var_name in os.environ:
raise Exception('Please set/export the environment variable: {}'.format(key_var_name))
subscription_key = os.environ[key_var_name]
endpoint_var_name = 'TRANSLATOR_TEXT_ENDPOINT'
if not endpoint_var_name in os.environ:
raise Exception('Please set/export the environment variable: {}'.format(endpoint_var_name))
endpoint = os.environ[endpoint_var_name]
# If you encounter any issues with the base_url or path, make sure
# that you are using the latest endpoint: https://learn.microsoft.com/azure/cognitive-services/translator/reference/v3-0-translate
path = '/translate?api-version=3.0'
params = '&to=de&to=it'
constructed_url = endpoint + path + params
headers = {
'Ocp-Apim-Subscription-Key': subscription_key,
'Content-type': 'application/json',
'X-ClientTraceId': str(uuid.uuid4())
}
# You can pass more than one object in body.
body = [{
'text' : 'Hello World!'
}]
request = requests.post(constructed_url, headers=headers, json=body)
response = request.json()
print(json.dumps(response, sort_keys=True, indent=4, separators=(',', ': ')))

Django + Axios: File download not working in Firefox

I am using Axios to send my Django backend information, which in turns creates a file and sends it back to the front end. The code I have below works great in Safari and Chrome. However, the file does not download in firefox. BTW, no errors show up in the firefox console, or in Django.
Axios
axios({
method:'post',
url:'/api/downloadDoc',
responseType:'blob',
data: params,
})
.then(response => {
let blob = new Blob([response.data], {type: 'application/force-download'})
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = "YourPaper.docx"
link.click()
})
.catch(e => {
console.log(e)
})
Django View Sending File
def downloadPaper(request):
if request.method == "POST":
#Recieve info through post and create document
#variable path is path to newly created document
wrapper = FileWrapper(open(path, 'rb'))
response = HttpResponse(wrapper, content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
response['Content-Disposition'] = 'attachment; filename=' + 'yourpaper.docx'
response['Content-Length'] = os.path.getsize(path)
return response
return HttpResponse(status=405)
I am hoping someone knows what could be causing firefox to not download.
Thanks!
fetch("/api/admin/plugin/download/" + id , {
method: 'GET',
headers: new Headers({
"Authorization": this.token,
"Allow-Control-Allow-Origin": "*",
"Content-Type" : "application/octet-stream"
}),
}).then(response => {
response.blob().then(blob => {
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
// must add <a> to body, then it works
document.body.appendChild(a);
a.style.display = 'none';
a.href = url;
a.download = decodeURI(response.headers.get("filename"));
a.click();
document.body.removeChild(a);
});
})

Anonymus user Django + Angular 2

i am using django + angular 2
i am using rest_framework_jwt with a url like this
url(r'^api/api-token-auth/', obtain_jwt_token),
url(r'^api/settings/?$', views.SettingsValues.as_view()),
My view is
class SettingsValues(generics.ListAPIView):
serializer_class = SettingsSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_queryset(self):
queryset = Settings.objects.all()
queryset = queryset.filter(user=self.request.user.id)
print self.request.user
return queryset
My service is:
getSettings() : Promise <SettingsValues> {
return this.http.get('/api/settings', { headers: this.headers })
.toPromise()
.then(response => response.json() as SettingsValues);
}
My login is working fine, but i cannot return the settings from django..
The print inside def get_queryset shows AnonymousUser.
Any idea what i am doing wrong ?
EDIT
private headers = new Headers({
'Content-Type': 'application/json',
'Accept': 'application/json',
});
After obtaining the token from server, you have to save it and send in the header of subsequent api calls. Something like this:
getSettings() : Promise <SettingsValues> {
this.headers.append('Authorization', 'JWT ' + token);
return this.http.get('/api/settings', { headers: this.headers })
.toPromise()
.then(response => response.json() as SettingsValues);
}
In Angular 1.x.x, i can add this to cache Authorization Header for all subsequent api calls.
$http.defaults.headers.common.Authorization = 'JWT ' + token`;
In Angular 2, I am not sure how to cache it for all but take a look at this answer