AngularJS, special character from Json via server - node.js

I'm trying to create a small multi-language project using NodeJS version 10.15.1, AngularJS version 1.5.8 and UTF8 encoded html. I should proceed with my own function instead of using other modules.
I created 2 different json files containing 2 different languages. The json is loaded via server using a $http call and the answer is stored inside a $scope variable.
$http.post(apihost + '/languages/language_frontend', {page: "home"}).then(function(language) {
$scope.language = language.json;
});
I pass the parameter page to filter with part of the json the function should retrieve.
router.post('/language_frontend', function(req, res, next) {
return new Promise(function(resolve,reject) {
if(config.language == 'it') return res.json({status: 'ok', json: italian_frontend[req.body.page]});
else if(config.language == 'en') return res.json({status: 'ok', json: english_frontend[req.body.page]});
});
});
This is (part) of one of the json
{
"home": {
"planning": "Pianificazione",
"activities_planning": "Pianificazione Attività"
},
"login": {
"test_one": "italiano uno",
"test_one": "italiano due"
}
}
And this is the html that displays the information
<div class="panel-heading">
<div class="row">
<div class="col-xs-3"><i class="fa fa-mobile-phone fa-5x"></i></div>
<div class="col-xs-9 text-right">
<div class="huge ng-binding">{{language.activities_planning}}</div>
</div>
</div>
</div>
The problem is that the displaying of activities_planning comes with an accented character and, coming from server side call, I don't know how to display it correctly. I'd like a general solution to implement everywhere, so I don't have to worry about few exceptions with special characters.
This is the result without a solution: Pianificazione Attivit�
Any suggestion?

So, here it is https://glitch.com/edit/#!/angularjs-specialchars. I tried to set up the same thing with you :
In my app.js on the backend, I get the content of JSON file and expose it in /language route :
const path = require("path");
const express = require("express");
const app = express();
const language = require("./test.json");
app.use('/', express.static(path.join(__dirname, 'public')));
app.get('/language', (req, res) => res.json({ status: "ok", json: language }));
app.listen(5000, function() {console.log("Server is running on port 5000")});
In my index.js on the client-side, I send a request to the server to get the JSON file :
angular.module("app", []).controller("MyController", ["$scope", "$http",
function ($scope, $http) {
// send request to get the json
$http.get('/language').then(function (resp) {
const language = resp.data.json;
console.log(language); // I've checked on the console.log, the text is OK
$scope.text = language.test; // bind to screen
});
}
]);
And in my index.html I just use it :
<body ng-app="app">
<div ng-controller="MyController">
<h1>Hello {{text}}!</h1>
</div>
</body>
What I have :

Related

How to send multipart-data video file from NextJS frontend to Express backend?

