CookieMonster's avatar

Put REQUEST 404 not found

I am creating a CRUD application where I can add ,edit or delete an article. I created an api to handle a put request to edit an article.

Article.vue:

<template>
    <div>
        <h2>Article</h2>
        <form @submit.prevent="addArticle()" class="mb-3">
         <input type="text" v-model="article.title" class="form-control" placeholder="Title">
         <textarea v-model="article.body" class="form-control" placeholder="Body"></textarea>
         <button type="submit" class="btn btn-light btn-block">Save</button>
        </form>
        <nav aria-label="Page navigation">
            <ul class="pagination">
                <li v-bind:class="[{disabled : !pagination.prev_page_url}]" @click="fetchArticles(pagination.prev_page_url)" class="page-item"><a class="page-link" href="#">Previous</a></li>
                <li class="page-item disabled"><a class="page-link text-dark" href="#">Page {{pagination.current_page}}/{{pagination.last_page}}</a></li>
                <li v-bind:class="[{disabled : !pagination.next_page_url}]" @click="fetchArticles(pagination.next_page_url)" class="page-item"><a class="page-link" href="#">Next</a></li>
            </ul>
        </nav>
        <div class="card card-body mb-2" v-for="article in articles" :key="article.id">
            <h3>{{article.title}}</h3>
            <p>{{article.body}}</p>
            <hr>
            <button @click="editArticle(article)" class="btn btn-primary">Edit</button>
            <button @click="deleteArticle(article.id)" class="btn btn-danger">Delete</button>
        </div>
    </div>
</template>

<script>
export default {
    data(){
        return {
            articles:[],
            article:{
                id:'',
                title:'',
                body:''
            },
            article_id:'',
            pagination:{},
            edit:false
        }
    },
    created(){
        this.fetchArticles();
    },
    methods:{
        fetchArticles(page_url){
            let vm = this;
            page_url = page_url || '/api/articles'
            fetch(page_url).
             then(res => res.json()).
             then(res => {
                 this.articles = res.data;
                 vm.makePagination(res.meta,res.links);
             }).
             catch(err => console.log(err));
        },
        makePagination(meta,links){
            let pagination = {
                current_page : meta.current_page,
                last_page : meta.last_page,
                next_page_url : links.next,
                prev_page_url : links.prev
            }
            this.pagination = pagination;
        },
        deleteArticle(article_id){
            if(confirm('Are you sure?')){
                fetch(`api/article/${article_id}`, {
                    method: 'delete'
                })
                .then(res => res.json())
                .then(data => {
                    alert('Article removed');
                    this.fetchArticles();
                })
                .catch(err => console.log(err));
            }
        },
    addArticle() {
      if (this.edit === false) {
        // Add
        fetch('api/article', {
          method: 'post',
          body: JSON.stringify(this.article),
          headers: {
            'content-type': 'application/json'
          }
        })
          .then(res => res.json())
          .then(data => {
            //this.clearForm();
            alert('Article Added');
            this.fetchArticles();
          })
          .catch(err => console.log(err));
      } else {
        // Update
        fetch('api/article', {
          method: 'put',
          body: JSON.stringify(this.article),
          headers: {
            'content-type': 'application/json'
          }
        })
          .then(res => res.json())
          .then(data => {
            //this.clearForm();
            alert('Article Updated');
            this.fetchArticles();
          })
          .catch(err => console.log(err));
      }
    },
        editArticle(article){
            this.edit = true;
            this.article.id = article.id;
            this.article_id = article.id;
            this.article.title = article.title;
            this.article.body = article.body;
        }
    }
}
</script>

It basically checks if my edit of boolean value in addArticle method that it will add new article if this.edit is false, otherwise it will edit the article.

api.php:


//Create a new article
Route::post('article','ArticleController@store');

// Update article
Route::put('article','ArticleController@store');

Now adding a new article works fine but when I tried to edit an article, it says put request is not found. I tried using postman to edit it by passing json data and it can update it but now i tried to implement it in the front end (vuejs) but everytime I hit save to update the article, I get this error. The put request route exist certainly.

How do I fix this?

Edit:

ArticleController:

 public function store(Request $request)
    {
        // Check if is PUT request, then get id and update else post request, create new article
        $article = $request -> isMethod('put') ? Article::findOrFail($request->article_id) :
         new Article;

         // store input in the relevant field
         $article->id = $request->input('article_id');
         $article->title = $request->input('title');
         $article->body = $request->input('body');
        
         //save article and return article
         if($article->save()){
             return new ArticleResource($article);
         }

    }
