why getting 'WSGIRequest' object has no attribute 'data' error? - reactjs

I am trying to use card payment through stripe in react Js and Django. I am following https://betterprogramming.pub/how-to-integrate-django-react-app-with-stripe-payments-95709b3f23e5 this tutorial.
frontend
const handleSubmit = async (event) => {
event.preventDefault();
const card = elements.getElement(CardElement);
const {paymentMethod, error} = await stripe.createPaymentMethod({
type: 'card',
card: card
});
ApiService.saveStripeInfo({
email, payment_method_id: paymentMethod.id})
.then(response => {
console.log(response.data);
}).catch(error => {
console.log(error)
})
}
export const api = axios.create({
baseURL: API_URL,
headers: {
"Content-type": "application/json"
}
});
export default class ApiService{
static saveStripeInfo(data={}){
return api.post(`${API_URL}/payments/save-stripe-info/`, data)
}
}
server
#api_view(['POST'])
def test_payment(request):
test_payment_intent = stripe.PaymentIntent.create(
amount=1000, currency='pln',
payment_method_types=['card'],
receipt_email='test#example.com')
return Response(status=status.HTTP_200_OK, data=test_payment_intent)
def save_stripe_info(request):
print('this => ',request.data)
data = request.data
email = data['email']
payment_method_id = data['payment_method_id']
# creating customer
customer = stripe.Customer.create(
email=email, payment_method=payment_method_id)
return Response(status=status.HTTP_200_OK,
data={
'message': 'Success',
'data': {'customer_id': customer.id}
}
)
but whenever I click the submit button it given me following error
AttributeError: 'WSGIRequest' object has no attribute 'data'
[12/Dec/2021 21:55:57] "POST /payments/save-stripe-info/ HTTP/1.1" 500 71355
for full code please visit https://betterprogramming.pub/how-to-integrate-django-react-app-with-stripe-payments-95709b3f23e5

According to the docs there is no data member of WSGIRequest. You will need to refer to the body attribute instead.

data is part of the rest_framework. You need to decorate the view with an api_view.
Just add "#api_view(['POST'])" before the def save_stripe_info(request).
It should be:
#api_view(['POST'])
def save_stripe_info(request):
print('this => ',request.data)
data = request.data
email = data['email']
payment_method_id = data['payment_method_id']
# creating customer
customer = stripe.Customer.create(
email=email, payment_method=payment_method_id)
return Response(status=status.HTTP_200_OK,
data={
'message': 'Success',
'data': {'customer_id': customer.id}
}
)
or just load the body in json.
data = json.loads(request.body)

Related

Why does my axios create function in React does not work?

I'm working on a fullstack app with Express and React. For the calls to the backend, I use axios (version 1.1.2). Before this version, I was using a function to avoid writing the same calls to the database every time. Now, I get this error:
POST http://localhost:5005/api/auth/signup 400 (Bad Request)
Where does it come from?
This is my non working code:
const API_URL = process.env.REACT_APP_API_URL
export default axios.create({
baseURL: `${API_URL}/api`,
timeout: 1000,
headers: {
"Content-type": "application/json",
},
})
// Here, http refers to the axios.create function
class AuthService {
signup(data: any) {
return http.post("/auth/signup", data)
}
}
const handleSubmit = (e: React.ChangeEvent<HTMLFormElement>) => {
authService
.signup(inputs)
...rest
}
But this is working:
const handleSubmit = (e: React.ChangeEvent<HTMLFormElement>) => {
axios
.post(`${API_URL}/api/auth/signup`, inputs)
...rest
}
Thanks for your help!
EDIT:
When I submit the form, on the front end I get this error, which on the back end would be returned if an input is empty:
Please provide your full name.
Back end code:
if (!fullName) {
return res
.status(400)
.json({ message: "Please provide your full name." })
}
EDIT 2:
I tried to add a console.log on the back end with the req.body and this is what I get:
{}
This is the full backend code:
router.post("/signup", (req, res, next) => {
const { email, fullName, password } = req.body
console.log(req.body)
if (!fullName) {
return res
.status(400)
.json({ message: "Please provide your full name." })
}
User.findOne({ email })
.then(foundUser => {
...rest
return User.create({
email,
fullName,
password,
}).then(createdUser => {
const payload = { user: createdUser }
const authToken = jwt.sign(
payload,
process.env.TOKEN_SECRET,
jwtConfig
)
res.status(201).json({
user: createdUser,
authToken: authToken,
})
})
})
.catch(err => console.log(err))
})
And my terminal returns this:
POST /api/auth/login 401 16.919 ms - 39
EDIT 3:
This is my Express app.js:
require("dotenv/config")
require("./db")
const express = require("express")
const app = express()
require("./config")(app)
const allRoutes = require("./routes/index")
app.use("/api", allRoutes)
require("./error-handling")(app)
module.exports = app
You can find the full repo here: https://github.com/JulSeb42/tsx-express-jwt

