I'm trying to send POST requests from a NEXTjs frontend with a simple form field to a backend located on the same server which is a python script using the Falcon library. The python script itself is ran by Gunicorn and listens on the 8080 port.
Both codes run pretty well without errors but when I try to submit the form all I get is a 415 error which seems to indicate that what I'm trying to send to the API is not a supported media type but, as pointed out in this answer
Falcon has out of the box support for requests with Content-Type: application/json
Since the webpage and the server are hosted on the same VPS I've also tried to use the 127.0.0.1 address in the fetch call but that was unsuccessful as well (the backend API didn't even responded in fact)
Here's the backend code:
#!/usr/bin/env python
# coding=utf-8
import time
import falcon
import json
class Resource(object):
def on_post(self, req, resp, **kwargs):
request_body = req.media
print('POST Request: {}'.format(req))
print('Request body: {}'.format(request_body))
start = time.time()
resp.body = json.dumps({
'count_identical_pairs': count_identical_pairs(request_body),
'computation_time': int((time.time() - start) * 1000)
})
def count_identical_pairs(integers_array):
total = 0
count = dict()
# Type checking
if not isinstance(integers_array, list):
return -1
# Check if N is within the range [0..100,000]
if len(integers_array) > 100000:
return -2
for integer in integers_array:
# Check if each element of the array is within the range [−1,000,000,000..1,000,000,000]
if integer not in range(-1000000000, 1000000000):
return -3
if str(integer) not in count:
count[str(integer)] = 1
else:
count[str(integer)] += 1
for key, value in count.items():
total += value * (value - 1) / 2
return total
api = application = falcon.API()
api.add_route('/count_identical_pairs', Resource())
And here's the frontend one:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class Index extends React.Component {
constructor() {
super();
this.state = {
input_array: [],
};
this.onSubmit = this.onSubmit.bind(this);
this.myHeaders = new Headers();
}
onChange = evt => {
// This triggers everytime the input is changed
this.setState({
[evt.target.name]: evt.target.value,
});
};
onSubmit = evt => {
evt.preventDefault();
console.log('this.state.input_array = ' + this.state.input_array);
console.log('JSON.stringify(this.state.input_array) = ' + JSON.stringify(this.state.input_array));
// Making a post request with the fetch API
// Test payload [1, 7, 7, 5, 7, 5, 6, 1]
fetch('http://vps638342.ovh.net:8080/count_identical_pairs', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=utf-8'
},
mode: 'no-cors', // Security hazard?
body: JSON.stringify(this.state.input_array),
redirect: 'follow'
})
.then(response => response.text())
.then(data => console.log('Data: ' + data))
.catch(error => console.log('Error: ' + error))
};
render() {
return (
<form onSubmit={this.onSubmit} >
<input
name="input_array"
type="text"
id="name"
value={this.state.input_array}
onChange={this.onChange}>
</input>
<input type="submit" />
</form>
);
};
}
ReactDOM.render(<Index />, document.getElementById("root"));
EDIT 1: I've tested the python backend API with Postman and I can see that it works pretty well already as you can see pictured here:
EDIT 2: Thanks to @Maku here's the update code on the backend that allows all origins, methods and header. I'm new to server development but I'm guessing it's not a very secure way to code but at least it works (I'll add a third EDIT if I find a more recommended way to do this)
Enable CORS in your falcon server and remove the 'no-cors' flag in your javascript, that worked for me the other day.
https://github.com/lwcolton/falcon-cors should work for you. To test it you could just allow all origins with something like this (I'm using another python framework so I haven't tested this exact falcon extension)
cors = CORS(allow_all_origins=True, allow_all_headers=True)
api = falcon.API(middleware=[cors.middleware])
Edit: added allow_all_headers=True like discussed in the comments.