Получение токена CSRF отсутствует или неверно для одного маршрута, но не для другого - PullRequest
0 голосов
/ 23 мая 2018

Работает на React / Django и столкнулся с проблемой, которую не могу решить.

На внешнем интерфейсе есть следующий JS, отправляющий данные в Django API.В этом случае rejectedDocuemts() отправляет массив filenames на сервер, так что в конечном итоге электронное письмо может быть создано и отправлено администратору для просмотра.Файлы, которые не соответствуют утвержденному списку расширений, попадают в этот список.Вот где возникает ошибка.

submitDocuments - это место, где отправляются файлы, которые соответствуют утвержденному списку расширений файлов.Весь объект files отправляется на сервер для сохранения.Этот работает отлично.

export function rejectedDocuments(filenames, id) {
    return function (dispatch) {
        axios.post(
            `${URL}/api/${(id)}/documents/upload/error`,
            filenames,
            { 
                headers: {
                    'content-type': 'text/plain',
                    'Authorization': 'JWT ' + sessionStorage.getItem('token')
                }
            }  
        )
    }
}

// Creates the error state to be used in components
export function submitDocuments(files, id) {
    return function (dispatch) {
        const uploaders = _.map(files, f => {
            const formData = new FormData();

            formData.append('file', f);
            return axios.post(
                `${URL}/api/${(id)}/documents/upload/success`,
                formData,
                { 
                    headers: { 
                        'content-type': 'multipart/form-data',
                        'Authorization': 'JWT ' +  sessionStorage.getItem('token')
                    }
                }          
            )
        });

        axios.all(uploaders)
        .then(response => {
            dispatch({ 
                type: SUBMIT,
                payload: response[0].status
            });
        })     
    }
}

В классе rejectedDocuments возникает ошибка (Forbidden (CSRF token missing or incorrect.): /api/84/documents/upload/error).Осматривая заголовки сети, они оба включают Cookie: csrftoken=xyz.У них обоих одинаковый токен CSRF.

Так что не уверен, что случилось или как обойти это.submitDocuments отлично работает, и они ничем не отличаются друг от друга, кроме того, что один отправляет файловый объект (submitDocuments), а другой отправляет массив строк (rejectedDocuments).

В любомСкорость, вот серверная часть Django:

# documents/urls.py
from django.conf.urls import url

from .views import (
    GetDocumentsAPIView, 
    RejectedDocumentsView,
    SaveDocumentAPIView
)

app_name = 'Documents'

urlpatterns = [
    url(r'^documents/upload/success', SaveDocumentAPIView.as_view(), name='upload'),
    url(r'^documents/upload/error', RejectedDocumentsView.as_view(), name='rejected'),
    url(r'^documents/', GetDocumentsAPIView.as_view(), name='documents')
]

# documents/views.py
import datetime

from django.contrib.auth import get_user_model
from django.db.models import Max
from django.views import View

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK, HTTP_400_BAD_REQUEST
from rest_framework.views import APIView

from .serializers import GetDocumentsSerializer, SaveDocumentSerializer
from .models import Uploads

User = get_user_model()

# Create your views here.
class GetDocumentsAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, *args, **kwargs):
        data = {'id': request.user.id}
        if kwargs:
            data['sur_id'] = kwargs.get('sur_id')
        serializer = GetDocumentsSerializer(data=data)
        if serializer.is_valid(raise_exception=True):
            return Response(serializer.data, status=HTTP_200_OK)
        return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

class RejectedDocumentsView(View):
    def post(request):
        print(request)

class SaveDocumentAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):
        max_id = Uploads.objects.all().aggregate(Max('id'))
        if max_id['id__max'] == None:
            max_id = 1
        else:    
            max_id = max_id['id__max'] + 1
        data = {
            'user_id': request.user.id,
            'sur_id': kwargs.get('sur_id'),
            'co': User.objects.get(id=request.user.id).co,
            'date_uploaded': datetime.datetime.now(),
            'size': request.FILES['file'].size
        }
        filename = str(data['co']) + '_' + str(data['sur_id']) + '_' + str(max_id) + '_' + request.FILES['file'].name
        data['doc_path'] = filename
        self.save_file(request.FILES['file'], filename)
        serializer = SaveDocumentSerializer(data=data)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data, status=HTTP_200_OK)
        return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

    # Handling the document
    def save_file(self, file, filename):
        with open('flu/' + filename, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)