Post local image to facebook group via Graph API

I'm working on a React project with Facebook user integration.
I need to post a client-side generated image to a private group on behalf of the logged-in user.
Using the {group-id}/photo endpoint I can successfully post a picture which already exists on the web providing the url and caption parameters:
const postImageUrlToFacebook = () => {
const url = "https://upload.wikimedia.org/wikipedia/commons/e/e0/New.gif";
let caption = "test upload url image from React";
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", { url, caption })
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
};
The definition of the httpFacebookGraphClient is the following:
import axios, { AxiosRequestConfig } from "axios";
const httpFacebookGraphClient = (token: string | null) => {
const defaultOptions = {
baseURL: "https://graph.facebook.com/v14.0",
method: "get",
// withCredentials: true,
headers: {
"Content-Type": "application/json",
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the access token parameter for any request
instance.interceptors.request.use((config: AxiosRequestConfig): AxiosRequestConfig => {
if (!config) {
config = {};
}
if (!config.params) {
config.params = {};
}
config.params.access_token = token;
config.params.limit = "999";
return config;
});
return instance;
};
export default httpFacebookGraphClient;
I would now need to start from a default svg, modifiy some g tags inside of it via javascript, convert to jpg and post it to a facebook group.
Let's suppose the starting SVG is the following:
<svg id="Livello_1" data-name="Livello 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 107"><defs><style>.cls-1,.cls-3,.cls-4,.cls-5{fill:#ffc000;}.cls-2{fill:#fff;}.cls-3,.cls-4,.cls-5{stroke:#fff;stroke-miterlimit:10;}.cls-3{stroke-width:0.87px;}.cls-4{stroke-width:0.79px;}.cls-5{stroke-width:0.65px;}</style></defs><title>bannerino scegli il tuo sconto</title><path class="cls-1" d="M136.88,2.63a10,10,0,0,1,10,10V94.37a10,10,0,0,1-10,10H13.13a10,10,0,0,1-10-10V12.63a10,10,0,0,1,10-10H136.88m0-2H13.13a12,12,0,0,0-12,12V94.37a12,12,0,0,0,12,12H136.88a12,12,0,0,0,12-12V12.63a12,12,0,0,0-12-12h0Z"/></svg>
I started from the end, trying to post a local image to the Facebook group before trying to build the image, but I'm already stuck.
Reading the Facebook api docs at this link I found this sentence on the url parameter;
The URL of a photo that is already uploaded to the Internet. You must specify this or a file attachment
mentioning a file attachment.
Again on the Facebook docs I found this that explain how to upload a file to Facebook to use it in subsequent api calls, but I can't make it to work.
Anyone could give me a hint on how to proceed?
I found the way around this and I can successfully post an image selected by input field client side.
React component state:
const [fileToUpload, setFileToUpload] = useState<any>();
inputFileChangeHandler:
const inputFileChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.currentTarget != null && event.currentTarget.files != null) {
setFileToUpload(event.currentTarget.files[0]);
}
};
uploadImageToFacebook:
const uploadImageToFacebook = () => {
let caption = "test upload local image from React";
const fileReader = new FileReader();
fileReader.onloadend = async () => {
if (fileReader.result != null) {
const photoData = new Blob([fileReader.result], { type: "image/png" });
const formData = new FormData();
formData.append("source", photoData);
formData.append("caption", caption);
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
};
fileReader.readAsArrayBuffer(fileToUpload);
};
On jsx:
<input type="file" id="file-input" onChange={inputFileChangeHandler} />
<button onClick={uploadImageToFacebook}>Invia</button>

Django/React 415 (Unsupported Media Type)

I'm getting POST http://localhost:8000/api/reports/ 415 (Unsupported Media Type) when I try to submit the form from React and I don't understand what's the problem.
Here's the code.
models.py
class Report(models.Model):
category = models.ForeignKey(Category, on_delete=models.PROTECT)
description = models.TextField()
address = models.CharField(max_length=500)
reporter_first_name = models.CharField(max_length=250)
reporter_last_name = models.CharField(max_length=250)
reporter_email = models.CharField(max_length=250)
reporter_phone = models.CharField(max_length=250)
report_image_1 = models.ImageField(_("Image"), upload_to=upload_to, null=True, blank=True)
report_image_2 = models.ImageField(_("Image"), upload_to=upload_to, null=True, blank=True)
report_image_3 = models.ImageField(_("Image"), upload_to=upload_to, null=True, blank=True)
date = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ('-date',)
def __str__(self):
return self.description
I also tried to put default values for images, but I still get the error.
serializers.py
class ReportSerializer(serializers.ModelSerializer):
categoryName = CategorySerializer(many=False, read_only=True, source='category')
class Meta:
model = Report
fields = '__all__'
views.py
class ManageReports(viewsets.ModelViewSet):
serializer_class = ReportSerializer
parser_classes = [MultiPartParser, FormParser]
def get_object(self, queryset=None, **kwargs):
id = self.kwargs.get('pk')
return get_object_or_404(Report, id=id)
def get_queryset(self):
return Report.objects.all()
This is the code responsible for the submit.
report.js
const initialPostData = Object.freeze({
category: '',
address: '',
description: '',
reporter_first_name: '',
reporter_last_name: '',
reporter_email: '',
reporter_phone: '',
});
const [postData, updatePostData] = useState(initialPostData);
const [postImage1, setPostImage1] = useState({image: null});
const [postImage2, setPostImage2] = useState({image: null});
const [postImage3, setPostImage3] = useState({image: null});
const handleChange = (e) => {
if([e.target.name] == 'reporter_image_1') {
setPostImage1({
image: e.target.files[0],
});
} else if([e.target.name] == 'reporter_image_2') {
setPostImage2({
image: e.target.files[0],
});
} else if([e.target.name] == 'reporter_image_3') {
setPostImage3({
image: e.target.files[0],
});
} else if([e.target.name] == 'category') {
updatePostData({
...postData,
[e.target.name]: e.target.value,
});
} else {
updatePostData({
...postData,
[e.target.name]: e.target.value.trim(),
});
}
};
const handleSubmit = async(e) => {
e.preventDefault();
let formData = new FormData();
formData.append('category', postData.category);
formData.append('address', postData.address);
formData.append('description', postData.description);
formData.append('reporter_first_name', postData.reporter_first_name);
formData.append('reporter_last_name', postData.reporter_last_name);
formData.append('reporter_email', postData.reporter_email);
formData.append('reporter_image_1', postImage1.image);
formData.append('reporter_image_2', postImage2.image);
formData.append('reporter_image_3', postImage3.image);
const submitForm = await submitReport(formData);
And here's the submitReport API
API.js
const axiosInstance = axios.create({
baseURL: 'http://localhost:8000/api/',
timeout: 5000,
headers: {
Authorization: accessToken
? 'JWT ' + accessToken
: null,
'Content-Type': 'application/json',
accept: 'application/json',
},
});
// Submit Report Form
const submitReport = async(formData) => {
const { data } = await axiosInstance.post('reports/', {...formData });
return data;
}
These should be all the files needed to understand what's going on.
Thanks in advance.
Your parser_classes attribute on viewset is wrong. If you want to accept JSON format in request body you need to include JSONParser in your parser_classes attribute.
See the related section from here in DRF docs.
I found the solution, I was using the spread operator in the submitReport function, but instead I don't need to use it, so:
API.js
// Submit Report Form
const submitReport = async(formData) => {
const { data } = await axiosInstance.post('reports/', {...formData });
return data;
}
becomes
// Submit Report Form
const submitReport = async(formData) => {
const { data } = await axiosInstance.post('reports/', formData);
return data;
}

Upload an image with reactjs and mongodb

I'm trying to make an image uploader, thanks to a form, in reactjs.
I've created an api in mongodb (thanks to express, mongoose, etc.), and i'm trying to use it in order to upload an image.
Actually, i would like to send an image file to the cloud (with Cloudinary), and get the url.
That is my form and methods :
class Header extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
data: [],
uploading: false,
image: [],
apiKey: 'xxx'
};
}
onChangeImage = e => {
this.setState({[e.target.name]: Array.from(e.target.files)});
};
sendImage = files => {
const formData = new FormData();
files.forEach((file, i) => {
formData.append(i, file)
});
fetch('http://localhost:3000/image-upload', {
method: 'POST',
headers : new Headers({
'Content-Type' : 'application/x-www-form-urlencoded',
'x-access-token' : this.state.apiKey
}),
body: formData
})
.then(res => res.json())
.then(image => {
this.setState({
uploading: false,
image
});
return true;
});
return false;
};
handleSubmit = (event) => {
event.preventDefault();
const { image } = this.state;
this.sendImage(image);
};
render() {
return(
<form onSubmit={this.handleSubmit} className="formAdd">
<input type='file' id="image" name="image" onChange={this.onChangeImage} />
<button className="contact-form-btn">
Send<i className="fa fa-long-arrow-right" aria-hidden="true"></i>
</button>
</form>
)
}
About my API Controller :
const cloudinary = require('cloudinary');
module.exports = {
create: function(req, res) {
cloudinary.config({
cloud_name: 'xxxx',
api_key: 'xxxxx',
api_secret: 'xxxxx'
});
const path = Object.values(Object.values(req.body.files)[0])[0].path;
cloudinary.uploader.upload(path)
.then(image => res.json([image]));
},
};
The error code that I get is 500 'TypeError: Cannot convert undefined or null to object'.
Indeed, it not found Object.values(Object.values(req.body.files)[0])[0].path.
What I've missed ?
Thanks.
You can use this to upload an image. Using async/await.
async uploadImage(image) {
const form = new FormData();
form.append('file', image);
form.append('upload_preset', 'g5ziunzg');
const res = await Axios.post('YOUR_CLOUDINARY_URL', form)
console.log(res)
return res.data;
}
This will return an object with the secure_url which you can store in your mongo database. I am assuming you have a backend-api for this task.
Inside your formSubmit function, you can first call this function and receive this secure_url.
Note that I am using axios here. This example can easily be translated to work with fetch.
You don't need to use Object.value since req.body.files is an array and you need to check its length before access. Try it:
const [file] = req.body.files
if (file) {
// your logic here
}

501 Not Implemented error in small web app

I am trying to upload images to an S3 bucket with react and expressjs. When I attempt to upload the image I get a 501 Not Implemented Error. I am using axios to contact the end point I created in the server code.
My react code:
class FileUpload extends Component {
state = {
file: null
};
submitFile = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('file', this.state.file[0]);
axios.post(`test-upload`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
// handle your response;
}).catch(error => {
// handle your error
});
}
handleFileUpload = (event) => {
this.setState({file: event.target.files});
}
render () {
return (
<form onSubmit={this.submitFile}>
<input label='upload file' type='file' onChange=
{this.handleFileUpload} />
<button type='submit'>Send</button>
</form>
);
}
}
export default FileUpload;
My server code:
const uploadFile = (buffer, name, type) => {
const params = {
ACL: 'public-read',
Body: buffer,
Bucket: process.env.S3_BUCKET,
ContentType: type.mime,
Key: `${name}.${type.ext}`
};
return s3.upload(params).promise();
};
app.use('/', (req,res) =>{
res.send(JSON.stringify({ greeting: `Hello!` }));
});
// Define POST route
app.post('/test-upload', (request, response) => {
const form = new multiparty.Form();
form.parse(request, async (error, fields, files) => {
if (error) throw new Error(error);
try {
const path = files.file[0].path;
const buffer = fs.readFileSync(path);
const type = fileType(buffer);
const timestamp = Date.now().toString();
const fileName = `bucketFolder/${timestamp}-lg`;
const data = await uploadFile(buffer, fileName, type);
return response.status(200).send(data);
} catch (error) {
return response.status(400).send(error);
}
});
});
This was done by following a guide on the internet but there seems to be something missing that I just can't figure out.
Figured it out in the end,
axios.post(/test-upload,
should have been
axios.post(http://localhost:3000/test-upload,

Resources