I am trying to enable a user to upload a video file and send it through my NextJS api to my Express backend (and store it in an S3 bucket, I don't have a need to save it on my server), but cannot figure out how to do it after days of searching. I understand that NextJS has a limitation where it cannot read multipart form data, but I don't understand how to strategically address this issue. I have seen packages like multer and formidable, but I am unsure how to use them (since they seem to be for the backend, and I am having trouble passing the file to my frontend API).
Since the video files can be big, I think I need to upload the video files to s3 directly from the frontend (using a multipart upload) and then send the AWS URL for the video to the backend. If this is the right approach, how do I do that?
Project setup: NextJS frontend running on port 3000 + Express backend server running on port 5000. Here is my code:
FileUpload.tsx (frontend)
...
<div>
<div>
{
previewURL != null ?
file.type.includes("video") ?
<video id="video" width="800" controls>
<source src={ previewURL } id="videoSrc" />
</video>
:
<audio id="audio" controls>
<source src={ previewURL } id="audioSrc" />
</audio>
: null
}
<form action="/api/uploadToAssemblyAi" method="post">
<label htmlFor="file">File</label>
<input
type="file"
id="file"
name="file"
onChange={ generatePreview }
/>
<button type="submit"> Upload </button>
<button type="reset" onChange={ clearFile }> Clear </button>
</form>
</div>
</div>
...
/api/uploadToAssemblyAi.tsx (frontend)
import type { NextApiRequest, NextApiResponse } from 'next'
import axios from "axios"
export const config = {
api: {
bodyParser: false
}
}
export async function uploadData(file: File) {
// Prep to send file to backend
const BACKEND_API_URL = process.env.BACKEND_URL
const assembly = axios.create({
baseURL: BACKEND_API_URL,
withCredentials: true,
});
// Add file to request
let formData = new FormData();
formData.append("file", file);
// Call backend
const { data } = await assembly.post(`${BACKEND_API_URL}/api/synthesize/transcribe}`, formData)
const transcriptionId = data.transcriptionId
return transcriptionId
}
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const file = req.body.file
// ^^^ This is not working, I cannot access the file from the form (this is for communication purposes, I know this isn't the right way to do it)
try {
const transcriptionId = await uploadData(file)
return res.status(201).redirect(307, `/calls/${transcriptionId}`)
} catch (err) {
console.log("Error: " + err)
return res.status(400).send( { msg: err })
}
}
uploadController.ts (backend)
...
// This is where I want to receive the file to upload to AWS
export async function transcribeController(req: Request, res: Response) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send({ msg: 'No files were uploaded.' });
}
const file = req.files
const file = req.body
// ^^^ I'm not sure how to access the file on the backend since I cannot even send it from the frontend (this is for communication purposes, I know this isn't the right way to do it)
return
}
...
You can use FormData to send the request. This will be sent as a multipart/form-data request.
let formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/uploadToAssemblyAi', {
method: 'POST',
body: formData,
});
You can also update your form element to specify that content type:
<form
action="/api/uploadToAssemblyAi"
method="post"
enctype="multipart/form-data"
>

How to pass data from app.post to app.get?

I'm trying to show webpage to users at the route app.get(/:transactionid) with the text telling them "Please wait, your file is processing"
And after file processing is finished (could take around 5-10 seconds) then change that text to "Your file is ready, download here"
But I don't really know how to pass variable from app.post to app.get.
The variable is just aws-s3 download link (if that matters).
I have 2 routes like so:
app.get('/:transactionid', async (req,res)=>{
res.render('purchase')
})
app.post('/:transactionid', async (req,res)=>{
// some file processing and uploading to aws-s3
const url = s3url;
res.render('purchase',{link:url})
})
and in the ejs view I have:
<body>
<% if(link) { %>
Your file is ready <div class="inline text-blue-600">Download here </div>
<% } else { %>
<div class="text-red-500">Please wait, your file is processing</div>
<% } %>
</body>
But the res.render('purchase',{link:url}) in app.post won't update the text in the browser. I'm not sure if I understand correctly but I think I should send the url from app.post to app.get?
EDIT:
Here are some snippets of what it looks like uploading and inserting it into database
var params = {
Bucket: 'xxxxxxx',
Key: decodeURIComponent(awskey),
Body: buff };
// upload to s3
var uploadfile = s3.upload(params, function(err, data) {
if(err) {console.log(err);}
});
// turn it into promise
var promise = uploadfile.promise();
promise.then( function(data){
pool.query(`
INSERT INTO transactions (transactionid, s3url)
VALUES ($1, $2)
`, [transactionid, data.Location], (err, results) => { // data.Location is the s3 url
if (err) {
throw err;
}
});
})

How to send data from react and use the request body in express, using Axios?

