Topic: fetch() method problems

Belovol pro asked 5 years ago


Good morning.

I realize, that  this question not concerns mdbreact itself. So I apologise in advance.

I have problem with fetch() from customised form component.

I load main.js bundle from django and then post request to same origin. It works as expected in Firefox, but responces with 403 in Chrome. 

I totally don't know where to dig. Help please!

Responces firefox first chrome next

Here is component's code

import React from 'react';
import { Container, Row, Col, Modal, ModalBody, ModalHeader, InputFile, Input, Button, Fa, ToastContainer, toast } from 'mdbreact';


/*
* Форма позволяет выгрузить информацию о:
* - координатах человека (телефон, почта, имя фамилия)
* - выбранном продукте - торте или капкейке.
* - выбранной начинке для торта
* - ориентировочном количестве гостей
* - ориентировочной дате торжества
* - фотографии торта и/или начинки
* Обязательными полями являются имя и телефон.
* Осуществляется валидация:
* - Имени и фамилии на предмет использования в нем только букв
* - Телефона на предмет использования в нем только десяти цифр без пробела
* - Даты на предмет использования формата XX.XX.XXXX, где Х - цифра. Проверяется, чтобы дата была
* не ранее текущей.(ПРОВЕРКА ПОКА НЕ РЕАЛИЗОВАНА)
* - Файлов на предмет соответствия типам jpeg и png.
*
* Логика работы формы такова:
* - В форму можно попасть из следующих мест
* - со страницы тортов
* - со траницы капкейков
* - с карточки торта
* - с карточки капкейка
* - Если человек попадает со страницы тортов или капкейков, то предполагается, что торт он не выбрал. В этом
* случае выводятся интерейсы выгрузки файла с изображением торта и начинки. В заголовок формы ничего
* не выводится.
* - Если человек попал из карточки торта, то в заголовок выводится название торта, интерфейс выгрузки фотографии торта не выводится.
* Если при заполнении формы человек выбрал начинку, то интерфейс выбора начинки пропадает.
* - Если человек попал из карточки капкейка, то в заголовок выводится название торта, интерфейс выгрузки фотографии капкейка не выводится.
* Интерфейс выбора фото начинки не выводится, предлагается выбрать начинки из фиксированного списка.
* - Когда идет команда Submit (Кнопка отправить), проверяются условия, обозначенные выше.
* - Если все хорошо, выводится сообщение "Дождитесь отправки", данные отправляются на сервер, полсе получения ответа
* пользоватль оповещается о статусе (досталено или нет). и форма автомматически закрывается.
* - Если пользователь нажмет крестик в правом верхнем углу - форма закрывается, данные не отправляются и не сохраняются.
*
*/


