Я уже несколько недель пытаюсь получить Nuxt. js + Express (используя create-nuxt-app
) для работы со сторонним API. Неважно, что я пытаюсь, ничего, абсолютно ничего, работает. Никакие операторы консоли с моей серверной стороны не запускаются, только операторы переднего плана. Кроме того, недавно я начал получать Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
, и я нигде в своем коде не устанавливаю заголовки.
Все, что я хочу сделать, - это принять пользовательский ввод, передать его на сервер, чтобы избежать проблем с CORS, получить данные из стороннего API, а затем отобразить их. Это просто. Это происходит все время в inte rnet, так почему я не могу заставить его работать?
Ниже приведены все файлы, вовлеченные в процесс, описанный выше.
pages / index . vue:
<template>
<div class="container">
<img src="@/assets/images/OscarPark.jpg" alt="Oscar in the park" title="I love the park!" />
<br />
<form>
<label for="title">Book Title:</label>
<input v-model="titleFromUser" type="text" name="title" class="title" />
<button @click.prevent="submit" class="submit">Find a Book!</button>
</form>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
components: {},
data() {
return {
titleFromUser: '',
};
},
computed: mapState(['newTitles']),
methods: {
submit() {
this.$store.dispatch('FETCH_BOOK_TITLES', this.titleFromUser);
this.titleFromUser = '';
},
},
};
</script>
<style lang="scss">
.container {
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
img {
transform: rotate(90deg); // this fixes a glitch that causes image to rotate needlessly
padding-top: $underHeaderGap; // these 2 padding lines help to fix the placement of the image caused by the needless rotation
padding-left: $underHeaderGap * 5;
margin-bottom: $underHeaderGap * 5;
height: $imgHeight;
}
}
</style>
store / index. js:
import consola from 'consola';
export const state = () => ({
newTitles: [],
});
export const mutations = {
SET_NEW_TITLES(state, newTitles) {
state.newTitles = newTitles;
},
};
export const actions = {
async FETCH_BOOK_TITLES({ commit }, titleFromUser) {
try {
consola.ready({
message: `'FETCH_BOOK_TITLES': titleFromUser: ${titleFromUser}`,
badge: true,
});
const { data } = await this.$axios.$post('/title', { titleFromUser });
consola.ready({
message: `data returned from api: ${data}`,
badge: true,
});
commit('SET_NEW_TITLES', data);
} catch (error) {
consola.error({
message: `FETCH_BOOK_TITLES: Something went wrong: ${error}`,
badge: true,
});
throw new Error(error);
}
},
};
server / index. js:
const express = require('express');
const cors = require('cors');
const consola = require('consola');
const axios = require('axios');
const { Nuxt, Builder } = require('nuxt');
const app = express();
const jsonParser = express.json();
const titleRouter = require('../api/title/index');
// Import and Set Nuxt.js options
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
async function start() {
// Init Nuxt.js
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
// Give app ability to parse json
app.use(jsonParser);
// Give app ability to get past CORS issues
app.use(cors());
// Give nuxt middleware to express
app.use(nuxt.render);
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
app.use('/title', titleRouter);
app.get('/title', (req, res) => {
consola.ready({
message: `res.json in title endpoint-server: ${res.json()}`,
badge: true,
});
consola.ready({
message: `req.json in title endpoint-server: ${req.json()}`,
badge: true,
});
const recommendationsURL = `https://tastedive.com/api/similar?q=and+then+there+were+none&type=books&info=1&k=${process.env.TASTE_DIVE_API_KEY}`;
axios
.get(recommendationsURL, (req, res) => {
consola.ready({
message: `from server/index.js: ${res.json()}`,
badge: true,
});
})
.catch((error) => {
consola.error({
message: `error from axios server ${error} `,
badge: true,
});
});
});
// Listen to the server
app.listen(port, host, () => {
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true,
});
});
}
start();
api / title / index. js:
const consola = require('consola');
const express = require('express');
const app = express();
const titleRouter = express.Router();
titleRouter.use((req, res, next) => {
Object.setPrototypeOf(req, app.request);
Object.setPrototypeOf(res, app.response);
req.res = res;
res.req = req;
next();
});
titleRouter.get('/title', (req, res) => {
res
.json()
.then((data) => {
consola.ready({
message: `~api/title get title is ${data}`,
badge: true,
});
})
.catch((error) => {
consola.error({
message: `~api/title get Something went wrong: ${error}`,
badge: true,
});
throw new Error(error);
});
});
titleRouter.post('/title', (req, res) => {
res
.json()
.then((data) => {
consola.ready({
message: `~api/title post title is ${data}`,
badge: true,
});
})
.catch((error) => {
consola.error({
message: `~api/title post Something went wrong: ${error}`,
badge: true,
});
throw new Error(error);
});
});
module.exports = titleRouter;
nuxt.config. js:
require('dotenv').config();
module.exports = {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: "Oscar's Book Recommendations",
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: process.env.npm_package_description || '',
},
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
},
/*
** Customize the progress-bar color
*/
loading: { color: '#fff' },
/*
** Plugins to load before mounting the App
*/
plugins: [],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Doc: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module',
'@nuxtjs/dotenv',
'@nuxtjs/style-resources',
],
/*
** Nuxt.js modules
*/
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
'@nuxtjs/pwa',
'@nuxtjs/auth',
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {
https: true,
baseURL: process.env.BASE_URL || 'http://localhost:3000',
},
/*
** Build configuration
*/
build: {
watch: ['api/title'],
/*
** You can extend webpack config here
*/
extend(config, ctx) {},
},
pageTransition: {
name: 'fade',
mode: 'out-in',
},
env: {
TASTE_DIVE_API_KEY: process.env.TASTE_DIVE_API_KEY,
},
serverMiddleware: ['~api/title'],
styleResources: {
scss: ['~assets/styles/main.scss'],
},
};
Кто-нибудь видит, кто я? Почему данные не передаются на сервер? Почему это говорит мне, что я не могу установить заголовки после их отправки, когда я не устанавливаю их где-нибудь в своем коде? Что не так с этим?
Я был бы очень признателен за любой вклад и помощь. Спасибо.