fetch response 404 but page exists - javascript

I'm have this Fetch code (POST) but the response says status: 404 even though when I open the url in the browser, the page exists and returns a JSON. when I changed the url to https://httpbin.org/post it returns a normal data.. and when I use the same url but with GET method (without any init parameters for the fetch method) it returns status: 200.
what am I doing wrong?
when I open the url in the browser:
php controller
/*
* filepath: application/modules/test/controllers/test.php
*/
public function homepage()
{
$this->load->view('home', $this->data);
}
public function get_result()
{
$response = [
'status' => 0,
'message' => 'abcde',
];
echo json_encode($response);
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="button" id="btn1" value="Button1">
<script src="/assets/js/test_fetch.js"></script>
</body>
</html>
javascript
/*
* filepath: /assets/js/test_fetch.js
*/
/*!
* Filename: test_fetch.js
* Tanggal: 20220214
* Author: david santana
* script utk belajar ttg penggunaan fetch API
* Copyright Gotravelly.com#2022
*/
const myBtn1 = document.querySelector('#btnSubmit');
console.log(myBtn1);
myBtn1.addEventListener('click', function() {
// fetch data from server
const url = '/test/get_results';
// const url = 'https://httpbin.org/post';
let myData = {
user_id: 123,
name: 'david',
};
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify(myData)
})
.then(response => console.log(response));
});

never mind, my colleague pointed out to me that I didn't add the csrf and I need to use FormData in the body parameter.. after I tried it, it works!
but then I tried to use Object with the same csrf key-value pair like this, but it doesn't work
let myData = {
user_id: 123,
name: 'david',
};
let csrf_name = document.querySelector('#csrf').attributes['name'].value;
let csrf = document.querySelector('#csrf').value;
myData[csrf_name] = csrf;
which brings me to a new question (if I may ask a follow-up question): does POST needs to always use a FormData as body parameter?

Related

Trying to fetch and display image from api using javascript?

I am currently building a football website using LiveScore API to fetch data. Needless to say, I am actually learning API fetch using this project. This api fetch current day's fixture. I tried to get the two teams T1 and T2 with their name playing against each other in my HTML file. I got the output below.
Output and api
There are overall 81 arrays, and I want to display logo of the team which is in T1 and T2 array in Img section just before the team name. But only a placeholder is displaying. What am I doing wrong?
I wrote some HTML and JS to fetch the data.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Football Live Today</title>
</head>
<body>
<div>
<h2>Football Live Today</h2>
<h3>Today's Fixtures</h3>
<ul>
<li id="fixtures"></li>
</ul>
</div>
<script src="popup.js"></script>
</body>
</html>
JS
async function fetchData() {
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Key': '********',
'X-RapidAPI-Host': 'livescore6.p.rapidapi.com'
}
};
const res = await fetch('https://livescore6.p.rapidapi.com/matches/v2/list-by-date?Category=soccer&Date=20231801&Timezone=-7', options)
const record = await res.json()
console.log('record', record)
const indices = [0, 1, 4, 49, 50];
const filteredStages = record.Stages.filter((stage, index) => indices.includes(index));
document.getElementById("fixtures").innerHTML = filteredStages.map(item => `
<li>${item.Cnm} | ${item.Snm} | <img src="('${item.Events[0].T1[0].Img}">
${item.Events[0].T1[0].Nm} Vs <img src="${item.Events[0].T2[0].Img}">
${item.Events[0].T2[0].Nm}</li>`);
}
fetchData();

contact form written in gatsby/react, hosted on AWS amplify, sent through sendgrid API, returns a 405 POST "method not allowed"

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 ?

How do I filter Fetch API results with a search bar in vanilla JS?

