"detail": "CSRF Failed: Отсутствует токен CCSRF." при отправке данных поста из angular 13 в подключенную базу данных django

мне нужно отправить данные post из angular в DRF через angular form, но получаю ошибку

я проверил почти все ответы, доступные в интернете, но не нашел полезного ответа.

 "detail": "CSRF Failed: CSRF token missing."

//post logic sources.service.ts

import { Injectable } from '@angular/core';
import { sources } from './sources';
import { HttpClient } from '@angular/common/http';
import { Observable , of, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { HttpHeaders } from '@angular/common/http';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type':  'application/json',
//     Authorization: 'my-auth-token',
    cookieName: 'csrftoken',
    headerName:  'X-CSRFToken',
//     X-CSRFToken: 'sjd8q2x8vs1GJcOOcgnVGEkdP8f02shB',
//     headerName: 'X-CSRFToken',
//      headerName: ,
  })
};

@Injectable({
  providedIn: 'root'
})
export class SourcesService {
API_URL = 'http://127.0.0.1:8000/sourceapi.api';
  constructor(private http: HttpClient) { }

     /** GET sources from the server */
    Sources() :  Observable<sources[]> {
      return this.http.get<sources[]>(this.API_URL);
    }
      /** POST: add a new source to the server */
//       addSource(data: object) : Observable<object>{
//         return this.http.post<object>(this.API_URL,data, httpOptions);
//       }

addSource(source : sources[]): Observable<sources[]>{
  return this.http.post<sources[]> (this.API_URL, source, httpOptions);
 //console.log(user);
  }
  }


//add-source.component.ts

import { Component, OnInit } from '@angular/core';
import { sources } from '../sources';
import { SourcesService } from '../sources.service';
import { FormGroup, FormControl, ReactiveFormsModule} from '@angular/forms';

@Component({
  selector: 'app-add-source',
  templateUrl: './add-source.component.html',
  styleUrls: ['./add-source.component.css']
})
export class AddSourceComponent implements OnInit {
   // a form for entering and validating data
   sourceForm = new FormGroup({
    name : new FormControl(),
    url : new FormControl(),
    client : new FormControl(),
  });
  constructor(private sourcesService: SourcesService) { }

  ngOnInit(): void {
  }

  sourceData_post: any;
  saveSource(){
    if(this.validate_form()){
      this.sourceData_post = this.sourceForm.value;
      this.sourcesService.addSource(this.sourceData_post).subscribe((source)=>{
      alert('source added');
      });
      }

      else{
      alert('please fill from correctly');
      }
  }
  validate_form(){
    const formData = this.sourceForm.value;
    if(formData.name == null){
      return false;
      }else if(formData.url == null){
      return false;
      }else{
      return true;
      }
      }

}

// add-source.component.html


<div class="bread-crumb">
    <div>    <span>Add Source</span>   </div>
</div>
<div class="container flex">
        <div class="form">
            <form action="" [formGroup]="sourceForm" (ngSubmit)="saveSource()">

                <table>
                    <tr>
                        <td>Source Name:</td>
                        <td>
                            <input class="input" type="text" formControlName="name">
                        </td>
                    </tr>
                    <tr>
                        <td>Source URL:</td>
                        <td>
                            <input class="input" type="text" formControlName="url">
                        </td>
                    </tr>

                     <tr>
                        <td>Source client:</td>
                        <td>
                            <input class="input" type="text" formControlName="client">
                        </td>
                    </tr>
                    <tr>
                        <td colspan="2">
                            <div class="center">
                                <button type="submit">submit</button>
                            </div>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
</div>

я пытался

imports: [
  BrowserModule,
  AppRoutingModule,
  HttpClientModule,
  Ng2SearchPipeModule,
  FormsModule,
  ReactiveFormsModule,
  HttpClientXsrfModule,
  HttpClientXsrfModule.withOptions({
    cookieName: 'XSRF-TOKEN',
    headerName: 'X-XSRF-TOKEN',
    })

но не помогло

Обратите внимание: это угловой 13

вам нужно освободить csrf в файле views.py

from django.views.decorators.csrf import csrf_exempt

и тогда

@csrf_exempt
def index(request):
pass

(Частичный ответ)

Вы получаете это сообщение об ошибке, потому что защита CSRF активирована по умолчанию, а вы не отправляете токен CSRF. Кто-то написал хорошее описание того, что такое CSRF здесь

При первом запросе GET сервер посылает вам маркер CSRF в cookie, и вы должны посылать его обратно при каждом запросе, как cookie и как заголовок запроса. Сервер будет проверять, что значение CSRF в куки совпадает со значением CSRF, которое находится в заголовке.

Повторять это при каждом запросе может быть утомительно, поэтому в Angular есть встроенный модуль для этого: HttpClientXsrfModule который вы настраиваете здесь :

HttpClientXsrfModule.withOptions({
  cookieName: 'XSRF-TOKEN',
  headerName: 'X-XSRF-TOKEN',
})

Одна проблема заключается в том, что вы можете отменить это поведение, снова установив заголовок вручную здесь :

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type':  'application/json',
    cookieName: 'csrftoken',
    headerName:  'X-CSRFToken',
  })
};
[...]

addSource(source : sources[]): Observable<sources[]>{
  return this.http.post<sources[]> (this.API_URL, source, httpOptions);

Вам это не нужно. Просто оставьте так :

addSource(source : sources[]): Observable<sources[]>{
  return this.http.post<sources[]> (this.API_URL, source);

Другая проблема заключается в том, что название заголовка/куки для CSRF не является стандартным. Оно может быть CSRF, XSRF или любым другим. Конечно, если вы отправите его как CSRF, а сервер ожидает его как XSRF, он не будет обнаружен.

Как я вижу из комментариев к вопросу, сервер посылает вам это

Set-Cookie: csrftoken=sjd8q2xsdfgfhjgfnVGEkdP8f02shB

Итак, мы уверены, что имя cookie - csrftoken. Поэтому оно должно быть таким же в конфигурации HttpClientXsrfModule. Можете попробовать следующим образом

HttpClientXsrfModule.withOptions({
  cookieName: 'csrftoken', // << This one is certain
  headerName: 'X-XSRF-TOKEN', // << For this one, I don't know yet
})

Можете ли вы попробовать это с разными значениями для headerName? Желательно csrftoken также? Имя заголовка и имя cookie часто совпадают.

Обновление :

Согласно документации по Django, имя заголовка CSRF по умолчанию - HTTP_X_CSRFTOKEN. Поэтому вы можете попробовать следующее :

HttpClientXsrfModule.withOptions({
  cookieName: 'csrftoken',
  headerName: 'HTTP_X_CSRFTOKEN',
})

Логика на стороне front-end была правильной, причина отсутствия токена csrf заключалась в фреймворке Django rest. Как только я удалил @api_view из моего views.py и вернул json-ответ, все заработало.

Вернуться на верх