How to send data using a POST fetch request from React/JS to a Django server?
I'm able to make a GET request, so the servers working in that regard.
The error I get from the terminal is:
[0] Forbidden (Origin checking failed - http://localhost:3000 does not match any trusted origins.): /hello/
[0] [04/Feb/2023 08:31:02] "POST /hello/ HTTP/1.1" 403 2569
The error I get from browser console is:
Form.jsx:47
POST http://localhost:8000/hello/ 403 (Forbidden)
First of all I installed npm install js-cookie and imported Cookies
Fetch request
import Cookies from 'js-cookie'
...
const response = await fetch('http://localhost:8000/hello/', {
method: 'POST',
headers: {
"X-CSRFToken": Cookies.get('csrftoken'),
"Accept": "application/json",
'Content-Type': 'application/json'
},
body: JSON.stringify({
some_data: 'some_value'
})
});
const data = await response.text();
console.log(data);
urls.py
urlpatterns = [
path('hello/', views.hello_world, name='hello_world'),
]
views.py
def hello_world(request):
if request.method == 'POST':
# do something with the data posted
data = request.POST
# return a JSON response
return HttpResponse("Success: ")
return HttpResponse("Error: ")
settings.py
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
]
# Tested using a combination of:
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = [
"http://localhost:3000",
]
CORS_ALLOW_METHODS = ['POST']
CORS_ALLOW_METHODS = [
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
]
CORS_ALLOW_METHODS = (
'GET',
'POST',
'OPTIONS',
)
CORS_ALLOW_CREDENTIALS = True
I also get this error/.text() response from:
const data = await response.text();
console.log(data);
Error/.text() response
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="robots" content="NONE,NOARCHIVE">
<title>403 Forbidden</title>
<style type="text/css">
html * { padding:0; margin:0; }
body * { padding:10px 20px; }
body * * { padding:0; }
body { font:small sans-serif; background:#eee; color:#000; }
body>div { border-bottom:1px solid #ddd; }
h1 { font-weight:normal; margin-bottom:.4em; }
h1 span { font-size:60%; color:#666; font-weight:normal; }
#info { background:#f6f6f6; }
#info ul { margin: 0.5em 4em; }
#info p, #summary p { padding-top:10px; }
#summary { background: #ffc; }
#explanation { background:#eee; border-bottom: 0px none; }
</style>
</head>
<body>
<div id="summary">
<h1>Forbidden <span>(403)</span></h1>
<p>CSRF verification failed. Request aborted.</p>
</div>
<div id="info">
<h2>Help</h2>
<p>Reason given for failure:</p>
<pre>
Origin checking failed - http://localhost:3000 does not match any trusted origins.
</pre>
<p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
<a
href="https://docs.djangoproject.com/en/4.1/ref/csrf/">Django’s
CSRF mechanism</a> has not been used correctly. For POST forms, you need to
ensure:</p>
<ul>
<li>Your browser is accepting cookies.</li>
<li>The view function passes a <code>request</code> to the template’s <code>render</code>
method.</li>
<li>In the template, there is a <code>{% csrf_token
%}</code> template tag inside each POST form that
targets an internal URL.</li>
<li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
<code>csrf_protect</code> on any views that use the <code>csrf_token</code>
template tag, as well as those that accept the POST data.</li>
<li>The form has a valid CSRF token. After logging in in another browser
tab or hitting the back button after a login, you may need to reload the
page with the form, because the token is rotated after a login.</li>
</ul>
<p>You’re seeing the help section of this page because you have <code>DEBUG =
True</code> in your Django settings file. Change that to <code>False</code>,
and only the initial error message will be displayed. </p>
<p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
</div>
</body>
</html>
In settings.py you have to add http://localhost:3000 to the list of trusted origins for unsafe requests (e.g. POST).
1 :
add this to your settings.py :
CSRF_TRUSTED_ORIGINS = [
'http://localhost:3000'
],
2 :
place this 'corsheaders.middleware.CorsMiddleware', as high as possible, especially before any middleware in the MIDDLEWARE list in the settings.py file
3- Turn cookies on, on your browser (chrome, safari ...)
Related
I'm trying to setup a gatsbyJS website on AWS amplify with a contact form.
When the user answers the form, a POST request is sent to sendgrid API and I receive an e-mail with the content of the form.
This behavior works perfectly on localhost (gatsby develop). But when I try to send the form from the AWS amplify hosted website, the POST request receives a 405 response "Method not allowed":
firefox dev console
MethodNotAllowedThe specified method is not allowed against this resource.POSTOBJECT...97WD...<HostId...NvPyRAXpAs7f...
Here is my promise code that sends the mail:
import sendgrid from "#sendgrid/mail";
sendgrid.setApiKey(process.env.GATSBY_SENDGRID_API_KEY);
//Promise pour envoi du mail
async function sendmail(req, res) {
try {
await sendgrid.send({
to: process.env.GATSBY_SENDGRID_AUTHORIZED_EMAIL,
from: `${req.body.email}`,
subject: `${req.body.subject}`,
html: `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en">
<head>
<meta charset="utf-8">
<title>RSVP</title>
<meta name="description" content="The HTML5 Herald">
<meta name="author" content="SitePoint">
<meta http-equiv="Content-Type" content="text/html charset=UTF-8" />
<link rel="stylesheet" href="css/styles.css?v=1.0">
</head>
<body>
<div class="container" style="margin-left: 20px;margin-right: 20px;">
<h3>Nouvelles inscription de : ✉️${req.body.email} </h3>
<div style="font-size: 16px;">
<p> Prénoms: ${req.body.firstname}</p>
<p> Noms: ${req.body.lastname}</p>
<p> Présences: ${req.body.presences}</p>
<p> Message: ${req.body.message}</p>
<h3>JSON raw:</h3>
<p>${req.body.messageRaw}</p>
<br>
</div>
</div>
</div>
</body>
</html>`,
}
)
}catch(error){
console.log(error);
return res.status(error.statusCode || 500).json({ error: error.message });
}
return res.status(200).json({ error: "" });
} export default sendmail;
And here is the onSubmit function that calls the promise thanks to fetch API:
...
const { user } = useAuth0();
...
const onSubmit = async () => {
const toastId = toast.loading("Please wait...")
let data = getValues();
let firstnameString = JSON.stringify(getValues("firstname"));
let lastnameString = JSON.stringify(getValues("lastname"));
let presenceString = JSON.stringify(getValues("presence"));
let commentairesString = JSON.stringify(getValues("message"));
let dataString = JSON.stringify(data);
fetch("/api/sendmail", {
body: JSON.stringify({
email: user.email,
subject: "RSVP mariage",
firstname: firstnameString,
lastname: lastnameString,
presences: presenceString,
message: commentairesString,
messageRaw: dataString,
}),
headers: {
"Content-Type": "application/json",
//"Access-Control-Allow-Methods": "OPTIONS, POST"
},
method: "POST",
}).then(res => {
reset();
toast.success('Message Envoyé !', {
id: toastId,
duration: 4000,
position: 'sticky',
});
}).catch(err => {
toast.error(`Oups, il y a eu une erreur: ${err.toString()}`,{
id: toastId,
duration: 4000,
position: 'sticky',
});
})
};
return (...
I double checked the sendgrid API key is correct when building on
amplify.
The code works on localhost (email is sent), no errors.
Based on this article: 405 Method not allowed - description and on this stackoverflow question.
I tried to add a redirect with the sendgrid API address:
https://api.sendgrid.com/V3/mail/send . -> Same 405 response is returned.
I tried to allow the POST method in the code with those headers: "Access-Control-Allow-Methods": "OPTIONS, POST" -> Same 405 response is returned.
Is there any limitations with AWS amplify that could prevent a POST request to be sent to another domain?
It doesn't seem to be a CORS issue if I am right?
Note: As a workaround, I'm considering to make a lambda function to call the sendgrid API if there are any limitations from amplify. Does it seem a better practice ?
I'm trying to embed an Apache 1.5 dashboard in a Wordpress site. So far I've succeeded in doing the following:
Upgrade to Apache Superset 1.5 and enable the embedded dashboard functionality
Configure dashboard to be embedded
Implemented backend code to obtain JWT token
What I'm struggling with now is to implement the "#superset-ui/embedded-sdk" functionality in my Wordpress site (using TwentyTwentyOne standard template). I'm trying to follow the instructions listed here: https://fossies.org/dox/apache-superset-1.5.0-source/md_superset_embedded_sdk_README.html
The CDN approach looks the easiest, but I'm getting the error ReferenceError: Buffer is not defined. And if I add the following code:
<script src="https://unpkg.com/buffer"></script>
Then I get the error ReferenceError: require is not defined. I'm not sure how I can resolve this error. Is the CDN approach a dead end, or is there a way I can make this work?
With the "Using npm" approach I'm struggling even more. I'm able to install the npm packages on bitnami linux, but then I don't know how to make use of them in the site. I wasn't able to make it work to add '#superset-ui/embedded-sdk' using wp_enqueue_script(). Also I tried the following code in wordpress:
<script type="module">
import {embedDashboard} from 'http://[MY_IP_ADDRESS]/wp-content/themes/twentytwentyone/node_modules/#superset-ui/embedded-sdk/lib/index.js';
</script>
However then I get the following error:
Uncaught SyntaxError: The requested module 'http://[MY_IP_ADDRESS]/wp-content/themes/twentytwentyone/node_modules/#superset-ui/embedded-sdk/lib/index.js' does not provide an export named 'embedDashboard'
I don't quite understand this error as embedDashboard does appear to be defined in the js file. I also tried using the embedded-sdk/bundle/index.js file with the same result.
I've spent many hours on this and I'm not sure what approach I should take. Can anyone point me in the right direction? Or even better: have a solution to a similar problem with sample code I can look at? If I need to provide more details on what I've tried or what errors I'm seeing let me know.
This is how I did it.
I wrote my backend in Flask.
My Superset Version is 2.0
backend.py
from flask import Flask, render_template, jsonify
import requests
import json
import os
app = Flask(__name__)
#app.route('/')
def hello():
return render_template('index.html')
#app.route("/guest-token", methods=["GET"])
def guest_token():
url = "http://<IP>:<PORT>/api/v1/security/login"
payload = json.dumps({ "password": "<>", "provider": "db", "refresh": "true", "username": "<>" })
headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' }
responsel = requests.request("POST", url, headers=headers, data=payload)
print(responsel.text)
superset_access_token = json.loads(responsel.text)['access_token']
payload = json.dumps ({
"user": {
"username": "kashew",
"first name":"Kashish",
"lastname":"Bakshi"
},
"resources": [{
"type": "dashboard",
"id": "8f96cc84-7e9e-4f5c-ba92-3a1f0825fe3d"
}],
"rls": []
})
bearer_token = "Bearer " + superset_access_token
print(bearer_token)
response2 = requests.post(
"http://<IP>:<PORT>/api/v1/security/guest_token",
data = payload,
headers = { "Authorization": bearer_token, 'Accept': 'application/json', 'Content-Type': 'application/json' })
print(response2.json())
return jsonify(response2.json()['token'])
if __name__ == "__main__":
app.run(debug=True)
Explanation:
The Major part of understanding is the Guest_Token Function.
Here two POST Requests are at play.
The first Request gets the access token from Superset.
The Second request uses the Access Token we got as Bearer and is a POST Call to get the Guest_Token from Superset.
Payload for Guest_Token:
{
"user": {
"username": "kashew",
"first name":"Kashish",
"lastname":"Bakshi"
},
"resources": [{
"type": "dashboard",
"id": "8f96cc84-7e9e-4f5c-ba92-3a1f0825fe3d"
}],
"rls": []
}
Explanation:
The Payload is a JSON with 3 Key-Value Pair.
user (Here, I created a Dummy User{ This can be replaced with your App Auth Logic})
resources (Refer this)
rls (I kept it empty but more can be found here)
I ran my backend on Localhost:5000.
For Frontend, I created a directory "templates" and put my index.html file inside it.
index.html
<html>
<head>
<script src="https://unpkg.com/#superset-ui/embedded-sdk"></script>
<style>
iframe {
width: 100%;
height: 50%;
border: none;
pretext {
margin-right: 10%;
margin-left: 10%;
font-family: Tahoma;
</style>
</head>
<body>
<div class="pretext">
<h2> Covid Statistics </h2>
<p> Dive into Covid data
<p id="dashboard-container"></p>
<script>
async function fetchGuestTokenFromBackend() {
let response = await fetch('http://127.0.0.1:5000/guest-token');
let data = await response.json()
return data
}
const myDashboard = supersetEmbeddedSdk.embedDashboard({
id: "8f96cc84-7e9e-4f5c-ba92-3a1f0825fe3d",
supersetDomain: "http://<IP>:<PORT>",
mountPoint: document.getElementById("dashboard-container"),
fetchGuestToken: () => fetchGuestTokenFromBackend(),
dashboardUiConfig: {
hideTitle: true,
hideChartControls: true
},
});
</script>
</div>
</body>
</html>
Explanation:
It calls my guest-token method which returns a Guest-Token to my frontend. The Frontend then makes a request to Superset Embed Dashboard URL with the guest-token which in turn embeds and renders the chart in an Iframe on my website.
Hello, I hope this example of using 'superset embedded-sdk' will help you:
<!DOCTYPE html>
<head>
...
<script src="https://unpkg.com/#superset-ui/embedded-sdk"></script>
</head>
<body>
<p id="dashboard" width="700" height="700"></p>
<script>
async function getToken() {
const res = await fetch('http://localhost:3000/token');
const data = await res.json()
console.log(data['value']);
return data['value'];
}
supersetEmbeddedSdk.embedDashboard({
id: "7b5ee110-435b-4ad1-a257-97ea340bf82d",
supersetDomain: "http://localhost:8088",
mountPoint: document.getElementById("dashboard"),
fetchGuestToken: () => getToken(),
dashboardUiConfig: {
hideTitle: true,
filters: {
expanded: true
}
}
});
</script>
</body>
</html>
OS: e.g. Mac OSX 10.12.6
Consumer Pact library: e.g. Pact JS v5.5.0
Node Version: 8.2.1
there is a post restful api with post body is plain text, when i use below code to generate a consumer pact file, it show JSON ParserError, seems the pact-js only support the json body, even i set the content-type as text/plain in the header
provider.setup()
.then(() => {
provider.addInteraction({
state: 'I want to add a tag',
uponReceiving: 'Step - 1 : add a tag',
withRequest: {
method: 'POST',
path: "/api/v1/tags",
headers: {
"Content-Type": "text/plain;charset=UTF-8"
},
query: {
"org": "testOrg"
}
body: "Tag_Pact_test_0001"
},
willRespondWith: {
status: 200,
body: {
"result": 0
}
}
})
})
Is any way to send the text/plain body in post method?
I have an nginx server set up where I would like to set up a service that receives a string and returns a result. I plan to use Python to do the processing, with CherryPy as an interface. I've tested the CherryPy part, and know it receives properly. When I try to connect to the CherryPy service with a web page, I get CORS errors. How can I get them to communicate?
Here's the Python Code:
import cherrypy
import random
import urllib
class DataView(object):
exposed = True
#cherrypy.tools.accept(media='application/json')
def GET(self):
rawData = cherrypy.request.body.read(int(cherrypy.request.headers['Content-Length']))
b = json.loads(rawData)
return json.dumps({'x': 4, 'c': b})
def CORS():
cherrypy.response.headers["Access-Control-Allow-Origin"] = "*"
if __name__ == '__main__':
conf = {
'/': {
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
'tools.CORS.on': True,
}
}
cherrypy.tools.CORS = cherrypy.Tool('before_handler', CORS)
cherrypy.config.update({'server.socket_port': 3000})
cherrypy.quickstart(DataView(), '', conf)
Here's my web page:
<html lang="en">
<head>
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<link href="http://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css" rel="stylesheet">
<script type="text/javascript">
$(document).on('click', "#submitButton", function(){
$.ajax({
type: 'GET',
url: 'http://localhost:3000',
contentType: 'text/plain',
xhrFields: {
// The 'xhrFields' property sets additional fields on the XMLHttpRequest.
// This can be used to set the 'withCredentials' property.
// Set the value to 'true' if you'd like to pass cookies to the server.
// If this is enabled, your server must respond with the header
// 'Access-Control-Allow-Credentials: true'.
withCredentials: false
},
headers: {
},
success: function() {
console.log("Success");
},
error: function() {
console.log("Fail");
}
});
});
</script>
</head>
<body>
<div id="header">
<h2>PDE Grammar Engine</h2>
<form>
Input Sentence:<br>
<input type="text" name="query" id="query"><br>
<input type="submit" id="submitButton" value="Submit">
</form>
</div>
</div id="results">
</div>
</body>
</html>
Turned out that the CherryPy server was not actually listening to the correct address. It was allowing connections from localhost, but not external connections. I had to add the following entry to the cherrypy.config.update
cherrypy.config.update({'server.socket_host': '0.0.0.0',
'server.socket_port': 3000})
I'm trying to give users on my website "points" or "credits" for tweeting about out the brand name.
I have the fancy twitter widget on the appropriate view...
<p>Tweet
<div id="credited"></div>
<script>window.twttr = (function (d, s, id) {
var t, js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src= "https://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
return window.twttr || (t = { _e: [], ready: function (f) { t._e.push(f) } });
}(document, "script", "twitter-wjs"));
</script>
I have the JS all written up and pretty....
function creditTweet() {
$.post(
"/credit_tweet",
{},
function(result) {
var text;
if (result.status === "noop") {
text = "Thanks for sharing already!";
} else if (result.status === "ok") {
text = "5 Kredit Added";
}
$("#credited").html(text);
}
);
}
$(function() {
twttr.ready(function (twttr) {
window.twttr.events.bind('tweet', creditTweet);
});
});
Now the problem is either in the controller OR in the routes (where I'm posting). I think the routes are fine because the POST is almost working, because this is the description of the error on wikipedia - "422 Unprocessable Entity (WebDAV; RFC 4918)
The request was well-formed but was unable to be followed due to semantic errors."
So, do you guys see anything wrong with my ruby code in the controller?
class SocialKreditController < ApplicationController
TWEET_CREDIT_AMOUNT = 5
def credit_tweet
if !signed_in?
render json: { status: :error }
elsif current_user.tweet_credited
Rails.logger.info "Not crediting #{ current_user.id }"
render json: { status: :noop }
else
Rails.logger.info "Crediting #{ current_user.id }"
current_user.update_attributes tweet_credited: true
current_user.add_points TWEET_CREDIT_AMOUNT
render json: { status: :ok }
end
end
end
And in my routes.rb, it's pretty straight forward, so I doubt there's anything wrong here...
get 'social_kredit/credit_tweet'
post '/credit_tweet' => 'social_kredit#credit_tweet'
Where oh where is this error? I clearly don't know smack about HTTP requests.
I got it working!
I added a...
skip_before_action :verify_authenticity_token
to the controller.
The issue was found when checking out the logs and seeing that the CSRF token could not be verified.
ihaztehcodez(who was last active in 2016 so it won't help nudging him to post an answer) mentions that the skip_before_action :verify_authenticity_token technique is not so secure 'cos you lose forgery protection.
they mention that the best/secure/'better practise', solutions are mentioned here WARNING: Can't verify CSRF token authenticity rails
e.g.
$.ajaxSetup({
headers: {
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
}
});
or
$.ajax({ url: 'YOUR URL HERE',
type: 'POST',
beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
data: 'someData=' + someData,
success: function(response) {
$('#someDiv').html(response);
}
});
or
putting this within an ajax request
headers: {
'X-Transaction': 'POST Example',
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
Same problem I faced.
It sorts out after adding
skip_before_action :verify_authenticity_token
at the top of your controller where your JS is calling or sending data.
class UserController < ApplicationController
skip_before_action :verify_authenticity_token
def create
end
end
as shown in code snippet.
I had this challenge when working on a Rails 6 API-only application.
I followed the answer here - Rails: How to implement protect_from_forgery in Rails API mode to implement protect_from_forgery in Rails API mode, but I was still having the Can't verify CSRF token authenticity error followed by the 422 Unprocessable Entity error when I send post requests from Postman:
Started POST "/api/v1/programs" for ::1 at 2021-02-23 18:42:49 +0100
Processing by Api::V1::ProgramsController#create as JSON
Parameters: {"program"=>{"name"=>"Undergraduate", "code"=>"UD", "affiliate_status"=>false, "motto"=>"Our motto is ...", "description"=>"This is a new program", "school_id"=>1}}
Can't verify CSRF token authenticity.
TRANSACTION (0.3ms) BEGIN
↳ app/controllers/api/v1/programs_controller.rb:27:in `create'
Baserecord::School Load (0.3ms) SELECT "schools".* FROM "schools" WHERE "schools"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/controllers/api/v1/programs_controller.rb:27:in `create'
TRANSACTION (0.4ms) ROLLBACK
↳ app/controllers/api/v1/programs_controller.rb:27:in `create'
Completed 422 Unprocessable Entity in 30ms (Views: 0.8ms | ActiveRecord: 6.9ms | Allocations: 13172)
Here's I solved it:
The issue was caused by a validation error from my models. A validation I added to the model for the controller I was calling was failing. I had to check the Body of the response returned by Postman after making the request to find the error:
{
"affiliate_status": [
"can't be blank"
]
}
Once I fixed the error following this answer - Rails: Validation fails for ActiveRecord in setting a random Boolean Attribute, everything worked fine afterward.
That's all.
I hope this helps
a 422 can happen when updating a model in a POST request:
e.g. #user.update!(email: nil) in users#update will make a 422 if there is a validation on email like validates :email, presence: true
If you're including Rails meta data in the HTML header with <%= csrf_meta_tags %> it'll generate the following.
<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="ihwlaOLL232ipKmWYaqbSZacpJegQqooJ+Cj9fLF2e02NTQw7P/MfQyRuzruCax2xYWtEHWsb/uqiiZP6NWH+Q==" />
You can pull the CRSF token from the meta data and pass it into your async request.
Using the native js fetch method you can pass it in as a x-csrf-token header.
This is a trimmed onSave handler for a React component that enhances a standard Rails form.
onSaveHandler = (event) => {
const data = "Foo Bar";
const metaCsrf = document.querySelector("meta[name='csrf-token']");
const csrfToken = metaCsrf.getAttribute('content');
fetch(`/posts/${this.props.post_id}`, {
method: "PUT",
body: JSON.stringify({
content: data
}),
headers: {
'x-csrf-token': csrfToken,
'content-type': 'application/json',
'accept': 'application/json'
},
}).then(res => {
console.log("Request complete! response:", res);
});
}
Forgery protection is a good idea. This way we stay secure and don't mess with our Rails configuration.
Using gem 'rails', '~> 5.0.5' & "react": "^16.8.6",