I use states to hold some form data in react, and what i want is, when i submit the form in react i send a post request using axios,and i want to use the form data in express. I tried req.body.params, req.body.data, req.body general,to access the request payload and they all returned undefined. Basically I want to access general(form data) in express. How can i do that?
note: the states are named general, and oneSuggestion, they are basic string states.
note:i searched similar questions for hours but none of them solved my question. if there was a solution, i couldn't find it, please excuse me.
edit: commented data, and put the states directly inside post.
React code:
function handleSubmit(e) {
e.preventDefault();
console.log(general, suggestionsForCases);
/*
let data = {
general: general,
oneSuggestion: oneSuggestion,
};
data = JSON.stringify(data);
console.log(data);
*/
let axiosConfig = {
headers: {
"Content-Type": "application/json;",
"Access-Control-Allow-Origin": "*",
},
};
axios
.post(
"http://localhost:8000/dict/",
{ general: general, oneSuggestion: oneSuggestion },
axiosConfig
)
.then((res) => console.log("success, dictionary sent,", res))
.catch((err) => {
console.log(err.response);
});
}
function handleInput(e) {
if (e.target.name == "general") {
setGeneral(e.target.value);
console.log(general);
}
if (e.target.name == "oneSuggestion") {
setOneSuggestion(e.target.value);
console.log(oneSuggestion);
}
}
return (
<div>
<form onSubmit={handleSubmit}>
<label>
general:
<textarea name="general" onChange={handleInput}></textarea>
</label>
<label>
suggestion:
<input name="oneSuggestion" onChange={handleInput}></input>
</label>
<button type="submit">submit</button>
</form>
</div>
);
}
export default AddDictionary;
express code:
const express = require("express");
const router = express.Router();
const Dictionary = require("../database/category/dictionary");
router.use(express.json());
router.get("/", (req, res) => {
console.log("success");
res.json({ msg: "success"});
});
router.post("/", (req, res) => {
console.log(req.body.general);
res.json({
msg: "success",
});
});
module.exports = router;
Ok, I found the problem. I deleted the axiosConfig from post, based on this source , and now this is the working code:
axios
.post(
"http://localhost:8000/dict/",
{ general: general, oneSuggestion: oneSuggestion }
)
.then((res) => console.log("success, dictionary sent,", res))
.catch((err) => {
console.log(err.response);
});
thank you guys for your help.
You'll need middleware in order to parse the request and make it accessible in req.body. I've assumed you're using a version after 4.16 which introduced express.json() as middleware for this scenario. I'll update my answer if you're using an earlier version.
Example using your code as a starter:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/', (req, resp) => {
console.log(request.body.params);
});
app.listen(3000);
To explain, anything you post, aka let's say you posted the following object:
{
fruit: 'apple',
vegetable: 'onion'
}
After using the parser you'd access the posted data in req.body.fruit and req.body.vegetable.

How to redirect after an error in Express

