如何仅使用 django 作为后端并使用 django-rest-framework 发布

Posted

技术标签:

【中文标题】如何仅使用 django 作为后端并使用 django-rest-framework 发布【英文标题】:How to use django only a backend and post with django-rest-framework 【发布时间】:2016-11-27 12:06:00 【问题描述】:

我将只使用 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/)。 我收到一个错误:“详细信息”:“CSRF 失败:CSRF 令牌丢失或不正确。”

如何使用 django-rest-framework 处理到 django 服务器的帖子?我没有找到解决这个问题的任何帮助材料。你能指导我怎么做吗?谢谢。

【问题讨论】:

你能澄清谁在为前端页面提供服务吗?是服务器直接渲染的静态 html/JS,还是 Django 渲染的?假设它是一个静态文件,那么我不确定使用 CSRF 是否有意义,因为您可能应该使用令牌或类似的安全性。 @dkarchmer 是的,它是由 nodejs 渲染的静态 HTML/JS。如果我还不是用户,如何使用令牌?我认为如果用户进行身份验证,就会给出令牌。你能指导我吗?谢谢。 【参考方案1】:

您可以使用csrf_exempt 进行注册和登录功能。例如,在这里您可以如何创建注册和登录 API。查看我的登录 API 如何返回令牌。见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 看到完整的工作示例(免责声明,这是我的代码)。

希望对你有帮助

【讨论】:

以上是关于如何仅使用 django 作为后端并使用 django-rest-framework 发布的主要内容,如果未能解决你的问题,请参考以下文章

使用 XML 或 JSON 文件作为数据库后端的 Django

如何使用Django 结合WebSocket 进行实时目标检测呢?以yolov5 为例,实现:FPS 25+ (1: 后端)

如何使用Django 结合WebSocket 进行实时目标检测呢?以yolov5 为例,实现:FPS 25+ (1: 后端)

与 Django rest 和 Angular/Cli 的条带集成

使用 AngularJs 发布请求时未设置 CSRF Coo​​kie - Django 后端

ssm框架如何将存有json对象的数组传到后端并接受