class FormsPage extends React.Component {
constructor(props) {
super(props);
this.submitHandler = this.submitHandler.bind(this);
this.changeHandler = this.changeHandler.bind(this);

this.state = {
tort_selected: this.props.tort ? [this.props.tort.id,true]:['',false],
fname: ['',false],
lname: ['',false],
email: ['',false],
phone: ['+7',false],
date: ['',false],
filling:[,false],
qty: ['',false],
tortPhoto:[,false,false],
fillPhoto:[,false,false],
what: this.props.fillings? [true,true]: [false,true],

}
}


fillingHandler = (e) => {
this.setState({...this.state, filling: e.target.selectedIndex == 0? ["Начинка не выбрана", false]: [e.target.selectedIndex, true]})
}

fileTortHandler = (e) => {
let file = e.target.files[0]
let valid = e.target.validity.valid
let type = e.target.files[0].type
this.setState({...this.state, tortPhoto: [file, valid ? ((type == "image/jpeg" || type == "image/png") ? true:false): false, valid]})

}

fileFillHandler = (e) => {
let file = e.target.files[0]
let valid = e.target.validity.valid
let type = e.target.files[0].type
this.setState({...this.state, fillPhoto: [file, valid ? ((type == "image/jpeg" || type == "image/png") ? true:false): false, valid]})


}

submitHandler (event){
event.preventDefault();
event.target.className += ' was-validated';
// Пустышка для отправки
let formData = new FormData();
const conf = {
method: "post",
body: formData,
mode: "same-origin",

};

// Осуществляем проверки, необходимые для начала отправки
if (!this.state.fname[1] || !this.state.phone[1]) {
toast.error('Сообщение не отправлено. Заполните необходимые поля', {
position: "top-right",
});
return}
else if (!this.state.tortPhoto[1] && this.state.tortPhoto[2]) {
toast.error('Сообщение не отправлено. Неверный тип файла изображения торта', {
position: "top-right",
});
return}

else if (!this.state.fillPhoto[1] && this.state.fillPhoto[2]) {
toast.error('Сообщение не отправлено. Неверный тип файла изображения начинки', {
position: "top-right",
});
return}

else{
// Пытаемся отправить
for (let a of Object.entries(this.state).values()) {
if (this.state[a[0]][1]==true) {
formData.append(a[0],this.state[a[0]][0])
// toast.success(a[0], {
// position: "top-right",
// });
}

}
fetch("/api/files/", conf)
.then(function(response) {
if(response.ok) {
return response.json();
}
throw new Error('Server side problems');
})
.then(response => toast.success("Данные успешно отправлены", {
position: "top-right",
}))
.catch( error => {
if (error.name == "TypeError") {
toast.error("Сообщение не отправлено. Проверьте сетевое соединение", {
position: "top-right",
});
toast.error("Если сеть в порядке - то лег наш сервер. Мы работаем над этим. А пока воспользуйтесь телефоном", {
position: "top-right",
});
}
else {
toast.error(error.name + "Сообщение не отправлено. На нашем сервере проблемы. Мы работаем над этим. А пока воспользуйтесь телефоном", {
position: "top-right",
});
}
}
)
};

}
// Этот параметр "show" по большому счету не нужен. Но трогать не будем - работает и бог с ним
//this.props.click_modal("show",event)


changeHandler(event){
this.setState({...this.state, [event.target.name]: [event.target.value, event.target.validity.valid]})
}

render() {
let selected_tort = this.state.tort_selected[1] ? "Вы выбрали "+ this.props.tort.name:null
return(
<div>
<ToastContainer
hideProgressBar={true}
newestOnTop={true}
autoClose={2000}
/>
<Modal size="lg" isOpen={this.props.modalOpen} className="mt-5 ">
<ModalHeader className="pink white-text" toggle={e => this.props.click_modal("show",e)}>
{selected_tort}

</ModalHeader>
<ModalBody>
<Row className="mt-6">
<Col md="">
<form className='needs-validation' onSubmit={this.submitHandler} noValidate>
<Row>
{/*---------------Имя - обязательное поле !!!----------------------------*/}
<div className="col-md-6 mb-3">
<label htmlFor="defaultFormRegisterNameEx" className="grey-text">Имя</label>
<input value={this.state.fname[0]} name='fname' onChange={this.changeHandler} pattern="[A-Za-zА-Яа-я]+" id="defaultFormRegisterNameEx" className="form-control" placeholder="Введите ваше имя" required/>
<div className="valid-feedback"></div>
<div className="invalid-feedback">Обязательное поле</div>
</div>
{/*---------------Фамилия - необязательное поле, но если вводит - контролируем!----------------------------*/}
<div className="col-md-6 mb-3">
<label htmlFor="defaultFormRegisterEmailEx2" className="grey-text">Фамилия</label>
<input value={this.state.lname[0]} name='lname' onChange={this.changeHandler} pattern="[A-Za-zА-Яа-я]+" id="defaultFormRegisterEmailEx2" className="form-control" placeholder="Введите вашу фамилию" />
<div className="valid-feedback"></div>
<div className="invalid-feedback">Обязательное поле</div>
</div>
</Row>
<Row>
{/*---------------Телефон - обязательное поле !!!----------------------------*/}
<div className="col-md-6 mb-3">
<label htmlFor="defaultFormRegisterPasswordEx4" className="grey-text">Телефон</label>
<input value={this.state.phone[0]} onChange={this.changeHandler} pattern="[+]7[0-9]{10}" id="defaultFormRegisterPasswordEx4" className="form-control" name='phone' placeholder="+7кодномер только цифры" required/>
<div className="invalid-feedback">Без пробелов и тире</div>
</div>
{/*---------------Почта - необязательное поле, но если вводит - контролируем!----------------------------*/}
<div className="col-md-6 mb-3">
<label className="grey-text">Почта</label>
<input value={this.state.email[0]} onChange={this.changeHandler} type="email" id="defaultFormRegisterEmailEx3" className="form-control" name='email' placeholder="Ваша почта"/>
</div>
</Row>
{/*------------------СТРОКА-3------------------------*/}
<Row>
{/*---------------Дата готовности - необязательное поле, но если вводит - проверяем----------------------------*/}
<div className="col-md-4 mb-3">
<label htmlFor="defaultFormRegisterPasswordEx4" className="grey-text">
{this.props.fillings?
"Дата готовности торта":
"Дата готовности капкейков"
}
</label>
<input value={this.state.date[0]} onChange={this.changeHandler} pattern="[0-9]{2}[.][0-9]{2}[.][0-9]{4}" id="defaultFormRegisterPasswordEx4" className="form-control" name='date' placeholder="XX.XX.XXXX"/>
<div className="invalid-feedback"></div>
</div>
{/*---------------Начинка - необязательное поле, выбор из ниспадающего списка----------------------------*/}
{this.props.fillings ?
<div className="form-group col-md-5 mb-3">
<label htmlFor="validatedFillingSelect" className="grey-text">
{this.state.filling[1] ?
"Вы выбрали " + this.props.fillings.find(e => e.id==this.state.filling[0]).name:
"Выберите начинку"}
</label>
<select className="custom-select browser-default" onChange={this.fillingHandler} id="validatedFillingSelect">
<option value="">Начинка не выбрана</option>
{this.props.fillings.map(fil =>
<option key={fil.id} value={fil.id}>{fil.name}</option>
)
}
</select>
<div className="invalid-feedback">Example invalid custom select feedback</div>
</div>:
<div className="form-group col-md-5 mb-3">
<label htmlFor="validatedFillingSelectex1" className="grey-text"> {this.state.filling[1] ? "Начинка выбрана": "Выберите начинку"}</label>
<select className="custom-select browser-default" onChange={this.fillingHandler} id="validatedFillingSelectex1">
<option value="">Начинка не выбрана</option>
<option value="1"> Шоколад</option>
<option value="1"> Сгущенка</option>
<option value="1"> Вишня</option>
<option value="1"> Клубника</option>
</select>
<div className="invalid-feedback">Example invalid custom select feedback</div>
</div>
}
{/*---------------Количество - необязательное поле, выбор из диапазона номеров----------------------------*/}
{this.props.fillings?
<div className="form-group col-md-3 mb-3">
<label htmlFor="validatedNumberGuests"className="grey-text">
{this.state.qty[1]?
"Количество гостей - " + this.state.qty[0]:
"Укажите число гостей"
}
</label>
<input className="form-control" type="number" id="validatedNumberGuests" onChange={this.changeHandler} name="qty" min="2" max="100"/>
</div>:
<div className="col-md-2 mb-3">
<label htmlFor="validatedNumberCakes" className="grey-text">
{this.state.qty[1]?
"Количество капкейков - " + this.state.qty[0]:
"Укажите количество капкейков"
}
</label>
<input className="form-control" type="number" id="validatedNumberCakes" onChange={this.changeHandler} name="qty" min="2" max="100"/>
</div>
}
</Row>
{this.props.fillings?
<Row>
{/*-------------Выбор файла с тортом -----------*/}
{selected_tort == null ?
<div className="col-lg-6 mb-3">
<div className="grey-text mb-2">
{(this.state.tortPhoto[2] && this.state.tortPhoto[1] == false)?
<div className="pink-text">
Неверный тип файла
</div>
:
"Файл с изображением торта"
}
</div>
<div className="custom-file ">
<input type="file" className="custom-file-input" onChange={this.fileTortHandler} id="validatedCustomFile" />
<label className="custom-file-label" htmlFor="validatedCustomFile">
{this.state.tortPhoto[2]? this.state.tortPhoto[0].name : <span className="small"> Файл с тортом</span>}
</label>

<div className="invalid-feedback">Файл не выбран</div>
</div>
</div>:
null
}
{/*-------------Выбор файла с начинкой -----------*/}
{!this.state.filling[1]?
<div className="d-flex flex-column col-lg-6 mb-3">
<div className="grey-text mb-2">
{(this.state.fillPhoto[2] && this.state.fillPhoto[1] == false)?
<div className="pink-text">
Неверный тип файла
</div>
:
"Файл с изображением начинки"
}
</div>
<div className="custom-file ">
<input type="file" className="custom-file-input" onChange={this.fileFillHandler} id="validatedCustomFile"/>
<label className="custom-file-label" htmlFor="validatedCustomFile">
{this.state.fillPhoto[1]? this.state.fillPhoto[0].name : <span className="small"> Файл с начинкой</span>}
</label>
<div className="invalid-feedback">Файл не выбран</div>
</div>
</div>:
null
}
</Row>:
null
}

<div className="d-flex mt-3 justify-content-center">
<Button color="pink" type="submit" outline>Отправить <Fa icon="paper-plane-o"/></Button>
</div>
</form>
</Col>
</Row>

</ModalBody>
</Modal>
</div>
);
}
};

export default FormsPage;

Belovol pro commented 5 years ago

firefox next  of corse


Belovol pro answered 5 years ago


I've solved this problem. So I leave this post here. May be it would be useful for somebody else. In fact it was django built-in CSRF protection. Firefox somehow knows about it and sets X-CSRFToken in request header. Chrome doesn't. So I should force this setting in header manually. I've used js-cookie package.

const conf = {
method: "post",
body: formData,
headers: {
'X-CSRFToken': Cookies.get('csrftoken'),
}



and then

 

fetch("/api/files/", conf)

Anna Morawska staff commented 5 years ago

Thank you for sharing your implementation with us,  it can help someone out there :) 



Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Resolved

Specification of the issue

  • User: Pro
  • Premium support: No
  • Technology: MDB React
  • MDB Version: 4.7.1
  • Device: 4xIntel Core i7-4510U CPU @2 GHz
  • Browser: Google Chrome V- 68.0.3440.8
  • OS: openSUSE Leap 42.3
  • Provided sample code: Yes
  • Provided link: Yes
Tags