Не публикует сериализацию, потому что RejectedDocumentsView(View) не нужен, и кажется, что ошибка происходит, прежде чем он делает этовид.

1 Ответ

0 голосов
/ 24 мая 2018

Я переоценил свой подход и обнаружил, что он ошибочен.Все данные передаются через один и тот же <form>, поэтому мне пришло в голову, что все они должны быть представлены через один и тот же FormData().

Я избавился от функции rejectedFiles и в итоге переписал функцию submitDocuments следующим образом:

export function submitDocuments(accepetedFiles, rejectedFiles, id) {
    return function (dispatch) {
        var formData = new FormData();

        // handle the accepetedfile and append to formData
        _.map(accepetedFiles, f => {
            formData.append('file', f);
        });
        // handle the rejectedfiles and append to formData
        _.map(rejectedFiles, r => {
            formData.append('rejected', r)
        })

        // Send the formDat to the server
        axios.post(
            `${URL}/api/${(id)}/documents/upload`,
            formData,
            {
                headers: {
                    'content-type': 'multipart/form-data',
                    'Authorization': 'JWT ' + sessionStorage.getItem('token')
                }
            }
        )
        // handling of the server response, should enable the modal to render
        .then(response => {
            dispatch({ 
                type: SUBMIT,
                payload: response.status
            });
        })  
    }
}

Это создает FormData(), который содержит все файлы и массивимен файлов с расширениями, которые не разрешены.

На сервере я избавился от следующего в urls.py:

url(r'^documents/upload/error', RejectedDocumentsView.as_view(), name='rejected'),

И в итоге получил следующее:

from django.conf.urls import url

from . import views

from .views import (
    GetDocumentsAPIView, 
    SaveDocumentAPIView
)

app_name = 'Documents'

urlpatterns = [
    url(r'^documents/upload', SaveDocumentAPIView.as_view(), name='upload'),
    url(r'^documents/', GetDocumentsAPIView.as_view(), name='documents')
]

Наконец, я обновил views.py для обработки массива, который поступал с request.POST:

class SaveDocumentAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, *args, **kwargs):

        # this for handlign the files we DON't want
        # files are not actually just sent to the server
        # just the files names so an email can be sent to the admin
        # to review the file types
        user_obj = User.objects.filter(id=request.user.id).first()

        if len(request.POST.getlist('rejected')) >= 1:
            send_mail(
                'Invalid File Extension',
                'The user %s %s with email %s has submitted the following files with invalid extensions: \n\n %s' % \
                (user_obj.first_name, user_obj.last_name, user_obj.email, request.POST.getlist('rejected')),
                'The Company <no-reply@company.com>',
                ['admin@company.com']
            )

        # this is for handling the files we do want
        # it writes the files to disk and writes them to the database
        for f in request.FILES.getlist('file'):
            max_id = Uploads.objects.all().aggregate(Max('id'))
            if max_id['id__max'] == None:
                max_id = 1
            else:    
                max_id = max_id['id__max'] + 1
            data = {
                'user_id': request.user.id,
                'sur_id': kwargs.get('sur_id'),
                'co': User.objects.get(id=request.user.id).co,
                'date_uploaded': datetime.datetime.now(),
                'size': f.size
            }
            filename = str(data['co']) + '_' + \
                    str(data['sur_id']) + '_' + \
                    str(max_id) + '_' + \
                    f.name
            data['doc_path'] = filename
            self.save_file(f, filename)
            serializer = SaveDocumentSerializer(data=data)
            if serializer.is_valid(raise_exception=True):
                serializer.save()
        return Response(status=HTTP_200_OK)

    # Handling the document
    def save_file(self, file, filename):
        with open('fileupload/' + filename, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)

Несмотря на небрежность, он делает то, что мне нужно, чтобы сделать.

...