I am using Express for routing and MongoDB for my simple Node blog app (I'm new and just learning) and I need to redirect to the home page if somebody enters the incorrect URL, whenever it attempts to redirect the program crashes.
Terminal Output:
Server started on 3000
Rendered homepage
events.js:174
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'postTitle' of null at line 115
Router Params / Get
//=== DYNAMIC POSTS ROUTE ===//
app.get("/posts/:postId", function(req, res){
const requestedPostTitle = req.params.postId;
Post.findOne({postTitle: requestedPostTitle}, function(err, foundPost){
if (!err) {
//FAILS HERE IF INCORRECT URL IS ENTERED INTO BROWSER
const title = foundPost.postTitle;
const date = foundPost.postDate;
const content = foundPost.postBody;
/*res.send(foundPost.postDate);*/
res.render(`post`, {
title: title,
date: date,
content: content
});
} /*else {
res.redirect(`/`);
console.log(`404 ${requestedPostTitle} does not exist`);
} */
});
});
The program will only crash if I type in an incorrect URL, after that, none of my pages will reload (I'm assuming because of the (err) callback), I have to restart my server manually and everything works again, nodemon doesn't reset it when it fails.
root view:
<h1>HOME</h1>
<p><%= pageStartContent %></p>
<% posts.forEach(function(post){ %>
<div class="post-box">
<h3><%= post.postTitle %></h3>
<p class="date"><%= post.postDate %></p>
<p class="post-body"><%= post.postBody.substring(0,450) + "..." %></p>
Read More
</div>
<% }) %>
app.get("/posts/:postId", function(req, res){
const requestedPostTitle = req.params.postId;
Post.findOne({postTitle: requestedPostTitle}, function(err, foundPost){
if (!err && foundPost) {
//FAILS HERE IF INCORRECT URL IS ENTERED INTO BROWSER
const title = foundPost.postTitle;
const date = foundPost.postDate;
const content = foundPost.postBody;
/*res.send(foundPost.postDate);*/
return res.render(`post`, {
title: title,
date: date,
content: content
});
}
return res.redirect(`/`);
});
});
the code didn't work before (maybe) because you are checking if there an error and if not rendering post but this does not mean that the post was found, you need to check if foundPost is not null.

Vuejs load image from nodejs res.sendFile

In my vue's created, I use axios to connect to my server to retrieve an image as below:
created() {
this.schoolID = localStorage.getItem('schoolID')
console.log(this.schoolID)
axios.get(process.env.MY_URL + '/public/logo', {
params: {
schoolID: this.schoolID
}
})
.then(response => {
this.logo = response.data
console.log(this.logo)
})
.catch(e => {
console.log(e.response)
this.errors.push(e)
})
},
and my nodejs will receive the request and send the response like below
router.get('/logo', function(req, res){
School.findOne({ _id: mongoose.mongo.ObjectId(req.query.schoolID) }).exec().then(function (data) {
if (!data){
console.log("error... no logo found for the given id: " + req.query.schoolID)
return res.status(200).send({success: false, msg: 'No logo found.'});
}else{
res.sendFile(__dirname + '/uploads/' + data.logo);
}
});
});
my image should be loaded into my code
<img :src="this.logo">
I'm positive I got (at least the nodejs) the image file correctly because if I just append some string at the end of data.logo, I will get 404 not found error in my Vue and ENOENT: no such file or directory, stat 'D:\web\musleh-api\uploads\1542208727664_logo_sdam.png' error in my nodejs
However, no image is being loaded,and my console.log(this.logo) will display funny symbols which I assume is the image file if we try to open it using any text editor.
What did I miss?
I believe what you need to do is this in your HTML code, assuming that your image is base64 encoded:
<img alt="Awesome logo" :src="'data:image/png;base64,'+this.logo" />
Here is the source
Addition, as above seems not to work:
After some research I think we are not completely on the wrong path here, but give this a try:
On the NodeJS side try this:
}else{
var img = fs.readFile('__dirname + '/uploads/' + data.logo', function (err, data) {
var contentType = 'image/png';
var base64 = Buffer.from(data).toString('base64');
base64='data:image/png;base64,'+base64;
res.send(base64);
});
});
});
then in VueJS as before:
<img alt="Awesome logo" :src="this.logo" />
OR THE EASY WAY:
On the NodeJS side try this:
}else{
// prefix your domain if your API serves to other domains
res.send('/uploads/' + data.logo');
});
});
});
Also here Vuejs code remains as you did it.
Assuming that you want to pass the school ID to request the image, you don't need axios to make the request, you can use directly the <img src> tag with a dynamic parameter to request the data. You also need to change you express configuration to return the image with Content-Type:'image/png' or whatever is the mime type of your data.
Then on vue template you will need to pass the school id to the dynamic route, the data request and handling will be done by the img element
<img :src="`/logo/${schoolID}">
.
router.get('/logo/:schoolID', function(req, res){
School.findOne({ _id: mongoose.mongo.ObjectId(req.params.schoolID) }).exec().then(function (data) {
if (!data){
console.log("error... no logo found for the given id: " + req.params.schoolID)
return res.status(200)
.send({success: false, msg: 'No logo found.'});
}else{
res.type('png')
.sendFile(__dirname + '/uploads/' + data.logo);
}
});
});

Resources