0 likes
19 replies
abhijeet9920's avatar

Hello @nickywan123 ,

How is user is able to update current article? Is user first clicks on Edit first then again he clicks on Save button?

Why don't you write the fetch function in editArticle() function itself rather than doing inside addArticle()?

CookieMonster's avatar

@abhijeet9920 Actually I am following a course tutorial on youtube to implement vue +laravel and this is how he did it.

https://www.youtube.com/watch?v=DJ6PD_jBtU0

At 20 minute mark onwards.

To answer your first question, I will probably need to set this.edit =false right before exiting the else block. Otherwise it will be a problem like what you mentioned.

Regarding your second question, I am just following the tutorial so I was also wondering why does he use the same function in controller to do add and edit article. It's just weird and confusing.

Snapey's avatar

When you call edit on your API, don't you need to follow REST principles and say what article you edit?

 // Update
        fetch('api/article', {
          method: 'put',

you call put but don't pass the article id in the URL

So, how is article being resolved in the controller method?

Also, it would help to know if you are seeing 404 in the ajax response or if your browser redirects to a 404 page, and also when you get the 404 is the record actually updated?

Snapey's avatar

This line

 this.article_id = article.id;

should be

 this.article.article_id = article.id;
CookieMonster's avatar

@snapey In my articlecontroller, I use ternary condition to check if the request is a PUT or otherwise. I updated my code above to show how it handles. I tested it on postman for a put request and a create new article and it was able to fetch the request.

When I get the 404, the record is not updated. Right after I click save (after editing the title or body), it doesn't bring me to any page, the chrome tools shows my console log PUT http://article.test/api/article 404 (Not Found).

Snapey's avatar
Snapey
Best Answer
Level 122

You have this line in the controller

$article = $request -> isMethod('put') ? Article::findOrFail($request->article_id)

so if the article is not found then a 404 error is thrown because you use findOrFail. Your ternary has nothing to do with it. Eloquent throws the 404 exception

You get a 404 because you have the mistake I mentioned in the editArticle function

It should look like this

        editArticle(article){
            this.edit = true;
            this.article.id = article.id;
            this.article.article_id = article.id;
            this.article.title = article.title;
            this.article.body = article.body;
        }

so that article_id is passed as an attribute in the form data

CookieMonster's avatar

So do I need to change this:

data(){
        return {
            articles:[],
            article:{
                id:'',
                title:'',
                body:''
            },
            article_id:'',
            pagination:{},
            edit:false
        }
    },

to

data(){
        return {
            articles:[],
            article:{
                id:'',
                title:'',
                body:'',
		article_id:""
            },
            pagination:{},
            edit:false
        }
    },

since this.article.article_id is looking for article_id attribute inside of article.

Snapey's avatar

It needs to be part of the data you submit to the server, so yes, it needs to be added

body: JSON.stringify(this.article),

here you post the data to the server, it needs to include a reference to the article that you are editing

CookieMonster's avatar

Change that part of the code to :

    data(){
        return {
            articles:[],
            article:{
                id:'',
                title:'',
                body:'',
                article_id:''
            },
            pagination:{},
            edit:false
        }
    },

as well as

    editArticle(article){
            this.edit = true;
            this.article.id = article.id;
            this.article.article_id = article.id;
            this.article.title = article.title;
            this.article.body = article.body;
        }

It still shows the same error, the id exists in the table db but I still get that exception. What else can I look out for?

Snapey's avatar

Check what is posted to the server with your browser network tools. Make sure a valid article id is being passed in the request data

CookieMonster's avatar

This is what is posted to server:

{id: 50, title: "New", body: "New onesdds"} body: "New onesdds" id: 50 title: "New"

which is the correct ID but article_id property is not in the JSON.

Snapey's avatar

so change your controller code to use id instead of article_id or debug your javascript to find out why you are not passing the variable.

Does your code even call editArticle ?

Snapey's avatar

You said that you changed the code to

            article:{
                id:'',
                title:'',
                body:'',
                article_id:''

but that the json sent is {id: 50, title: "New", body: "New onesdds"} so why is article_id not part of this structure when you say you added it

CookieMonster's avatar

Ok my bad, I forgot to save the code after I made the changes. It's working now.

Though, I remove article_id in the body and in the controller(replaced it with id) and use id instead and still works as usual. In this case, article_id is pretty much not needed?

Snapey's avatar

yep think so.

Please mark it answered if your problem is solved.

Please or to participate in this conversation.