I'm really new to javascript.
I'm trying to setup a page that fetches from a placeholder API(https://jsonplaceholder.typicode.com/posts) and displays the Title, Author(which I had to add) as well as the Body. I'm also trying to give the ability to use the search bar to filter the results by userId.
I got some help a few days ago getting the data from the API to show on my page but I've been struggling to get the search bar to work. Doe's anyone have any ideas on how to get it to work?
Any help is greatly appreciated.
Edit: I thought I found a solution but I can't seem to get it to work. I updated the code to reflect what I'm trying. Sorry if I'm missing simple stuff.
const searchInput = document.querySelector("[data-search]")
let html = []
searchInput.addEventListener("input", e => {
const value = e.target.value.toLowerCase()
html.forEach(post => {
const isVisible =
posts.userId.toLowerCase().includes(value)
posts.element.classList.toggle("hide", !isVisible)
})
})
Promise.all([1, 22, 33, 44, 55, 66, 77, 88, 95, 99].map(id =>
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method:'PATCH',
body: JSON.stringify({
name: "John Doe",
}),
headers: {
'Content-Type': 'application/json; charset=UTF-8'
},
})
.then((response) => response.json())))
.then(data => {
console.log(data);
const html = data.map(posts => {
return `
<div class="post">
<p id="title">${posts.title}</p>
<p id="author">${posts.name}</p>
<p id="body">${posts.body}</p>
</div>`
}).join(' ');
console.log(html);
document.querySelector('#content').insertAdjacentHTML('beforeend', html);
})
;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="resources/css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<title>Code Problem 4</title>
</head>
<body>
<header><h1>Blog Post</h1></header>
<div id="content">
<h2>Posts</h2>
<form id="search" action="action_page.php">
<input type="text" placeholder="Filter by User ID" name="search">
<button type="submit" onclick="searchWithField()"><i class="fa fa-search"></i></button>
</form>
</div>
<script src="resources/javascript/script.js"></script>
</body>
<footer>
<span class="shapes">● ▴ ■</span>
</footer>
</html>
Let's take a look at your code:
The below code performs a PATCH request, meaning it updates the data of a post, in your case by adding a name. Afterwards, it returns the post its updated data.
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method:'PATCH',
body: JSON.stringify({
name: "John Doe",
}),
headers: {
'Content-Type': 'application/json; charset=UTF-8'
},
})
If we take a closer look at the api it's returning data, we see that a userId is already defined. This means the user (which, in your case, is the author) can be retrieved from another api endpoint (https://jsonplaceholder.typicode.com/users/${post.userId}), so no need to use the FETCH request. Instead, we'll first GET the posts, then GET the users, and combine them in a nice list post_data for reference:
var post_data = [];
//Step one: get all the posts
fetch(`https://jsonplaceholder.typicode.com/posts`, {
method:'GET',
headers: {
'Content-Type': 'application/json; charset=UTF-8'
},
})
//Transform our data to correct format
.then((response) => response.json())
//Do something with the posts
.then(data => {
//We use the fetches array to add all our calls to get the user (and its name), and make it possible to wait for all of them to complete before we update our html.
let fetches = []
data.forEach(function(post) {
//Lets get the user coupled to each post
fetches.push(
fetch(`https://jsonplaceholder.typicode.com/users/${post.userId}`, {
method:'GET',
headers: {
'Content-Type': 'application/json; charset=UTF-8'
},
})
//We want the user name to be part of the post data, so we add it and save it to locally held data
.then((response) => response.json())
.then(user => {
//We add the user name directly as a property 'userName' to our posts.
post["userName"] = user.name;
//Add this post to our locally stored post_data
post_data.push(post);
})
);
});
//This promise will wait on all user names to be added to our posts
Promise.all(fetches).then(function() {
//At this moment, all our posts have been loaded. Now it's time to display them!
//I've added the post id, which will be a handy way to hide the irrelevant data.
const html = data.map(post => {
return `
<div class="post" id="post_${post.id}">
<p id="title">${post.title}</p>
<p id="author">${post.userName}</p>
<p id="body">${post.body}</p>
</div>`
}).join(' ');
document.querySelector('#content').insertAdjacentHTML('beforeend', html);
});
});
;
Aaah, that's better!
Now we want to be able to filter these results based on author. Since you ask for this to be done in javascript, I've adjusted your html from:
<form id="search" action="action_page.php">
<input type="text" placeholder="Filter by User ID" name="search">
<button type="submit" onclick="searchWithField()"><i class="fa fa-search"></i></button>
</form>
to
<input type="text" id="search-input" placeholder="Filter by Author" name="search">
<button type="submit" id="search-button"><i class="fa fa-search"></i></button>
The php link is removed (since i don't think you're using php). Note that I've added an id to both the <input> and <button> tag. We'll use those to identify our button.
We can finally try to filter our results:
//Now, we want to filter our blog posts by author.Since you have provided a button, i'll assume we only want to filter results as soon as the button is pressed:
document.getElementById("search-button").addEventListener("click",function(){
//First, we want to get the text in the input field:
const filterText = document.getElementById("search-input").value;
//Secondly, we want to find the posts that have a user matching that name, or that id.
//We convert all to lower case to ease searching.
let filtered_post_data = post_data.filter(post => {
username = post.userName.toLowerCase();
return (username.includes(filterText.toLowerCase()) || post.id == filterText);
});
//now we have the filtered data, it is time to hide all data:
post_data.forEach(function(post) {
let post_html = document.getElementById(`post_${post.id}`);
post_html.style.display = 'none';
});
//And afterwards, show all filtered data:
filtered_post_data.forEach(function(post) {
let post_html = document.getElementById(`post_${post.id}`);
post_html.style.display = 'inline';
});
});
working example on codepen

