"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-ответ, все заработало.