strapi + nuxt search by slug - axios

I'm modifing my strapi and nuxt site to find my post by slug instead of id
https://forum.strapi.io/t/strapi-v4-search-by-slug-instead-id/13469/21
this is working, but when i do my axios request in _slug.vue the url is correct but it doesn't save the info in my response variable, in my browser console says can't read data from undefined, checked the url is correct and in my strapi console i have everything ok.
async asyncData({ route, i18n, title, description }) {
try {
const locale = i18n.locale;
const qs = require("qs");
const query = qs.stringify(
{
populate: "*",
publicationState: "live",
locale: [locale],
},
{
encodeValuesOnly: true,
}
);
const response = await axios.get(
`http://localhost:1337/api/posts/${route.params.slug}/?&` +
query
);
console-log(response)
let post = response.data;
if (locale != post.data.attributes.locale) {
post.data.attributes.localizations.data.forEach((element) => {
if (element.attributes.locale === locale) {
title = element.attributes.title;
description = element.attributes.description;
}
});
} else {
title = post.data.attributes.title;
description = post.data.attributes.description;
}
return { post, title, description };
} catch (error) {
this.error = error.response.data.error.details.errors;
}
},
Any ideas? thanks

Related

Swift 5/iOS 13+ – Google Cloud Functions won't let me add a value