Pusher notification with Laravel 5.4 not working as expected

So I'm really stuck for a while now, with Laravel event and pusher API.
I created event and broadcast, but when I access with pusher Javascript API it returns empty array, when I try to print it in the route it also returns empty array, meanwhile if I check Laravel log stoarage/logs/laravel.log I see exactly what I am expecting in the Javascript and route.
My code:
.env
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=******
PUSHER_APP_KEY=524**************8
PUSHER_APP_SECRET=d2**************c
Boadcasting.php
'default' => env('BROADCAST_DRIVER', 'pusher'),
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => 'us2',
'encrypted' => true
],
],
The event class: ProjectEvent.php
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class ProjectEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $username;
public $message;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($username)
{
$this->username = $username;
$this->message = "{$username} sent you a message";
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return ['project-update'];
}
}
Route:
Route::get('/test', function () {
print_r(event(new App\Events\ProjectEvent('Michel')));
//return "Event has been sent!";
});
Route::get('/view', function () {
return view('testevent');
});
testevent.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="http://js.pusher.com/3.1/pusher.min.js"></script>
<script type="text/javascript">
Pusher.logToConsole = true;
var pusher = new Pusher('524************8', {
cluster: 'us2',
encrypted: true
});
var channel = pusher.subscribe('project-update');
channel.bind('App\\Events\\ProjectEvent', function(data) {
alert(data.message);
});
</script>
</body>
</html>
I really can't figure out where it all going wrong, since I am able to see the event in the log while the JS alert isn't working, also I try to dump the event on the route, it return empty array()

Returning HTML with Azure Serverless Function req.body

I've got some TIF files in Azure Blob Storage. I'd like to display them in the browser via a link embedded in a spreadsheet. The simplest way to do this should be to take the file code as a request parameter and return the properly formatted HTML, right?
So right now I've got it returning a req.body with some HTML. Unfortunately, the HTML just shows up as a string in the browser. How do I make it render as HTML with minimal rigamarole?
Here's my code:
if (req.query.blob) {
let blob = req.query.blob;
context.res = {
// status: 200, /* Defaults to 200 */
body: `<object width=200 height=200
data="<baseaddress>/${blob}.tif" type="image/tiff">
<param name="src" value="<baseaddress>/${blob}.tif">
<param name="negative" value="yes">
</object>`
};
}
You need to set the headers to specify the content type to HTML and the response must be a full valid HTML page (with the <html> tag and the rest).
Example:
module.exports.hello = (event, context, callback) => {
const html = `
<!doctype html>
<html>
<head>
<title>The Page Title</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>`;
const response = {
statusCode: 200,
headers: {
'Content-Type': 'text/html'
},
body: html
};
callback(null, response);
};

Categories

Resources