简体   繁体   中英

400 Bad Request issue with POST request from my Vue app to my Rails API

I'm learning Rails and Vue, and created a simple Rails API and Vue app to learn how to GET and POST from my Vue app to my Rails API. So far the GET requests are fine but I'm getting 400 Bad Request due to bad syntax from sending POST requests. The POST requests work when testing it using Insomnia (an API testing app similar to Postman), but I get 400 when I use my Vue app to POST.

Here's my Rails controller:

def create
    @todo = Todo.new(todo_params)
    if @todo.save
        render :show, status: :created, location: @todo
    else
        render json: @todo.errors, status: :unprocessable_entity
    end
end

...

private

    def set_todo
        @todo = Todo.find(params[:id])
    end

    def todo_params
        params.require(:todo).permit(:title, :description, :finished)
    end

Here's my Vue app, with a simple text input to POST to my Rails:

<template>
<input type="text" v-model="postBody"/>
<button v-on:click="submit()">Submit</button> 
</template>

<script>
import axios from 'axios';
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      todos: [],
      errors: [],
      postBody: ''
    }
  },
methods: {
    submit() {axios.post(`http://localhost:3000/todos/`, { 
        headers: {
          'Content-Type': 'application/json; charset=UTF-8',
        },
        body: JSON.stringify(this.postBody) 
      })
      .then(response => response.data)
      .catch(e => {
        this.errors.push(e)
      })
    }
  }
}
</script>

I try to submit this POST request in JSON into the text input to my Rails endpoint http://localhost:3000/todos/ :

{
  "title": "Sweets",
  "description": "Buy cookies",
  "finished": false
}

Then I get this error from the browser:

{status: 400, error: "Bad Request",…}
error: "Bad Request"
exception: "#<ActionController::ParameterMissing: param is missing or the value is empty: todo>"
status: 400
traces: {Application Trace: [{id: 1, trace: "app/controllers/todos_controller.rb:51:in `todo_params'"},…],…}
Application Trace: [{id: 1, trace: "app/controllers/todos_controller.rb:51:in `todo_params'"},…]
0: {id: 1, trace: "app/controllers/todos_controller.rb:51:in `todo_params'"}
1: {id: 2, trace: "app/controllers/todos_controller.rb:24:in `create'"}

This is the error I'm getting from terminal from my local Rails server (I'm testing this in local environment in both Vue and Rails):

Started POST "/todos/" for ::1 at 2020-09-21 12:09:16 +0800
Processing by TodosController#create as JSON
  Parameters: {"headers"=>{"Content-Type"=>"application/json; charset=UTF-8"}, "body"=>"\"{   \\\"title\\\": \\\"Sweets\\\", \\t\\\"description\\\": \\\"Buy cookies\\\",   \\\"finished\\\": false }\"", "todo"=>{}}
Completed 400 Bad Request in 0ms (ActiveRecord: 0.0ms)
  
ActionController::ParameterMissing (param is missing or the value is empty: todo):
  
app/controllers/todos_controller.rb:51:in `todo_params'
app/controllers/todos_controller.rb:24:in `create'

I understand it might be something to do with strong params in my Rails controller, so when I removed the required params .require(:todo) the POST went through but the JSON fields are all null.

  {
    "id": 6,
    "title": null,
    "description": null,
    "finished": null
  }

I also tried to see if it was related to how the form data in my Vue app was sent to my Rails endpoint, so I tried JSON.stringify on the body but it didn't help.

This is Day 3 of me trying to solve this, without success. What did I do wrong? Any help much appreciated! :)

Firstly, you are submitting the title , description etc on the root of the JSON payload, but Rails is expecting them nested under todo .

Secondly, you are submitting the headers in the payload argument of the axios request - the first argument to post is the payload, the second argument is the configuration.

Thirdly, you shouldn't need to convert your data to a JSON string yet. axios should handle that for you.

Try this axios request instead:

axios.post(
  `http://localhost:3000/todos/`,
  { todo: this.postBody },
  { headers: { 'Content-Type': 'application/json; charset=UTF-8' }
)

After going down the rabbit hole of various error messages from Rails console, I realised it's nothing to do with the Rails API or the strong params, but how I structure the inputs to not POST a malformed JSON to my Rails endpoint. Reading the npm axios documentation helped loads.

So instead of a single text input field in my Vue app, I created a form with matching field names to my params:

<form @submit.prevent="onSubmit">
  <div class="input">
    <label for="title">Title</label>
    <input
       type="text"
       id="title"
       v-model="title">
  </div>
  <div class="input">
    <label for="name">Description</label>
    <input
      type="text"
      id="description"
      v-model="description">
  </div>
  <div class="input">
    <label for="finished">Finished?</label>
    <input
      type="text"
      id="finished"
      v-model="finished">
  </div>
  <div class="submit">
    <button type="submit">Submit</button>
  </div>
</form>

And then in my methods:

methods: {
    onSubmit() {
      const formData = {
      title: this.title,
      description: this.description,
      finished: this.finished      
      }
      axios({
        method: "POST",
        url: "http://localhost:3000/todos/",
        headers: {"Content-Type": "application/json"},
        data: formData
      })
      .then(response => { 
        console.log(response);
      })
      .catch(e => {
      this.errors.push(e)
      })
    },

And that did the trick! I can POST to my Rails API now. The problem for me is I still don't understand how and why it worked, other than it did! So any answers appreciated just for personal learning (but not critical to the original problem).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM