使用axios向Django制作的API发出Put请求会导致500内部服务器错误

经历

当对由Django Rest Framework创建的API执行PUT操作时,会显示500(内部服务器错误)。

总结原因和解决方法。

设计

.
├── README.rst
├── apiv1
│   ├── __init__.py
│   ├── __pycache__
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── serializers.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── config
│   ├── __init__.py
│   ├── __pycache__
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── db.sqlite3
├── env
│   ├── bin
│   ├── include
│   ├── lib
│   └── pyvenv.cfg
├── manage.py
├── memo.txt
├── vue
│   ├── index.html
│   ├── style.css
│   └── vue_script.js
└── woop # Django models作成用
    ├── __init__.py
    ├── __pycache__
    ├── admin.py
    ├── apps.py
    ├── migrations
    ├── models.py
    ├── tests.py
    └── views.py

后端 Django rest framework

from django.db import models
from django.utils import timezone

# Create your models here.
import uuid


class Goal(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(verbose_name='目標', max_length=40)
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title
from rest_framework import serializers
# models
from woop.models import Goal, Task

class GoalSerializer(serializers.ModelSerializer):

    class Meta:
        model = Goal
        fields = ['id', 'title', 'created_at']
from django.shortcuts import render
from rest_framework import viewsets
from woop.models import Goal, Task
from .serializers import GoalSerializer, TaskSerializer


class GoalViewSet(viewsets.ModelViewSet):
    queryset = Goal.objects.all()
    serializer_class = GoalSerializer

前端 Vue(CDN)

我们在Vue中使用了CDN。这包括index.html文件、Vue的js文件以及css文件这三个部分。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
    <title>Document</title>
</head>

<body>
    <div id="app">
  <div>
    <input v-model='new_goal'>
    <button v-on:click='postGoal(new_goal)' class='btn'>登録</button>

    <p>目標の数 : {{ goals.length }}</p>
    <ul>
    <li v-for='(goal, index) in goals' v-bind:key='goal.id'>
      <div style='font-size: 5px;'>id : {{ goal.id }}</div>
      <div v-if='!isEditGoal' v-on:dblclick='isEditGoal = true'>
        {{ index }}: {{ goal.title }}</div>
      <div v-else><input type='text' v-model='goal.title' v-on:blur='updateGoal(goal.id, goal.title)'></div>
      <div style='font-size: 5px;'>{{ goal.created_at }}</div>
      <button v-on:click='deleteGoal(goal.id)' class='btn'>削除</button>
    </li>
    </ul>

    <pre>{{ $data }}</pre>
  </div>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src='/vue/vue_script.js'></script>
</body>
</html>
new Vue({
    el: '#app',
    data: function (){
        return {
            goals: {},
            new_goal: '',
            isEditGoal: false,
    }
},
    created() {
        this.reloadGoal();
    },
    methods: {
        postGoal: function (title) {
            const vm = this;
            axios.post('http://127.0.0.1:8000/api/v1/Goal/',
                { title: title })
                .then(response => { vm.reloadGoal(); })
        },
        deleteGoal: function (id) {
            const vm = this;
            axios.delete('http://127.0.0.1:8000/api/v1/Goal/' + id)
                .then(response => { vm.reloadGoal(); })
        },
        updateGoal: function (id, title) {
            const vm = this;
            console.log(vm)
            axios.put('http://127.0.0.1:8000/api/v1/Goal/' + id,
                { id: id, title: title })
                .then(response => { vm.isEditGoal = false })
                .catch((error) =>{ console.log(error) })
                .then(response => { vm.reloadGoal(); })
        },
        reloadGoal() {
            const vm = this;
            axios.get('http://127.0.0.1:8000/api/v1/Goal/')
                .then((response) => { vm.goals = response.data })
        },
    },
}
)

这段代码的输出如下所示。
每个目标都被注册了,并且显示了id、title和created_id。
如果双击标题部分,它会切换到input元素(通过切换isEditGoal的值为false和true),以便可以更新内容。

スクリーンショット 2020-04-18 23.52.30.png

点击输入表单的边框部分,或者使用Tab键切换到表单上的其他元素,以确认更新的内容,并返回到文本。

スクリーンショット 2020-04-18 23.51.15.png
spread.js:25 PUT http://127.0.0.1:8000/api/v1/Goal/3932e94a-1894-445e-ba2b-4415899fa2a8 500 (Internal Server Error)

Error: Request failed with status code 500
    at e.exports (spread.js:25)
    at e.exports (spread.js:25)
    at XMLHttpRequest.l.onreadystatechange (spread.js:25)

根据上述所述的错误。
当查询状态码为500时,会显示服务器错误,因此可能是Django端出现了问题。

原因的确认方法 de

当我使用诸如axios状态码500、vue put等关键词进行搜索时,我找到了一个关于相同情况的问题。
错误:请求失败,状态码为500#1989。

在上述问题中,这不是axios的问题,而是后端存在问题。由于开发者工具的网络提示请您查看有问题的部分,所以我决定去看一下。

スクリーンショット 2020-04-18 23.52.43.png

错误

您通过PUT方法调用了这个URL,但URL未以斜杠结尾且您已经设置了APPEND_SLASH。Django无法在保留PUT数据的同时重定向到斜杠结尾的URL。请将您的表单更改为指向 127.0.0.1:8000/api/v1/Goal/0c08d6b1-03cf-47f8-979e-60cb53704f52/(注意末尾的斜杠),或在Django设置中将APPEND_SLASH设置为False。

由于调用的URL末尾没有斜杠,因此我想要进行重定向。然而,Django无法在重定向时保持PUT数据不变,所以请修改设置。

如果Django的末尾没有斜杠,且settings.py中的APPEND_SLASH为True时,它会进行重定向。(默认值为True)

解决方案

我先在用Vue定义的`updateGoal`方法中的axios.put的第一个参数上添加了`+ ‘/’`。

        updateGoal: function (id, title) {
            const vm = this;
            console.log(vm)
            axios.put('http://127.0.0.1:8000/api/v1/Goal/' + id + '/', 
              { id: id, title: title })
                .then(response => { vm.isEditGoal = false })
                .catch((error) =>{ console.log(error) })
                .then(response => { vm.reloadGoal(); })
        },
ezgif.com-video-to-gif.gif
广告
将在 10 秒后关闭
bannerAds