如何使用Django作为后端,结合Django REST Framework进行发布。

3
我将仅使用Django作为后端。前端将使用React完成,而不使用Django模板。我正在使用django-rest-framework创建网站的rest api。
我为用户制作了一个序列化器。
class CustomUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        fields = (
            'id', 'email', 'password', 'username', 'first_name', 'last_name', 'date_of_birth', 'gender', 'mobile_number'
        )
        extra_kwargs = {
            'password': {'write_only': True},
            'id': {'read_only': True}
        }

    def create(self, validated_data):
        user = CustomUser.objects.create(
            email=validated_data['email'],
            username=validated_data['email'],
            first_name=validated_data['first_name'],
            last_name=validated_data['last_name'],
            date_of_birth=validated_data['date_of_birth'],
            gender=validated_data['gender'],
            mobile_number=validated_data['mobile_number']
        )
        user.set_password(validated_data['password'])
        user.save()
        return user

class CustomUserViewSet(viewsets.ModelViewSet):
    queryset = CustomUser.objects.all()
    serializer_class = CustomUserSerializer

在浏览器中,当我访问/custom/users/时,我可以查看用户。我还可以创建新用户,成功注册后返回该用户。如果使用httpie/curl也可以正常工作。
(djangoweb) vagrant@precise32:~$ http --json POST http://55.55.55.5/custom/users/ email="ter23minal2@gmail.com" password="terminal2123" username="t223erm" first_name="te2er" last_name="mi2nal" date_of_birth=1992-12-12 gender=2 mobile_number=66222666666336

它创建并返回新用户对象。
因此,我制作了一个注册用户的表单,但我没有从django服务器提供服务:
<form action="http://55.55.55.5/custom/users/" method="post" id="register-form">
    <input type="text" placeholder="email" name="email"/>
    ...
    ...
    <button id="post">Register</button>
</form>

并使用ajax提交表单。

// using the javscript Cookies library
var csrftoken = Cookies.get('csrftoken');

function csrfSafeMethod(method) {
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

$('#post').click(function(event) {
    event.preventDefault();
    var $form = $('#register-form');
    var data = $form.serialize();        
    $.ajax({
        type: "POST",
        url: "http://55.55.55.5/custom/users/",
        data: JSON.stringify(data),
        sucess: function() { console.log("Success!"); },
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        crossDomain:false,
        beforeSend: function(xhr, settings) {
          xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    });
});

现在,如果我点击按钮,问题就开始了:
  • 尽管我给了 event.preventDefault(),页面仍会自动加载到django服务器的url上(即http://55.55.55.5/custom/users/)。
  • 我收到一个错误信息:"detail": "CSRF Failed: CSRF token missing or incorrect."
如何使用django-rest-framework处理向django服务器的post请求?我没有找到任何有关此问题的帮助资料。请问您能指导我吗?谢谢。

你能澄清一下是谁在提供前端页面吗?是由服务器直接呈现的静态HTML/JS,还是由Django渲染的?假设它是一个静态文件,那么我不确定使用CSRF是否有意义,因为你应该使用令牌或类似的安全机制。 - dkarchmer
@dkarchmer 是的,它是由nodejs渲染的静态HTML/JS。如果我还不是用户,我该如何使用令牌?我以为只有在用户进行身份验证后才会分配令牌。您能指导一下我吗?谢谢。 - Robin
1个回答

3
您可以在注册和登录功能中使用csrf_exempt。以下是一个示例,演示如何创建注册和登录API。请查看我的登录API如何返回token。请参阅http://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
我尝试编辑我的代码以替换您的模型名称,但我没有测试它,所以您可能需要修复任何我有的拼写错误(或让我知道,这样我就可以修复它们)。
class AccountViewSet(viewsets.ModelViewSet):
    queryset = CustomUser.objects.all()
    serializer_class = CustomUserSerializer

    def get_permissions(self):

        if self.request.method in permissions.SAFE_METHODS:
            return (permissions.IsAuthenticated(),)

        if self.request.method == 'POST':
            return (permissions.AllowAny(),)

        return (permissions.IsAuthenticated(), IsAccountOwner(),)

    @csrf_exempt
    def create(self, request):
        '''
        When you create an object using the serializer\'s .save() method, the
        object\'s attributes are set literally. This means that a user registering with
        the password \'password\' will have their password stored as \'password\'. This is bad
        for a couple of reasons: 1) Storing passwords in plain text is a massive security
        issue. 2) Django hashes and salts passwords before comparing them, so the user
        wouldn\'t be able to log in using \'password\' as their password.

        We solve this problem by overriding the .create() method for this viewset and
        using Account.objects.create_user() to create the Account object.
        '''

        serializer = self.serializer_class(data=request.data)

        if serializer.is_valid():
            password = serializer.validated_data['password']
            confirm_password = serializer.validated_data['confirm_password']

            if password and confirm_password and password == confirm_password:

                user = CustomUser.objects.create_user(**serializer.validated_data)

                user.set_password(serializer.validated_data['password'])
                user.save()

                return Response(serializer.validated_data, status=status.HTTP_201_CREATED)

        return Response({'status': 'Bad request',
                         'message': 'Account could not be created with received data.'
                        }, status=status.HTTP_400_BAD_REQUEST)

class APILoginViewSet(APIView):

    @csrf_exempt
    def post(self, request, format=None):
        data = JSONParser().parse(request)
        serializer = LoginCustomSerializer(data=data)

        if serializer.is_valid():
            email = serializer.data.get('email')
            password = serializer.data.get('password')

            if not request.user.is_anonymous():
                return Response('Already Logged-in', status=status.HTTP_403_FORBIDDEN)

            user = authenticate(email=email, password=password)

            if user is not None:
                if user.is_active:
                    login(request, account)

                    serialized = UserSerializer(user)
                    data = serialized.data

                    # Add the token to the return serialization
                    try:
                        token = Token.objects.get(user=user)
                    except:
                        token = Token.objects.create(user=user)

                    data['token'] = token.key

                    return Response(data)
                else:
                    return Response('This account is not Active.', status=status.HTTP_401_UNAUTHORIZED)
            else:
                return Response('Username/password combination invalid.', status=status.HTTP_401_UNAUTHORIZED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def get(self, request, format=None):
        data_dic = {"Error":"GET not supported for this command"}
        return Response(data_dic, status=status.HTTP_400_BAD_REQUEST)

您可以在https://github.com/dkarchmer/django-aws-template查看一个完整的工作示例(免责声明,这是我的代码)。
希望这能帮到您。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接