I am trying to make an AJAX request from my reactjs frontend to my django backend but I am getting this error upon POST. I'm not sure how to properly pass CSRF tokens around for my form POST to work properly
Forbidden (CSRF token missing or incorrect.): /api/contact
[05/Nov/2016 03:43:14] "POST /api/contact HTTP/1.1" 403 2502
I created a react component and added it into my form
CSRF component
import React from 'react';
import $ from 'jquery';
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = $.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const Csrf = () => {
let csrfToken = getCookie('csrftoken');
return (
<input type="hidden" name="csrfmiddlewaretoken" value={ csrfToken } />
);
};
export default Csrf;
Contact form component:
const ContactForm = ({ formOnChange, onFormSubmit, isFullNameValid, isEmailValid, isMsgValid }) => {
let rows = 10;
let tabindex = 8;
let isSubmitDisabled = !(isFullNameValid && isEmailValid && isMsgValid);
return(
<div className="form-wrapper">
<form className="contact-form" onChange={ formOnChange }>
<Csrf />
<input className={ isFullNameValid ? "" : "active" } type="text" name="fullname" placeholder="Full Name *" />
<div className="email-phone-group">
<input type="text" name="phonenumber" placeholder="Phone Number"/>
<input className={ isEmailValid ? "" : "active" } type="text" name="email" placeholder="Email *"/>
</div>
<textarea className={ isMsgValid ? "" : "active" } rows={ rows } tabIndex={ tabindex } name="message" placeholder="Message... *"/>
<button disabled={ isSubmitDisabled } className="contact-form-submit-btn" type="button" onClick={ onFormSubmit }>Submit</button>
</form>
</div>
);
};
My redux action calls this AJAX function
export function contactSubmission(contactSubmission) {
// thunk
return dispatch => {
console.log("contact form submitted");
console.log(contactSubmission);
$.ajax({
method: 'POST',
url: '/api/contact',
dataType: 'json',
success: payload => {
if (payload.error) {
return dispatch(contactSubmissionFailure());
} else {
return dispatch(contactSubmissionSuccess(payload));
}
},
error: payload => {
console.log("ajax error");
console.log(payload);
return dispatch(contactSubmissionFailure());
}
});
};
}
you should put the following code before $.ajax to send a CSRF code:
$.ajaxSetup({
headers: {"X-CSRFToken": getCookie("csrftoken")}
});
...THEN YOUR CODE
$.ajax({...});
You can notice that you have already set getCookie.
With this should work properly.
Related
I included a Vue form component in one of my Django templates. Now, I would like to send a CSRF token along with the data, since Django views require a CSRF token. Is there any way I can include it in my Vue form?
Here is my component:
<template>
<form #submit.prevent="formSubmit()">
<input type="text" class="form-control" v-model="amount">
<br>
<input type="text" class="form-control" v-model="price">
<br>
<button class="btn btn-primary" style="width: 100%">BUY</button>
</form>
</template>
<script>
import axios from 'axios'
export default {
mounted() {
console.log('Component mounted.')
},
data() {
return {
name: '',
description: '',
output: ''
};
},
methods: {
formSubmit() {
let currentObj = this;
axios.post('MY_URL', {
price: this.price,
amount: this.amount,
})
.then(function (response) {
currentObj.output = response.data;
}.bind(this))
.catch(function (error) {
currentObj.output = error;
});
},
}
}
</script>
First, acquire the token from the csrftoken cookie:
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
...or from querying the document:
const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value
Then, add the token value to your POST header:
axios.post('MY_URL', {
price: this.price,
amount: this.amount,
}, {
headers: {
'X-CSRFToken': csrftoken
}
})
I am trying to build a full-stack app with React and Django.
I am sending an API request from the Django server to React. This is my code how I do it:
posts/component.js:
import React, {useEffect, useState} from 'react'
import {
apiPostAction,
apiPostCreate,
apiPostList} from './lookup'
export function PostsComponent(props) {
const textAreaRef = React.createRef()
const [newPosts, setNewPosts] = useState([])
const handleBackendUpdate = (response, status) =>{
// backend api response handler
let tempNewPosts = [...newPosts]
if (status === 201){
tempNewPosts.unshift(response)
setNewPosts(tempNewPosts)
} else {
console.log(response)
alert("An error occured please try again")
}
}
const handleSubmit = (event) => {
event.preventDefault()
const newVal = textAreaRef.current.value
// backend api request
apiPostCreate(newVal, handleBackendUpdate)
textAreaRef.current.value = ''
}
return <div className={props.className}>
<div className='col-12 mb-3'>
<form onSubmit={handleSubmit}>
<textarea ref={textAreaRef} required={true} className='form-control' name='post'>
</textarea>
<button type='submit' className='btn btn-primary my-3'>Post</button>
</form>
</div>
<PostsList newPosts={newPosts} />
</div>
}
export function PostsList(props) {
const [postsInit, setPostsInit] = useState([])
const [posts, setPosts] = useState([])
const [postsDidSet, setPostsDidSet] = useState(false)
useEffect(()=>{
const final = [...props.newPosts].concat(postsInit)
if (final.length !== posts.length) {
setPosts(final)
}
}, [props.newPosts, posts, postsInit])
useEffect(() => {
if (postsDidSet === false){
const handlePostListLookup = (response, status) => {
if (status === 200){
setPostsInit(response)
setPostsDidSet(true)
} else {
alert("There was an error")
}
}
apiPostList(handlePostListLookup)
}
}, [postsInit, postsDidSet, setPostsDidSet])
const handleDidRepost = (newPost) => {
const updatePostsInit = [...postsInit]
updatePostsInit.unshift(newPost)
setPostsInit(updatePostsInit)
const updateFinalPosts = [...posts]
updateFinalPosts.unshift(posts)
setPosts(updateFinalPosts)
}
return posts.map((item, index)=>{
return <Post
post={item}
didRepost={handleDidRepost}
className='my-5 py-5 border bg-white text-dark'
key={`${index}-{item.id}`} />
})
}
export function ActionBtn(props) {
const {post, action, didPerformAction} = props
const likes = post.likes ? post.likes : 0
const className = props.className ? props.className : 'btn btn-primary btn-sm'
const actionDisplay = action.display ? action.display : 'Action'
const handleActionBackendEvent = (response, status) =>{
console.log(response, status)
if ((status === 200 || status === 201) && didPerformAction){
didPerformAction(response, status)
}
}
const handleClick = (event) => {
event.preventDefault()
apiPostAction(post.id, action.type, handleActionBackendEvent)
}
const display = action.type === 'like' ? `${likes} ${actionDisplay}` : actionDisplay
return <button className={className} onClick={handleClick}>{display}</button>
}
export function ParentPost(props){
const {post} = props
return post.parent ? <div className='row'>
<div className='col-11 mx-auto p-3 border rounded'>
<p className='mb-0 text-muted small'>Repost</p>
<Post hideActions className={' '} post={post.parent} />
</div>
</div> : null
}
export function Post(props) {
const {post, didRepost, hideActions} = props
const [actionPost, setActionPost] = useState(props.post ? props.post : null)
const className = props.className ? props.className : 'col-10 mx-auto col-md-6'
const handlePerformAction = (newActionPost, status) => {
if (status === 200){
setActionPost(newActionPost)
} else if (status === 201) {
if (didRepost){
didRepost(newActionPost)
}
}
}
return <div className={className}>
<div>
<p>{post.id} - {post.content}</p>
<ParentPost post={post} />
</div>
{(actionPost && hideActions !== true) && <div className='btn btn-group'>
<ActionBtn post={actionPost} didPerformAction={handlePerformAction} action={{type: "like", display:"Likes"}}/>
<ActionBtn post={actionPost} didPerformAction={handlePerformAction} action={{type: "unlike", display:"Unlike"}}/>
<ActionBtn post={actionPost} didPerformAction={handlePerformAction} action={{type: "repost", display:"Repost"}}/>
</div>
}
</div>
}
posts/lookup.js:
import {backendLookup} from '../lookup'
export function apiPostCreate(newPost, callback){
backendLookup("POST", "/posts/create/", callback, {content: newPost})
}
export function apiPostAction(postId, action, callback){
const data = {id: postId, action: action}
backendLookup("POST", "/posts/action/", callback, data)
}
export function apiPostList(callback) {
backendLookup("GET", "/posts/", callback)
}
lookup/component.js:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
export function backendLookup(method, endpoint, callback, data) {
let jsonData;
if (data){
jsonData = JSON.stringify(data)
}
const xhr = new XMLHttpRequest()
const url = `http://localhost:8000/api${endpoint}`
xhr.responseType = "json"
const csrftoken = getCookie('csrftoken');
xhr.open(method, url)
xhr.setRequestHeader("Content-Type", "application/json")
if (csrftoken){
xhr.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest")
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
// xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
// xhr.setRequestHeader("X-CSRFToken", csrftoken)
xhr.onload = function() {
callback(xhr.response, xhr.status)
}
xhr.onerror = function (e) {
console.log(e)
callback({"message": "The request was an error"}, 400)
}
xhr.send(jsonData)
}
Also, when I run my serverside, it all works well on Django.
When I am on my React localhost and I try to submit some text in the text field after submitting there is no content sent. When I console.log() it shows that after typing some text content = null is sent.
Also, when I lick the "Repost" button. I get this error in the console: components.js:45 POST http://localhost:8000/api/posts/action/ 400 (Bad Request)
Any ideas? Why is my POST request wrong?
I was trying to implement on keyup search with django and jquery following this video
but when I try the code everytime it returns forbidden 403!
the html page:
<div class="search-input">
<form>
{% csrf_token %}
<button>بحـث</button>
<input
type="text"
name="q"
id="search"
placeholder="Est: Game of thrones, Vikings or Deadpool"
/>
</form>
</div>
<div class="search-results"></div>
urls.py:
path('search/', views.search_titles, name='search')
views.py:
def search_titles(request):
if request.method == 'POST':
search_text = request.POST['search_text']
else:
search_text = ''
search_results = Media.objects.filter(
is_published=True, title__contains=search_text)[:5]
context = {
'search_results': search_results
}
return render(request, 'partials/_search_results.html', context)
the jquery file:
$("#search").on("keyup", function() {
$.ajax({
type: "POST",
url: "/search/",
data: {
search_text: $("#search").val(),
csrfmiddlewaretoken: $("input[name=csrfmiddlewaretoken]").val()
},
success: searchSuccess,
dataType: "html"
});
});
function searchSuccess(data, textStatus, jqXHR) {
$("search-results").html(data);
}
});
search_results.html ( that i don't even know the reason of )
{% if search_results.count > 0 %} {% for result in search_results %}
<li>
{{ result.title }}
</li>
{% endfor %} {% else %}
<li>Nothing to show</li>
{% endif %}
This is likely happening because you are not properly setting up AJAX to pass your CSRF token. The Django docs have a great section on how to set this up. Here's the short story.
First, you'll need to acquire the token:
Here's how to do it if CSRF_USE_SESSIONS and CSRF_COOKIE_HTTPONLY in your settings are False:
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
OR, Here's how to do it if CSRF_USE_SESSIONS or CSRF_COOKIE_HTTPONLY in your settings are True:
{% csrf_token %}
<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
</script>
IN BOTH CASES, you'll also need to set up the token on your AJAX request:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
I have the following code below but I still get "CSRF token missing or incorrect." error. anyone spot what I have done wrong here?
<form method="post" action="" id="registration">
{{ form.as_p }}
<input type="submit" value="{% trans 'Submit' %}" />
</form>
<script>
$('#registration').submit(function(e){
e.preventDefault(); //prevent default form submit
$.ajax({
url: '/accounts/registerAjax/',
type: 'POST',
contentType: "application/json;charset=utf-8",
dataType: "json",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'id_username': $("#id_username").val(),
'id_email': $("#id_email").val()
},
success: function() {
alert('Test');
},
error: function(errorThrown){
console.log(errorThrown);
alert('Error');
alert(errorThrown);
}
});
From the docs.
on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token.
As a first step, you must get the CSRF token itself. The recommended source for the token is the csrftoken cookie, which will be set if you’ve enabled CSRF protection for your views as outlined above.
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
The above code could be simplified by using the jQuery cookie plugin to replace getCookie:
var csrftoken = $.cookie('csrftoken');
I have an Edit Form page which edits existing posts. i wanted to put a Preview link into this page. what i want is ; when i click preview link it must ;
take form element's current values and write them into my
Post_Preview Model.
then , ajax function must redirect to my *show_preview* page which i defined in views and urls.
and in show_preview page (preview.html) it must be rendered. (form's
current values)
this is how it must works. And here are my codes :
and my edit-form page:
<form method="post" action="">
{% csrf_token %}
<input type="hidden" id="post_owner" value="{{ post.owner }}"/></td>
<input type="hidden" id="post_id" value="{{ post.id }}"> </td>
{{ form.title }}
{{ form.body }}
Preview
<input type="submit" value="Save" style="cursor: pointer;"/>
</form>
ajax Function:
$(function(){
$('#preview').click(function() {
var title = $('#id_title').val();
var body = $('#id_body').val();
var owner = $('#post_owner').val(); //hidden value at form page
var id = $('#post_id').val(); //hidden value at form page
var ajaxOptions = {
type:'post',
url : '/admin/post/save_preview/', //save_preview's url
data : {
'title' : title,
'body' : body,
'owner' : owner,
'id' : id
},
success: function(){
window.open("/blog/"+owner+"/preview/"+id); //show_preview's url
},
error: function(){
alert('There is an Error'); //this is what i see when click preview link.
}
};
$.ajax(ajaxOptions);
});
});
my save_preview view:
def save_preview(request):
title = request.POST['title']
body = request.POST['body']
owner = request.POST['owner']
post_id = request.POST['id']
try:
preview = Post_Preview(id=post_id, title=title, body=body, owner=owner)
preview.save()
except:
pass
return HttpResponse(200)
my show_preview view:
def show_preview(request,post_id,username):
preview = Post_Preview.objects.get(id=post_id)
return render_to_response('preview.html',{'post': preview}, context_instance=RequestContext(request))
my related url lines:
url(r'^admin/post/save_preview/', view='save_preview' ,name='save_preview'),
url(r'^blog/(?P<username>[-\w]+)/preview/(?P<post_id>\d+)', view='show_preview', name='show_preview'),
When i click Preview link at edit-form page : it shows 'There is an Error' error; which is defined in ajax function.
thank you!
edit : there is more code before my ajax function in js file (which are related to django-ajax relation ):
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
// Send the token to same-origin, relative URLs only.
// Send the token only if the method warrants CSRF protection
// Using the CSRFToken value acquired earlier
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
edit 2: when i add try/except blocks for save_preview ; i didnt throw popup error page. it redirected to save_preview page and threw and error :
Post_Preview matching query does not exist.
156. preview = Post_Preview.objects.get(id=post_id)
You're not sending the csrf token in $.ajax.