Learning how to use Sapper. I have a component with form (the form has one field to enter an email address) and use fetch to post the data to the serverhandle. When I post the data and try to log the data I posted, it logs Undefined. I have no idea why and need help to figure it out.
Here is my component with the form and fetch post code:
<script>
let emailvalue
let url = "posthandle"
async function handleSubmit(event) {
console.log(event);
console.log(event.target);
emailvalue = event.target.email.value;
console.log("this is from the variable--data--:" + content)
// ******** Here is the Fetch() code
fetch(url, {
method: 'POST',
body: JSON.stringify(emailvalue),
headers: {
'Content-Type': 'application/json'
}
})
.then(r => {
//this is gives the error that res stream is locked console.log(r.json())
consolde.log(r)
r.json()
.then(function(result) {
// this logs [object object]
console.log("let us see" + result)
})
})
.catch(err => {
// POST error: do something...
console.log('POST error', err.message)
})
//post code example https://stackoverflow.com/questions/55393685/js-sapper-posting-data-to-server-the-right-way
}
</script>
<p> {emailvalue} </p>
<form on:submit|preventDefault="{handleSubmit}">
<label for="email">Email</label>
<input required type="email" id="email" />
<button type="submit">Create account</button>
</form>
The line that reads: console.log("let us see" + result) is showing [object Object] and I don't understand why?
My handle to manage the post :
export async function post(req, res, next) {
res.setHeader('Content-Type', 'application/json')
/* Retrieve the data */
var data = req.body;
// Do something with the data...
// it logs Undefined. Why?
console.log("Here's the posted data:" + data );
/* Returns the result */
return res.end(JSON.stringify({ success: true }));
}
why data is undefined? Is the code wrong? Should I do something to read "data" and manage the actual data posted in the form?
Here is my server code (after #J) pointed out the body-parser issue:
import express from 'express';
import * as sapper from '#sapper/server';
const sirv = require('sirv');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const session = require('express-session');
const assets = sirv('static', {
maxAge: 31536000, // 1Y
immutable: true
});
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}))
app.use(assets, sapper.middleware()).listen(process.env.PORT, err => { if (err) console.log('error', err); });
If I console.log (r) from my fetch function which has the reponse. I get this in the console: Response {type: "basic", url: "http://localhost:3000/posthandle", redirected: false, status: 200, ok: true, …}
I had to npm install body-parser --save and add it to the server.js. I had to add bodyParser.urlencoded and bodyParser.json to get it to work.
import sirv from 'sirv';
import polka from 'polka';
import compression from 'compression';
import * as sapper from '#sapper/server';
// const express = require('express')
const app = polka()
const bodyParser = require('body-parser')
const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app
.use(
compression({ threshold: 0 }),
sirv('static', { dev }),
sapper.middleware()
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
Related
Before I deploy, the app performed fine on localhost. But since I deployed my frontend (react) to Netlify and backend(node/express + mysql) to Heroku, all requests sent from the frontend started to get blocked by CORS policy, with the error message:
"Access to XMLHttpRequest at 'https://xxx.herokuapp.com/login' from origin 'https://xxx.netlify.app' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://xxx.app/' that is not equal to the supplied origin."
Most importantly, the value of my Access-Control-Allow-Origin header is literally the same as the origin stated.
Originally, I've tried to use a wildcard ("*"), but it seems that due to the withCredential problem, the system just can't allow that kind of vague statement.
I've also seen many people using Netlify.toml to tackle some configuration problems, but seems ineffective for me.
Is it the header's problem? If not, then what is the problem?
I really want to know what I should do to solve this error...
The console window of the app deployed:
Cors Error
My index.js in the server folder:
const express = require('express')
const mysql = require('mysql')
const cors = require('cors')
const session = require('express-session')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const port = 3010
const app = express()
app.use(express.json())
app.use(cors({
origin: ["https://xxx.app/"], // the link of my front-end app on Netlify
methods: ["GET", "POST"],
credentials: true
}))
app.use(cookieParser())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(
session({
key: "userId",
secret: "subscribe",
resave: false,
saveUninitialized: false,
cookie: {
expires: 60 * 60 * 24
},
})
)
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "https://xxx.netlify.app/"); // the link of my front-end app on Netlify
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PATCH, DELETE, OPTIONS"
);
res.setHeader('content-type', 'application/json');
next();
});
const db = mysql.createPool({
// create an instance of the connection to the mysql database
host: 'xxx.cleardb.net', // specify host name
user: 'xxx', // specify user name
password: 'xxx', // specify password
database: 'heroku_xxx', // specify database name
})
...
app.get('/login', (req, res) => {
if (req.session.user) {
res.send({
isLoggedIn: true,
user: req.session.user
})
} else {
res.send({
isLoggedIn: false
})
}
})
...
app.listen(process.env.PORT || port, () => {
console.log('Successfully Running server at ' + port + '.')
});
My Frontend:
import React, { useEffect, useState } from 'react'
import '../App.css'
import './HeroSection.css'
import { Link } from 'react-router-dom'
import Axios from 'axios'
function HeroSection() {
Axios.defaults.withCredentials = true
let username = "";
const [name, setName] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [isLoading, setLoading] = useState(true)
...
useEffect(() => {
Axios.get('https://xxx.herokuapp.com/login').then((response) => {
if (response.data.isLoggedIn) {
username = response.data.user[0].username;
}
setIsLoggedIn(response.data.isLoggedIn)
Axios.post('https://xxx.herokuapp.com/getLang', {
username: username,
}).then((response) => {
console.log(response.data);
})
Axios.post('https://xxx.herokuapp.com/getStatus', {
username: username,
}).then(response => {
setName(response.data[0].firstname + " " + response.data[0].lastname);
setLoading(false);
})
})
}, [])
if (!isLoggedIn || isLoading) {
return (
<div>
...
</div>
)
} else {
return (
<div>
...
</div>
)
}
}
export default HeroSection
By the way, I use ClearDB MySQL on Heroku and MySQL WorkBench for the database, which all works fine.
You could debug by doing something like:
const allowList = ["https://yyy.app/"];
// Your origin prop in cors({})
origin: function (origin, callback) {
// Log and check yourself if the origin actually matches what you've defined in the allowList array
console.log(origin);
if (allowList.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
When I tried to log in from page, I got error
TypeError: req.flash is not a function
I explain the errors I got and the methods I tried
If I delete this code console.log (req.flash ("validation_error")) code in the function named "registerFormunuGoster" in the
auth_controller file, I can make a successful link to the page in
the first step. If I do not delete this code, I cannot connect to
the page successfully in the first step.
The text I mentioned above is translated into code below.
const registerFormunuGoster = (req, res) => { res.render("register", { layout: "./layout/auth_layout" ,}) }
Let's say I write the code mentioned above and opened the page, after that I fill the form on my page and I get the same error whenever I press the submit button after filling out the form. To solve this problem, under the auth_controller.js file If I delete the code "req.flash (" validation_error ", errors)" in the function named "register" this time i get a different error.I am leaving the other error I get below. I think the reason I got such an error must be because I did res.redirect().
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
The events I mentioned in item 4 are translated into code below.
`const register = (req, res) => {
const hatalar = validationResult(req);
if (!hatalar.isEmpty()) {
res.redirect("/register")
}
res.render("register", { layout: "./layout/auth_layout" ,})
}`
Below are my app.js and auth_controller.js configuration
app.js
const express = require("express")
const app = express()
const dotenv = require("dotenv").config()
const session = require("express-session")
const flash = require("connect-flash")
// database bağlantısı - veritabanı bağlantısı
require("./src/config/database")
// TEMPALTE ENGİNE AYARALARI
const ejs = require("ejs")
const expressLayouts = require("express-ejs-layouts")
const path = require("path")
app.set("view engine", "ejs")
app.set("views", path.resolve(__dirname, "./src/views"))
app.use(express.static("public"))
app.use(expressLayouts)
// routerlar include edilip kullanılır
const authRouter = require("./src/routers/auth_router")
app.use("/", authRouter)
// formdan yollanılan verileri json a çevirip verilere dönüşmesi için bu şart
app.use(express.urlencoded({ extended: true }))
//* const session = require("express-session")
//* Seesion ve flash message
//* bunları yapmak için yukarıdaki modul lazım
//? önemli bir not çıldıurmak üzereydim kodlar çalışmıyordu kodların çalışması üçün üst sıralar çekince oldu bakalım neyden kaynaklanıyorumuş deneyerek bulucam
//? app.get gibi sunucu istekleri yönlendirmeden önce kullanılması lazımmış
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
maxAge:1000*5
}
//* maxAge: verilen cookienin ne kadar zaman sonra kendisini ihma etmesini söylüyor
//* saniye cinsinden verdik
}))
//? flash mesajlarının middleware olarak kullanılmasını sağladık yani aldığımız hatayı flash sayesinde kullanabilceğiz
app.use(flash())
let sayac = 0
app.get("/", (req, res) => {
if (!req.session.sayac) {
req.session.sayac = 1
} else {
req.session.sayac++
}
res.json({ message: "Hello World", sayac: req.session.sayac })
})
app.listen(process.env.PORT, _ => console.log(`Server started at ${process.env.PORT} port `))
auth_controller.js:
const { validationResult } = require("express-validator")
const loginFormunuGoster = (req, res) => {
res.render("login", { layout: "./layout/auth_layout" })
}
const login = (req, res) => {
res.render("login", { layout: "./layout/auth_layout" })
}
const registerFormunuGoster = (req, res) => {
// console.log(req.flash("validation_error"))
res.render("register", { layout: "./layout/auth_layout" ,})
}
const register = (req, res) => {
const hatalar = validationResult(req);
// req.body adı altında girilen veriler gözüküyor
// console.log(req.body)
// console.log(hatalar)
if (!hatalar.isEmpty()) {
// req.flash("validation_error",hatalar)
res.redirect("/register")
}
res.render("register", { layout: "./layout/auth_layout" ,})
}
const forgetPasswordFormunuGoster = (req, res) => {
res.render("forget_password", { layout: "./layout/auth_layout" })
}
const forgetPassword = (req, res) => {
res.render("forget_password", { layout: "./layout/auth_layout" })
}
module.exports = {
loginFormunuGoster,
registerFormunuGoster,
forgetPasswordFormunuGoster,
register,
login,
forgetPassword,
}
I would really appreciate everyone's help!!!
I hope I explained the error I received well
I think your problem come from a misunderstanding of how you should send you request back.
The second error you have 'Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client' indicates that the request you are trying to manipulate again is already finished (you can read a better explanation here: Error: Can't set headers after they are sent to the client)
So in your case, you cannot do a .redirect and a .render in the same request.
For your initial error, it seems that flash is not attached to the req object. I'm not sure but it might be because you are requiring it after your router and it is not ready when used in it.
There:
// 'use' it before you require your auth_router
app.use(flash());
// then require your auth_controller.js file somewhere in this
const authRouter = require("./src/routers/auth_router")
app.use("/", authRouter)
Read connect-flash doc for more indication on how to use it: https://www.npmjs.com/package/connect-flash
This is my server.js file
import { json } from 'body-parser';
import sirv from 'sirv';
// import polka from 'polka';
import compression from 'compression';
import * as sapper from '#sapper/server';
import express from 'express';
const { PORT, NODE_ENV } = process.env;
const dev = NODE_ENV === 'development';
express() // You can also use Express
.use(
json(),
compression({ threshold: 0 }),
sirv('static', { dev }),
sapper.middleware()
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
This is the endpoint I'm trying to reach
located in src/routes/contact.js
export async function post(req, res, next) {
res.setHeader('Content-Type', 'application/json')
const data = req.body
console.log(data);
return res.end(JSON.stringify({ success: true }))
}
And this is the fetch
fetch('/contact', {
method: 'POST',
body: 'Hola mundo',
headers: {
'Content-Type': 'application/json'
}
}).then(() => {
console.log('Form submitted');
})
But network reports NOT FOUND, What I'm missing here? I thought it was a matter of polka then I switched to Express but the issue remains
PD: This is my reference Post for the structure
fetch post is giving me undefined for the posted data?
I'm trying to setup CSRF tokens so that I can do a number of checks before issueing a token to the client to use in future requests.
Taking the guidance from the csurf documentation, I've setup my express route with the following:
const express = require('express');
const router = express.Router({mergeParams: true});
const csurf = require('csurf');
const bodyParser = require('body-parser');
const parseForm = bodyParser.urlencoded({ extended: false });
const ErrorClass = require('../classes/ErrorClass');
const csrfMiddleware = csurf({
cookie: true
});
router.get('/getCsrfToken', csrfMiddleware, async (req, res) => {
try {
// code for origin checks removed for example
return res.json({'csrfToken': req.csrfToken()});
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
router.post('/', [csrfMiddleware, parseForm], async (req, res) => {
try {
// this returns err.code === 'EBADCSRFTOKEN' when sending in React.js but not Postman
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
For context, the React.js code is as follows, makePostRequest 100% sends the _csrf token back to express in req.body._csrf
try {
const { data } = await makePostRequest(
CONTACT,
{
email: values.email_address,
name: values.full_name,
message: values.message,
_csrf: csrfToken,
},
{ websiteId }
);
} catch (error) {
handleError(error);
actions.setSubmitting(false);
}
Postman endpoint seems to be sending the same data, after loading the /getCsrfToken endpoint and I manually update the _csrf token.
Is there something I'm not doing correctly? I think it may be to do with Node.js's cookie system.
I think your problem is likely to be related to CORS (your dev tools will probably have sent a warning?).
Here's the simplest working back-end and front-end I could make, based on the documentation:
In Back-End (NodeJS with Express) Server:
In app.js:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
const cors = require('cors');
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
var app = express()
const corsOptions = {
origin: "http://localhost:3000",
credentials: true,
}
app.use(cors(corsOptions));
app.use(cookieParser())
app.get('/form', csrfProtection, function (req, res) {
res.json({ csrfToken: req.csrfToken() })
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
module.exports = app;
(make sure you update the corsOptions origin property to whatever your localhost is in React.
In Index.js:
const app = require('./app')
app.set('port', 5000);
app.listen(app.get('port'), () => {
console.log('App running on port', app.get('port'));
});
In React:
Create file "TestCsurf.js" and populate with this code:
import React from 'react'
export default function TestCsurf() {
let domainUrl = `http://localhost:5000`
const [csrfTokenState, setCsrfTokenState] = React.useState('')
const [haveWeReceivedPostResponseState, setHaveWeReceivedPostResponseState] = React.useState("Not yet. No data has been processed.")
async function getCallToForm() {
const url = `/form`;
let fetchGetResponse = await fetch(`${domainUrl}${url}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": localStorage.getItem('xsrf-token'),
},
credentials: "include",
mode: 'cors'
})
let parsedResponse = await fetchGetResponse.json();
setCsrfTokenState(parsedResponse.csrfToken)
}
React.useEffect(() => {
getCallToForm()
}, [])
async function testCsurfClicked() {
const url = `/process`
let fetchPostResponse = await fetch(`${domainUrl}${url}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": csrfTokenState,
},
credentials: "include",
mode: 'cors',
})
let parsedResponse = await fetchPostResponse.text()
setHaveWeReceivedPostResponseState(parsedResponse)
}
return (
<div>
<button onClick={testCsurfClicked}>Test Csurf Post Call</button>
<p>csrfTokenState is: {csrfTokenState}</p>
<p>Have we succesfully navigates csurf with token?: {JSON.stringify(haveWeReceivedPostResponseState)}</p>
</div>
)
}
Import this into your app.js
import CsurfTutorial from './CsurfTutorial';
function App() {
return (
<CsurfTutorial></CsurfTutorial>
);
}
export default App;
That's the simplest solution I can make based on the CSURF documentations example. It's taken me several days to figure this out. I wish they'd give us a bit more direction!
I made a tutorial video in case it's of any help to anyone: https://youtu.be/N5U7KtxvVto
I am a beginner to react and node and this will be a very basic problem.
I am creating a basic react fronted with a node backend. I setup mysql database connection and all set. I want to insert user details using /createUser api call to store data to database.
I run the server/app.js, react serve and index.js which contained my listener for /createUser.
When I input username,password using my form, empty req.body object will be returned in express.post method while I am expecting the username and password I entered.
In other case, when I start the listener, react front is not loading by giving the error below.
GET http://localhost:3000/%PUBLIC_URL%/manifest.json 404 (Not Found)
manifest.json:1 Manifest: Line: 1, column: 1, Unexpected token.
It seems like node component cannot access my manifest file. Am I correct?
index.js
const express = require('express');
const bodyParser = require('body-parser');
const store = require('./store');
const app = express();
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json({ type: 'application/*+json' }));
app.use(bodyParser.json());
app.post('/createUser', (req, res) => {
console.log(req.body);
store
.createUser({
username: req.body.username,
password: req.body.password
})
.then(() => res.sendStatus(200))
})
app.listen(3001, () => {
console.log('Server running on http://localhost:3001')
})
login.js
import React, { Component } from 'react';
import classes from './Login.css';
function post (path, data) {
console.log(data);
return window.fetch(path, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
}
function handleLoginClick(e) {
e.preventDefault();
console.log('The link was clicked.');
const Login = document.querySelector('.Login');
const username = Login.querySelector('.username').value;
const password = Login.querySelector('.password').value;
post('/login', { username, password })
.then(({ status }) => {
if (status === 200) alert('login success')
else alert('login failed')
})
}
function handleSignupClick(e) {
e.preventDefault();
console.log('The link was clicked sign up.');
const CreateUser = document.querySelector('.CreateUser')
const username = CreateUser.querySelector('.username').value;
const password = CreateUser.querySelector('.password').value;
post('/createUser', { username, password })
}
class Login extends Component {
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>
<form className="Login">
<h1>Login</h1>
<input type="text" className="username" placeholder="username"/>
<input type="password" className="password" placeholder="password"/>
<input type="submit" value="Login" onClick={handleLoginClick}/>
</form>
<form className="CreateUser">
<h1>Create account</h1>
<input type="text" className="username" placeholder="username"/>
<input type="password" className="password" placeholder="password"/>
<input type="submit" value="Create" onClick={handleSignupClick}/>
</form>
</div>
);
}
}
export default Login;
What's wrong with my code? Can someone please explain me.
my code : https://github.com/indunie/tms2
Try to replace bodyParser configuration to following lines of code:
app.use(bodyParser.json({ limit: '100mb' }));
app.use(bodyParser.urlencoded({ limit: '100mb', extended: false }));
This might help you
solved [https://github.com/expressjs/express/issues/3881][1]
There are two solutions here:
Change the URL to http://localhost:3001/manifest.json to match how you have express.static setup.
Change express.static to app.use('/public', express.static('public')); such that http://localhost:3001/public/manifest.json will refer to the given file.