I'm using Google Cloud Functions (GCF) to schedule posts on Instagram (an app similar to Hootsuite). I just integrated local notifications and I want to add the ID (generated automatically by my "notificationManager") to the post (data sent to Google Cloud).
Basically, the way that it works is the app sends the data to GCF and GCF is responsible for adding the elements to Firestore Database, but after many failed attempts, I cannot seem to figure out how to add an additional value. My goal is to add the element "notificationIdentifier", but for some reason, GCF won't register it (I can't even see it in the logs!).
Here's what's in GCF:
/* eslint-disable max-len */
/* eslint-disable */
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const bodyParser = require("body-parser");
const axios = require("axios");
const FACEBOOK_GRAPH_API_VERSION = "v11.0";
const FACEBOOK_APP_ID = Undisclosed APP_ID;
const FACEBOOK_APP_SECRET = Undisclosed APP_SECRET;
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
admin.initializeApp();
const app = express();
const main = express();
const db = admin.firestore();
/**
* publish Instagram Media Object.
* #param {string} container_id instagram media object creation id.
* #param {string} accessToken user access token.
* #return {Promise} Returns container status
*/
async function getContainerStatus(container_id, accessToken) {
let status = "IN PROGRESS";
let response;
try {
response = await axios.get(
`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/${container_id}`,
{
params: {
access_token: accessToken,
fields: "status_code",
},
}
);
} catch (error) {
console.log(error);
return "ERROR";
}
// console.log(response.data.status_code, "status");
status = response.data.status_code;
return status;
}
/**
* Get LongLive Token Expire in 60 days.
* #param {string} accessToken user access token.
* #return {Promise} Return Long Live token.
*/
function getLongLiveToken(accessToken) {
return new Promise((resolve, reject) => {
axios
.get(
`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/oauth/access_token`,
{
params: {
grant_type: "fb_exchange_token",
client_id: FACEBOOK_APP_ID,
client_secret: FACEBOOK_APP_SECRET,
fb_exchange_token: accessToken,
},
}
)
.then((response) => {
resolve(response.data.access_token);
})
.catch((error) => {
reject(error);
});
});
}
/**
* Get Facebook Pages.
* #param {string} accessToken user access token.
* #return {Promise} Returns the facebook pages result.
*/
function getFacebookPages(accessToken) {
return new Promise((resolve, reject) => {
axios
.get(
`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/me/accounts`,
{
params: {
access_token: accessToken,
},
}
)
.then((response) => {
const data = response.data.data;
resolve(data);
})
.catch((error) => {
reject(error);
});
});
}
/**
* Get Instagram Account From Facebook Pages.
* #param {string} accessToken user access token.
* #param {string} pageId Page ID.
* #return {Promise} Returns the Instagram Account Id.
*/
function getInstagramAccountId(accessToken, pageId) {
return new Promise((resolve, reject) => {
axios
.get(
`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/${pageId}`,
{
params: {
access_token: accessToken,
fields: "instagram_business_account",
},
}
)
.then((response) => {
if (response.data.instagram_business_account) {
resolve({
id: response.data.instagram_business_account.id,
});
} else {
resolve({ error: "No instagram Business Account" });
}
})
.catch((error) => {
reject(error);
});
});
}
/**
* Get Facebook Profile.
* #param {string} accessToken user access token.
* #return {Promise} Returns the Facebook Account Profile.
*/
function getFacebookProfile(accessToken) {
return new Promise((resolve, reject) => {
axios
.get(`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/me`, {
params: {
access_token: accessToken,
fields: "name,picture,email",
},
})
.then((response) => {
resolve({
picture: response.data.picture.data.url,
name: response.data.name,
email: response.data.email,
facebookUserId: response.data.id,
});
})
.catch((error) => {
reject(error);
});
});
}
/**
* Get Instagram Profile.
* #param {string} accessToken user access token.
* #param {string} instagramAccountId Instagram Account ID.
* #return {Promise} Returns the Instagram Account Profile.
*/
function getInstagramProfile(accessToken, instagramAccountId) {
return new Promise((resolve, reject) => {
axios
.get(
`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/${instagramAccountId}`,
{
params: {
access_token: accessToken,
fields: "name,username,profile_picture_url",
},
}
)
.then((response) => {
resolve(response.data);
})
.catch((error) => {
reject(error);
});
});
}
/**
* Create Instagram Media Object.
* #param {string} accessToken user access token.
* #param {string} instagramAccountId Instagram Account ID.
* #param {string} caption caption.
* #param {string} mediaType Instagram Account ID.
* #param {string} mediaUrl media url
* #return {Promise} Returns created Instagram Media ID.
*/
function createInstagramMedia(
accessToken,
instagramAccountId,
caption,
mediaType,
mediaUrl,
tags,
) {
return new Promise((resolve, reject) => {
const params = {
access_token: accessToken,
caption: caption,
};
let user_tags = [];
if(tags != null) {
for (let i = 0; i < tags.length; i++) {
const user_tag = {
"username": tags[i],
"x": Math.random(),
"y": Math.random()
}
user_tags.push(user_tag);
}
}
if (mediaType == "PICTURE") {
params["image_url"] = mediaUrl;
if(tags != null) {
params["user_tags"] = user_tags;
}
} else if (mediaType == "VIDEO") {
params["video_url"] = mediaUrl;
params["media_type"] = "VIDEO";
} else {
reject({ message: "Unknow media!" });
return;
}
axios
.post(
`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/${instagramAccountId}/media`,
params
)
.then(async (response) => {
const container_id = response.data.id;
let container_status = "IN_PROGRESS";
while (container_status == "IN_PROGRESS") {
container_status = await getContainerStatus(
container_id,
accessToken
);
console.log(container_status, "Container status");
}
// resolve(response.data);
if (container_status == "ERROR") {
reject({ error: "Container error!" });
} else {
resolve(response.data);
}
})
.catch((error) => {
console.log(error);
reject(error);
});
});
}
/**
* publish Instagram Media Object.
* #param {string} accessToken user access token.
* #param {string} instagramAcctId instagram media object creation id.
* #param {string} mediaObjectId instagram media object creation id.
* #return {Promise} Returns the Instagram Account Profile.
*/
function publishMedia(accessToken, instagramAcctId, mediaObjectId) {
console.log(accessToken, " --> ", instagramAcctId, " --> ", mediaObjectId);
return new Promise((resolve, reject) => {
axios
.post(
`https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/${instagramAcctId}/media_publish`,
{
access_token: accessToken,
creation_id: mediaObjectId,
}
)
.then((response) => {
resolve(response.data);
})
.catch((error) => {
console.log(error);
reject(error);
});
});
}
/**
* Facebook Login with uuid
* body params {uuid, accessToken}
*/
app.post("/register", async (req, res) => {
const uuid = req.body.uuid;
const accessToken = req.body.accessToken;
const instagramAccts = [];
let longLiveToken, userData, pages;
try {
longLiveToken = await getLongLiveToken(accessToken);
} catch (error) {
console.log(error);
return res.status(400).json({
error: "Failed to create LongLive Token",
});
}
try {
userData = await getFacebookProfile(accessToken);
} catch (error) {
console.log(error);
return res.status(400).json({
error: "Failed to get Facebook profile",
});
}
try {
pages = await getFacebookPages(accessToken);
} catch (error) {
console.log(error);
return res.status(400).json({
error: "Failed to get Facebook pages",
});
}
if (pages.length > 0) {
for (let i = 0; i < pages.length; i++) {
try {
const data = await getInstagramAccountId(accessToken, pages[i].id);
if (!data.error) {
const igProfile = await getInstagramProfile(accessToken, data.id);
// const tags = await db
// .collection("tags")
// .where("tag", igProfile["username"])
// .get();
// if (tags.docs.length < 1) {
// await db.collection("tags").add({
// tag: igProfile["username"],
// });
// }
igProfile["isActive"] = true;
if (i == 0) {
igProfile["isPrimary"] = true;
} else {
igProfile["isPrimary"] = false;
}
instagramAccts.push(igProfile);
}
} catch (error) {
console.log(error);
return res.status(400).json({
error: "Failed to get Instagram accounts",
});
}
}
}
try {
await db.collection("users").doc(uuid).set({
longLiveToken: longLiveToken,
picture: userData.picture,
name: userData.name,
email: userData.email,
facebookUserId: userData.facebookUserId,
accessToken: accessToken,
uuid: uuid,
instagramAccts: instagramAccts,
});
} catch (error) {
console.log(error);
return res.status(400).json({
error: "Failed to save user info to firestore",
});
}
return res.status(200).json({
message: "Success",
instagramAccts: instagramAccts,
});
});
app.post("/getInstagramAccounts", async (req, res) => {
const uuid = req.body.uuid;
const user = await db.collection("users").doc(uuid).get();
const userData = user.data();
const accessToken = userData.longLiveToken;
const instagramAccts = [];
let pages;
try {
pages = await getFacebookPages(accessToken);
} catch (error) {
console.log(error);
return res.status(400).json({
error: "Failed to get Facebook pages",
});
}
if (pages.length > 0) {
for (let i = 0; i < pages.length; i++) {
try {
const data = await getInstagramAccountId(accessToken, pages[i].id);
if (!data.error) {
const igProfile = await getInstagramProfile(accessToken, data.id);
if (i == 0) {
igProfile["isActive"] = true;
} else {
igProfile["isActive"] = false;
}
instagramAccts.push(igProfile);
}
} catch (error) {
console.log(error);
return res.status(400).json({
error: "Failed to get Instagram accounts",
});
}
}
}
try {
await db
.collection("users")
.doc(uuid)
.update({ instagramAccts: instagramAccts });
} catch (error) {
console.log(error);
return res.status(500).json({
error: "Failed to update instagram accounts",
});
}
return res.status(200).json({ instagramAccts: instagramAccts });
});
/**
* Schedule Instagram Media Object to publish.
* body params {time, mediaType, uuid, media, instagramAcctId}
*/
app.post("/schedule", async (req, res) => {
const uuid = req.body.uuid;
const time = req.body.time;
const mediaType = req.body.mediaType;
const media = req.body.media; // url string array
const tags = req.body.tags; // array of strings
const longitude = req.body.longitude;
const latitude = req.body.latitude;
const instagramAcctId = req.body.instagramAcctId;
const caption = req.body.caption;
const thumbnail = req.body.thumbnail;
const timeStamp = req.body.timeStamp;
const notificationIdentifier = req.notificationIdentifier;
try {
const postData = await db.collection("posts").add({
uuid: uuid,
time: time,
mediaType: mediaType,
media: media,
instagramAcctId: instagramAcctId,
published: false,
caption: caption,
tags: tags ? tags : null,
thumbnail: thumbnail ? thumbnail : null,
longitude: longitude,
latitude: latitude,
timeStamp: timeStamp,
notificationIdentifier: notificationIdentifier ? notificationIdentifier : null,
});
const ref = await postData.get();
await db.collection("posts").doc(ref.id).update({ id: ref.id });
res.status(200).json({
message: "Success!",
});
} catch (error) {
console.log(error);
res.status(500).json(error.message);
}
});
app.post("/update-schedule", async (req, res) => {
const id = req.body.id;
const time = req.body.time;
const tags = req.body.tags; // array of strings
const longitude = req.body.longitude;
const latitude = req.body.latitude;
const instagramAcctId = req.body.instagramAcctId;
const caption = req.body.caption;
const timeStamp = req.body.timeStamp;
const notificationIdentifier = req.body.notificationIdentifier;
const updateData = {};
if (time) {
updateData["time"] = time;
}
if (tags) {
updateData["tags"] = tags;
}
if (longitude) {
updateData["longitude"] = longitude;
}
if (latitude) {
updateData["latitude"] = latitude;
}
if (instagramAcctId) {
updateData["instagramAcctId"] = instagramAcctId;
}
if (caption) {
updateData["caption"] = caption;
}
if (timeStamp) {
updateData["timeStamp"] = timeStamp;
}
if (notificationID) {
updateData["notificationIdentifier"] = notificationIdentifier;
}
try {
await db.collection("posts").doc(id).update(updateData);
res.status(200).json({
message: "Update success!",
});
} catch (error) {
console.log(error);
res.status(500).json(error.message);
}
});
app.post("/remove-schedule", async (req, res) => {
const id = req.body.id;
try {
const postData = await db.collection("posts").doc(id).get();
if (postData.exists) {
await db.collection("posts").doc(id).delete();
res.status(200).json({
message: "Remove success!",
});
} else {
res.status(400).json({
message: "There is no such post!",
});
}
} catch (error) {
console.log(error);
res.status(500).json(error.message);
}
});
app.post("/get-posts-by-date", async (req, res) => {
const uuid = req.body.uuid;
let posts_by_date = [];
try {
const posts = await db
.collection("posts")
.where("published", "==", false)
.where("uuid", "==", uuid)
.orderBy("time")
.get();
for (let i = 0; i < posts.docs.length; i++) {
const postData = posts.docs[i].data();
console.log(new Date(postData["time"]).toLocaleDateString());
const localDate = new Date(postData["time"]).toLocaleDateString();
if (posts_by_date[localDate]) {
posts_by_date[localDate].push(postData);
} else {
posts_by_date[localDate] = [];
posts_by_date[localDate].push(postData);
}
}
console.log(posts_by_date, "posts by date");
return res.status(200).json({
posts: posts_by_date,
});
} catch (error) {
console.log(error);
return res.status(500).json(error.message);
}
});
exports.scheduledFunction = functions.pubsub
.schedule("* * * * *")
.onRun((context) => {
console.log("This will be run every 1 minute!");
db.collection("posts")
.where("published", "==", false)
.get()
.then((querySnapshot) => {
querySnapshot.forEach(async (doc) => {
console.log(doc.id, " => ", doc.data());
const post = doc.data();
const now = new Date();
const publishDate = new Date(post.time);
if (publishDate <= now) {
//publish media object.
const uuid = post.uuid;
const userRef = await db.collection("users").doc(uuid).get();
const user = userRef.data();
const longLiveToken = user.longLiveToken;
const instagramAcctId = post.instagramAcctId;
const medias = post.media;
//const notificationIdentifier = post.notificationIdentifier;
for (let i = 0; i < medias.length; i++) {
try {
const mediaObjects = await createInstagramMedia(
longLiveToken,
instagramAcctId,
post.caption,
post.mediaType,
medias[i],
post.tags,
// notificationIdentifier
);
const mediaObjectId = mediaObjects.id;
await publishMedia(
longLiveToken,
instagramAcctId,
mediaObjectId
);
} catch (error) {
console.log(error);
return;
}
}
await db
.collection("posts")
.doc(doc.id)
.update({ published: true });
}
});
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
return null;
});
main.use("/v1", app);
main.use(bodyParser.json());
main.use(bodyParser.urlencoded({ extended: true }));
exports.api = functions.https.onRequest(main);
and this is how the data is sent:
private func schedulePost(uuid: String, time: Date, mediaType: String, media: [String], caption: String, tags:[String], location: CLLocation?, thumbImageUrl: String) {
var newCap: [String] = []
newCap.append(caption)
newCap.insert(contentsOf: tags, at: newCap.endIndex)
let newCaption = newCap.joined(separator: " ")
print (newCaption)
AuthManager.shared.loadUser()
guard let instagramAccountId = AuthManager.shared.currentUser?.id else {return}
let timeStamp = NSDate().timeIntervalSince1970
if !self.TrueStory {
StoryManager().addNewTask("POST", "Post", time, self.ImageURL)
print("Scheduled Post" + self.ImageURL)
// let notificationID = notificationIdentifier
//tags.insert(caption, at: tags.firstIndex)
let param = ["uuid": uuid, "time": time, "mediaType": mediaType, "media": media, "instagramAcctId": instagramAccountId, "caption": newCaption, "tags": "", "latitude": location?.coordinate.latitude ?? "", "longitude":location?.coordinate.longitude ?? "", "thumbnail":thumbImageUrl, "timeStamp":timeStamp, "notificationIdentifier":notificationIdentifier] as [String : Any]
print ("The PARAMS are: ")
print (param)
ServerApi.shared.scheduleIGPosts(param: param, success: {response in
print(response)
ProgressHUD.dismiss()
AppManager.shared.isPostScheduled = true
AppManager.shared.showNext()
// NotificationCenter.default.post(name: .PostWasSuccessfullyScheduled, object: nil, userInfo: ["posted": true])
}, failure: {(error) in
print(error)
self.showAlert(error.description)
ProgressHUD.dismiss()
})
} else if self.TrueStory {
StoryManager().addNewTask("STORY", "Story", time, self.ImageURL)
print("Scheduled Story" + self.ImageURL)
// let notificationID = notificationIdentifier
let param = ["uuid": uuid, "time": time, "mediaType": mediaType, "media": media, "instagramAcctId": instagramAccountId, "caption": caption, "tags": "", "latitude": location?.coordinate.latitude ?? "", "longitude":location?.coordinate.longitude ?? "", "thumbnail":thumbImageUrl, "timeStamp":timeStamp, "notificationIdentifier":notificationIdentifier] as [String : Any]
// "notificationID":notificationIdentifier
ServerApi.shared.scheduleIGPosts(param: param, success: {response in
print(response)
ProgressHUD.dismiss()
AppManager.shared.isPostScheduled = true
AppManager.shared.showNext()
// NotificationCenter.default.post(name: .PostWasSuccessfullyScheduled, object: nil, userInfo: ["posted": true])
}, failure: {(error) in
print(error)
self.showAlert(error.description)
ProgressHUD.dismiss()
})
}
}
And the "ScheduleIGPost" function for your entertainment:
import Foundation
import SwiftyJSON
import Alamofire
struct AppUrls {
static let baseUrl = URL that I wont disclose
static let registerIGAccounts = baseUrl + "register"
static let scheduleIGPosts = baseUrl + "schedule"
static let updateIGPosts = baseUrl + "update-schedule"
static let removeIGPosts = baseUrl + "remove-schedule"
}
class ServerApi {
static let shared = ServerApi()
func scheduleIGPosts(param: [String: Any], success: #escaping(JSON) -> Void, failure: #escaping(JSON) -> Void) {
ApiWrapper.requestPOSTURLWithoutToken(AppUrls.scheduleIGPosts, params: param, success: {(response) in
print(JSON(response))
success(JSON(response))
}, failure: { (error) in
let err = JSON(error)
print(err)
failure(err)
})
}
I did check and see if "param" included "notificationIdentifier" when sending to GCF and it does, but if I check the logs in GCF, I can see everything that's supposed to be except for "notificationIdentifier". And when I replace, for example, "tags" with the "notificationIdentifier", then it works.
I'm unsure if Firestore/Firebase has a limit of elements or not and why this behaviour is happening. To be honest, I couldn't find much information online.
Feel free to ask any questions if I was unclear, as I tend to be!
This was solved quite simply, the app is using a Cloud Function called "API" which is used before accessing the "Schedule" function. So changed the values (or rather added the values) and everything is working fine now.

Nuxtjs meta not working in vercel deployment. but working in local dev server

when calling API in asyncData with static parameter meta working in both local and vercel server. but if parameter make dynamic live server meta not working
async asyncData(context) {
let pageinfo = ''
try {
console.log(context.query.id)
const url_key = 'cocomama-coconut-water-250ml'
const custId = ''
const guestId = ''
const API_HEADER = Config.ADMIN_API_HEADER.headers
API_HEADER.vendorUrlKey = 'centrealbazaarindialtdkaloor'
pageinfo = await fetch(
`${
Config.BASE_URL
}ProductDetails?urlKey=${url_key}&custId=${custId}&guestId=${guestId}`,
{
method: 'GET',
headers: API_HEADER
}
).then(res => res.json())
} catch (error) {
console.log(error)
}
pageinfo = pageinfo.Data.ProdDetails
return { pageinfo }
}

Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in

I am getting ** Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in 5f8425a33a14f026f80133ed** where 5f8425a33a14f026f80133ed is the id passed to the axios url
I want to display the services based on the user id. My url works perfectly in postman but when i access it from the veux store it gives an error.
services.js (store)
import axios from 'axios';
const state = {
services : {},
status: '',
error: null
};
const getters = {
services : state => { return state.services }
};
const actions = {
async fetchServices({commit}, userId) {
let res = await axios.get('http://localhost:5000/api/services/displayUser' , userId)
commit('setProducts', res.data)
return res;
}
};
const mutations = {
setProducts (state, items) {
state.services= items
},
};
export default {
state,
actions,
mutations,
getters
};
This is how I am calling the action :
computed: {
...mapGetters(["services"]),
},
methods: {
...mapActions(["fetchServices"]),
getData(){
this.fetchServices(this.user._id)
},
},
async created() {
await this.getProfile();
await this.getData();
}
The axios route is defined as
router.get('/displayUser', (req,res) => {
const query = user = req.body ;
Services.find(query)
.exec((err, services) => res.json(services))
})
the error screenshot :
Error screenshot
GET request should not have a body. Either use query params, indicate an id in a path, or use POST request.
In case of query params this may look like this:
let res = await axios.get('http://localhost:5000/api/services/displayUser' , { params: { userId })
router.get('/displayUser', (req,res) => {
const query = user = req.query;
Services.find(query)
.exec((err, services) => res.json(services))
})
This worked for me too:
In front end: Vue Js
let res = axios.get("http://localhost:3000/api/v1/role/getRoleByName",
{ params: { roleName: "name of role you want to send as params" },
});
In back end: Node Js
router.get('/getRoleByName', (req,res)=>{
let roleName = req.query.roleName;
roleModule.getRoleByName(roleName).then(data =>{
response.json(res,data)
}
).catch(err=> {
response.badRequest(res, err);
})
});
it's a silly mistake axios.post req.
async addTodo({ commit }, title) {
try {
const res = await axios.post(BASE_URL, { title, complete: false });
commit("newTodo", res.data);
} catch (err) {
console.log(err.message);
}
},

Angular 6 Downloading file from rest api

I have my REST API where I put my pdf file, now I want my angular app to download it on click via my web browser but I got HttpErrorResponse
"Unexpected token % in JSON at position 0"
"SyntaxError: Unexpected token % in JSON at position 0↵ at JSON.parse (
this is my endpoint
#GetMapping("/help/pdf2")
public ResponseEntity<InputStreamResource> getPdf2(){
Resource resource = new ClassPathResource("/pdf-sample.pdf");
long r = 0;
InputStream is=null;
try {
is = resource.getInputStream();
r = resource.contentLength();
} catch (IOException e) {
e.printStackTrace();
}
return ResponseEntity.ok().contentLength(r)
.contentType(MediaType.parseMediaType("application/pdf"))
.body(new InputStreamResource(is));
}
this is my service
getPdf() {
this.authKey = localStorage.getItem('jwt_token');
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/pdf',
'Authorization' : this.authKey,
responseType : 'blob',
Accept : 'application/pdf',
observe : 'response'
})
};
return this.http
.get("http://localhost:9989/api/download/help/pdf2", httpOptions);
}
and invocation
this.downloadService.getPdf()
.subscribe((resultBlob: Blob) => {
var downloadURL = URL.createObjectURL(resultBlob);
window.open(downloadURL);});
I resolved it as follows:
// header.component.ts
this.downloadService.getPdf().subscribe((data) => {
this.blob = new Blob([data], {type: 'application/pdf'});
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement('a');
link.href = downloadURL;
link.download = "help.pdf";
link.click();
});
//download.service.ts
getPdf() {
const httpOptions = {
responseType: 'blob' as 'json')
};
return this.http.get(`${this.BASE_URL}/help/pdf`, httpOptions);
}
I solved the issue in this way (please note that I have merged multiple solutions found on stack overflow, but I cannot find the references. Feel free to add them in the comments).
In My service I have:
public getPDF(): Observable<Blob> {
//const options = { responseType: 'blob' }; there is no use of this
let uri = '/my/uri';
// this.http refers to HttpClient. Note here that you cannot use the generic get<Blob> as it does not compile: instead you "choose" the appropriate API in this way.
return this.http.get(uri, { responseType: 'blob' });
}
In the component, I have (this is the part merged from multiple answers):
public showPDF(fileName: string): void {
this.myService.getPDF()
.subscribe(x => {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
var newBlob = new Blob([x], { type: "application/pdf" });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob, fileName);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const data = window.URL.createObjectURL(newBlob);
var link = document.createElement('a');
link.href = data;
link.download = fileName;
// this is necessary as link.click() does not work on the latest firefox
link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));
setTimeout(function () {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(data);
link.remove();
}, 100);
});
}
The code above works in IE, Edge, Chrome and Firefox. However, I don't really like it, as my component is pulluted with browser specific stuff which will surely change over time.
For Angular 12+, I came up with something like this:
this.ApiService
.getFileFromApi()
.pipe(take(1))
.subscribe((response) => {
const downloadLink = document.createElement('a');
downloadLink.href = URL.createObjectURL(new Blob([response.body], { type: response.body.type }));
const contentDisposition = response.headers.get('content-disposition');
const fileName = contentDisposition.split(';')[1].split('filename')[1].split('=')[1].trim();
downloadLink.download = fileName;
downloadLink.click();
});
The subscribe is on a simple get() with the Angular HttpClient.
// api-service.ts
getFileFromApi(url: string): Observable<HttpResponse<Blob>> {
return this.httpClient.get<Blob>(this.baseApiUrl + url, { observe: 'response', responseType: 'blob' as 'json'});
}
You can do it with angular directives:
#Directive({
selector: '[downloadInvoice]',
exportAs: 'downloadInvoice',
})
export class DownloadInvoiceDirective implements OnDestroy {
#Input() orderNumber: string;
private destroy$: Subject<void> = new Subject<void>();
_loading = false;
constructor(private ref: ElementRef, private api: Api) {}
#HostListener('click')
onClick(): void {
this._loading = true;
this.api.downloadInvoice(this.orderNumber)
.pipe(
takeUntil(this.destroy$),
map(response => new Blob([response], { type: 'application/pdf' })),
)
.subscribe((pdf: Blob) => {
this.ref.nativeElement.href = window.URL.createObjectURL(pdf);
this.ref.nativeElement.click();
});
}
// your loading custom class
#HostBinding('class.btn-loading') get loading() {
return this._loading;
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
In the template:
<a
downloadInvoice
[orderNumber]="order.number"
class="btn-show-invoice"
>
Show invoice
</a>
My answer is based on #Yennefer's, but I wanted to use the file name from the server since I didn't have it in my FE. I used the Content-Disposition header to transmit this, since that is what the browser uses for a direct download.
First, I needed access to the headers from the request (notice the get method options object):
public getFile(): Observable<HttpResponse<Blob>> {
let uri = '/my/uri';
return this.http.get(uri, { responseType: 'blob', observe: 'response' });
}
Next, I needed to extract the file name from the header.
public getFileName(res: HttpResponse<any>): string {
const disposition = res.headers.get('Content-Disposition');
if (!disposition) {
// either the disposition was not sent, or is not accessible
// (see CORS Access-Control-Expose-Headers)
return null;
}
const utf8FilenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:; |$)/;
const asciiFilenameRegex = /filename=(["'])(.*?[^\\])\1(?:; |$)/;
let fileName: string = null;
if (utf8FilenameRegex.test(disposition)) {
fileName = decodeURIComponent(utf8FilenameRegex.exec(disposition)[1]);
} else {
const matches = asciiFilenameRegex.exec(disposition);
if (matches != null && matches[2]) {
fileName = matches[2];
}
}
return fileName;
}
This method checks for both ascii and utf-8 encoded file names, prefering utf-8.
Once I have the file name, I can update the download property of the link object (in #Yennifer's answer, that's the lines link.download = 'FileName.ext' and window.navigator.msSaveOrOpenBlob(newBlob, 'FileName.ext');)
A couple of notes on this code:
Content-Disposition is not in the default CORS whitelist, so it may not be accessible from the response object based on the your server's configuration. If this is the case, in the response server, set the header Access-Control-Expose-Headers to include Content-Disposition.
Some browsers will further clean up file names. My version of chrome seems to replace : and " with underscores. I'm sure there are others but that's out of scope.
//Step: 1
//Base Service
this.getPDF() {
return this.http.get(environment.baseUrl + apiUrl, {
responseType: 'blob',
headers: new HttpHeaders({
'Access-Control-Allow-Origin': '*',
'Authorization': localStorage.getItem('AccessToken') || ''
})
});
}
//Step: 2
//downloadService
getReceipt() {
return new Promise((resolve, reject) => {
try {
// {
const apiName = 'js/getReceipt/type/10/id/2';
this.getPDF(apiName).subscribe((data) => {
if (data !== null && data !== undefined) {
resolve(data);
} else {
reject();
}
}, (error) => {
console.log('ERROR STATUS', error.status);
reject(error);
});
} catch (error) {
reject(error);
}
});
}
//Step 3:
//Component
getReceipt().subscribe((respect: any) => {
var downloadURL = window.URL.createObjectURL(data);
var link = document.createElement(‘a’);
link.href = downloadURL;
link.download = “sample.pdf";
link.click();
});
This also works in IE and Chrome, almost the same answer only for other browsers the answer is a bit shorter.
getPdf(url: string): void {
this.invoiceService.getPdf(url).subscribe(response => {
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
const newBlob = new Blob([(response)], { type: 'application/pdf' });
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob);
return;
}
// For other browsers:
// Create a link pointing to the ObjectURL containing the blob.
const downloadURL = URL.createObjectURL(newBlob);
window.open(downloadURL);
});
}

How to translate superagent to axios?

I have some upload working for superagent. It involves posting to an api for cloudinary. My question is how do I do the same thing with axios. I'm not sure what superagent.attach and superagent.field relate to in axios.
Basically when I make the post request I need to attach all these fields to the request or else I get bad request and I want to do this in axios not superagent as I am switching over to axios.
Here are all the params:
const image = files[0];
const cloudName = 'tbaustin';
const url = `https://api.cloudinary.com/v1_1/${cloudName}/image/upload`;
const timestamp = Date.now()/1000;
const uploadPreset = 'cnh7rzwp';
const paramsStr = `timestamp=${timestamp}&upload_preset=${uploadPreset}ORor-6scjYwQGpNBvMW2HGMkc8k`;
const signature = sha1(paramsStr);
const params = {
'api_key': '177287448318217',
'timestamp': timestamp,
'upload_preset': uploadPreset,
'signature': signature
}
Here is the superagent post request:
let uploadRequest = superagent.post(url)
uploadRequest.attach('file', image);
Object.keys(params).forEach((key) => {
uploadRequest.field(key, params[key]);
});
uploadRequest.end((err, res) => {
if(err) {
alert(err);
return
}
You would need to use FromData as follows:
var url = `https://api.cloudinary.com/v1_1/${cloudName}/upload`;
var fd = new FormData();
fd.append("upload_preset", unsignedUploadPreset);
fd.append("tags", "browser_upload"); // Optional - add tag for image admin in Cloudinary
fd.append("signature", signature);
fd.append("file", file);
const config = {
headers: { "X-Requested-With": "XMLHttpRequest" },
onUploadProgress: function(progressEvent) {
// Do something with the native progress event
}
};
axios.post(url, fd, config)
.then(function (res) {
// File uploaded successfully
console.log(res.data);
})
.catch(function (err) {
console.error('err', err);
});
See full example here