i need to send the post data from angular to DRF through angular form but geeting the error
i checked almost all the answers available on the internet but did not found and useful answer.
"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: 'sjd8q2x8hgjkvs1GJcOOcgnVGEkdP8f02shB',
// 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 >
<div> <span>Add Source</span> </div>
</div>
<div >
<div >
<form action="" [formGroup]="sourceForm" (ngSubmit)="saveSource()">
<table>
<tr>
<td>Source Name:</td>
<td>
<input type="text" formControlName="name">
</td>
</tr>
<tr>
<td>Source URL:</td>
<td>
<input type="text" formControlName="url">
</td>
</tr>
<tr>
<td>Source client:</td>
<td>
<input type="text" formControlName="client">
</td>
</tr>
<tr>
<td colspan="2">
<div >
<button type="submit">submit</button>
</div>
</td>
</tr>
</table>
</form>
</div>
</div>
i tried
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
Ng2SearchPipeModule,
FormsModule,
ReactiveFormsModule,
HttpClientXsrfModule,
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN',
})
but did not help
Note :- this is angular 13
CodePudding user response:
you need to exempt csrf in views.py
from django.views.decorators.csrf import csrf_exempt
and then
@csrf_exempt
def index(request):
pass
CodePudding user response:
(Partial answer)
You get this error message because the CSRF protection is activated by default and you don't send the CSRF token. Someone wrote a good description of what CSRF is here
On the first GET
request, the server sends you the CSRF token in a cookie, and you have to send it back on every request, as a cookie AND as a request header. The server will check that the CSRF value in the cookie matches with the CSRF value that is in the header.
It can be tedious to repeat that on every request so Angular has a builtin module for that : HttpClientXsrfModule
that you configured here :
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN',
})
One problem is that you override this behavior by setting again the header by hand here :
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);
You don't need that. Just leave it like this :
addSource(source : sources[]): Observable<sources[]>{
return this.http.post<sources[]> (this.API_URL, source);
Another problem is that the name for the CSRF header/cookie is not standard. It can be CSRF
, XSRF
, or whatever you want. Of course, if you send it as CSRF
and the server expects it as XSRF
, it will not be detected.
As I can see from the comments on the question, the server sends you that
Set-Cookie: csrftoken=sjd8q2xsdfgfhjgfnVGEkdP8f02shB
So we are sure that the cookie name is csrftoken
. So it should be the same in the configuration of the HttpClientXsrfModule
. Can you try like this
HttpClientXsrfModule.withOptions({
cookieName: 'csrftoken', // << This one is certain
headerName: 'X-XSRF-TOKEN', // << For this one, I don't know yet
})
Can you try this with different values for the headerName
? Preferably csrftoken
also ? header name and cookie name are often the same.
Update :
According to the Django documentation, the default CSRF header name is HTTP_X_CSRFTOKEN
. So you can try this :
HttpClientXsrfModule.withOptions({
cookieName: 'csrftoken',
headerName: 'HTTP_X_CSRFTOKEN',
})