christianmalek / vuex-rest-api

A utility to simplify the use of REST APIs with Vuex
http://vuex-rest-api.org
MIT License
382 stars 48 forks source link

Dispatching POST request doesn't send the second argument #32

Closed colonelpopcorn closed 7 years ago

colonelpopcorn commented 7 years ago

I've got a super simple express.js server up and running with body-parser. I can successfully send a request and receive it on the server, but when I log the body of the request it's empty. I tried to make a request with postman and was able to get the object on the server, but I had to set content-type to x-www-form-urlencoded. I went back to my code and created a new Axios instance with content-type set to form-urlencoded, but I still could not see the body of the request on the server.

Here's my store:

import Vue from 'vue'
import Vuex from 'vuex'
import Vapi from 'vuex-rest-api'
import axios from 'axios'

Vue.use(Vuex);

var ax = axios.create({
    baseURL: "http://localhost:3000",
    timeout: 1000,
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});

const todos = new Vapi({
    axios: ax,
    state: {
        todo: null,
        todos: []
    }
})
.get({
    action: "getTodos",
    property: "todos",
    path: "/api/todos"
})
//This one won't send the payload I define in another component.
.post({
    action: "addTodo",
    property: "todos",
    path: "/api/todos",
    onSuccess: (state, payload) => {
        console.log(payload);
    }
})
.getStore();

export const store = new Vuex.Store(todos);

Here's the component where I dispatch it.

<template>
    <div id="app">
        <h1>{{ message }}</h1>
        <router-link to="/admin">Go to the admin page!</router-link>
        <ul>
            <li v-for="(item, index) in this.$store.state.todos">
                <template>
                    <div class="input-group">
                        <input type="checkbox" :id="index" v-model="item.checked" tabindex="0">
                        <label :for="index" :class="{strikethrough: (isStrikeThrough && item.checked), zebra: (isZebraStripes && index % 2 === 0)}">{{ item.description }}</label>
                    </div>
                </template>
            </li>
        </ul>
        <input v-model="tempMessage">
        <button @click="addTodo">Submit</button>
    </div>
</template>
<script>
export default {
    name: 'home',
    data() {
        return {
            message: "Todo App",
            tempMessage: ''
        };
    },
    computed: {
        isStrikeThrough() {
            return this.$store.getters.isStrikeThrough;
        },
        isZebraStripes() {
            return this.$store.getters.isZebraStripes;
        }
    },
    methods: {
        addTodo() {
            var tempObject = { description: '', checked: false };
            if (this.$store.getters.isSaltyDeveloperMode) {
                var saltyPhrases = this.$store.getters.getSaltyPhrases;
                var saltyIndex = Math.floor(Math.random() * saltyPhrases.length);
                tempObject.description = saltyPhrases[saltyIndex];
            }
            else {
                tempObject.description = this.tempMessage;
            }
            var body = {
                tempObject: tempObject
            }
            //My body object will not log to the server.
            this.$store.dispatch('addTodo', body);
            this.tempMessage = '';
        }
    },
    mounted() {
        //Initial loading of todos works just fine!
        this.$store.dispatch('getTodos');
    }
}
</script>
<style>
.strikethrough {
    text-decoration: line-through;
}

.zebra {
    color: white;
    background-color: black;
}
</style>

And my simple express js server.

var express = require('express'),
    bodyParser = require("body-parser");
    path = require('path');
    app = express(),
    PORT_NUMBER = 3000;

var todos = [ 
            { description: "Example todo!", done: false },
            { description: "Another Example todo!", done: true }
        ];

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use('/dist', express.static('dist'));

app.get('/api/todos', function (req, res) {
    res.send(todos);
});

app.get('/api/todos/:todo', function (req, res) {
    res.send(todos[req.params.todo - 1]);
});

app.post('/api/todos', function (req, res) {
    console.log("We got a request!");
    console.log(req.body);
});

app.put('/api/todos/:todo', function (req, res) {

});

app.delete('/api/todos/:todo', function (req, res) {
    var removedItem = todos.splice((req.params.todo - 1), 1);
    res.send(removedItem);
});

app.get('*', function(req, res) {
    res.sendFile(path.resolve('index.html'));
});

console.log("About to listen on port " + PORT_NUMBER);

app.listen(PORT_NUMBER);

Here's what I expect when I log the body to the console: { body: { tempObject: {description: "Something from the text input", checked: false} } }

Here's what I get: {}

I think I'm just misunderstanding how the POST requests are done, but I can't figure it out. Is body parser my problem? Should I be looking for a different property in the request object?

christianmalek commented 7 years ago

Your server should work fine. You have to dispatch the actions as written here: https://github.com/christianmalek/vuex-rest-api#calling-the-actions

// wrong
this.$store.dispatch("addTodo", body)

// correct
this.$store.dispatch("addTodo", {data: body})
colonelpopcorn commented 7 years ago

I've changed the dispatch as shown: this.$store.dispatch("addTodo", {data: body});, and I still get an empty json object on the server. Is there anything special that needs to happen for the data to be sent to the server properly? Does Axios normally send the data object as a request body? I'm also logging my object before sending it and confirming that the expected data is there before sending it.

Edit: I've confirmed with postman that I can receive a body on my express server.

Edit: I figured it out, my object literal was not a properly formatted JSON string. You can close this issue now.

christianmalek commented 7 years ago

Does Axios normally send the data object as a request body?

Yes. This should work.

I'm also logging my object before sending it and confirming that the expected data is there before sending it.

How do you log it? If you use an axios interceptor you have to return the config (see https://github.com/mzabriskie/axios#interceptors).

colonelpopcorn commented 7 years ago

I logged it on the server with a console log. I figured out with Postman that it was improperly formatted JSON that was the problem. I fixed it on the client and I can now send items to the server. Thank you for the help!

christianmalek commented 7 years ago

Glad you find the solution by yourself. :)