Ajax return PUT ... 405 (Method Not Allowed) - ajax

web.php:
Route::put('contact', 'FormsController#send');
script.js:
const sendForm = function () {
action = 'contact';
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
const getMessageSend = document.querySelector("#messageSend");
getMessageSend.classList.add("message-send");
getMessageSend.innerText = "Thank you for sending an email. You will receive an answer shortly.";
} else {
const getMessageSendError = document.querySelector("#messageSendError");
getMessageSendError.classList.add("message-send");
getMessageSendError.classList.add("message-send-error");
getMessageSendError.innerText = "An error occurred and the email was not sent.";
}
}
xmlhttp.open("PUT", action, true);
xmlhttp.send();
};
and my form:
{!! Form::open(['action'=>['FormsController#send'], 'method' => 'PUT', 'class' => 'form', 'novalidate' => 'novalidate', 'files' => true]) !!}
...
{!! Form::submit('Send', ['class' => 'submit-btn']); !!}
{!! Form::close() !!}
Laravel reurn error:
script.js:566 PUT http://subdomain.mypage.com/contact 405 (Method Not Allowed)
I have this same name FormsController#send, and name address contact. I added to header in index file:
but the error still occurs. I do not know what I'm doing wrong.
Edit:
more information with error:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------15304174751887
Content-Length: 147
Cookie: XSRF-TOKEN=eyJpdiI6Imk0RVE5NDB3eHBXaGQ0OUwyc0VXa3c9PSIsInZhbHVlIjoiV2xPYzl4VzN1bEFJdDFBZ0JiZ1Nhd2JYZTVzRW1WekdobVwvNjNpY24wVmtEVUJQbFV1K1Q4anBydXdOSEt5WWIiLCJtYWMiOiI5MzgwYmMzNDU0ZTAzY2NkNzNkNGEwNjk0NTYxNDk2ZjE5NjQzMTVjNGViOTQ5ZTNhZmI5N2I3NDQzNjM2OTY2In0%3D; hr_session=eyJpdiI6IlVLVU9WN0pEZlVJbUZOeFdIRWZ0cnc9PSIsInZhbHVlIjoiVzFEenJyc2x6d09TQjV0bWczc05LMlwvNHZDZ3hKSDdaZEpaeEFnVXc2d0NmZVpTeGY1NUJocWFzOStDN3h6OWIiLCJtYWMiOiIwYTJjYjZhMjcyMzk3MWVlOGUzMDYxOTM4ODFiNTc1Y2QzMWQ4ZWRhY2Y1ZTdiMjIyYTg4MTFhM2I2NGE0OTI2In0%3D
Connection: keep-alive

Try to replace xmlhttp.send(); for
var formdata = new FormData();
formdata.append("_method", "put");
xmlhttp.send(formdata);

Related

Internal server error 500 when sending ajax post request in laravel 9

I am using laravel 9. I am trying to make a ajax crud operation using alax. It respond well in get request. but when I sending the post request it shows:
http://127.0.0.1:8000/add/teacher 500 (Internal Server Error) in the console. I used meta in the head and ajax csrf header according to documentation. Still showing error. here my ajax code example bellow:
<script>
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
$(document).ready(function() {
$(".add_teacher").on('click', function(event) {
event.preventDefault();
var name = $("#name").val();
var email = $("#email").val();
var position = $("#position").val();
var phone = $("#phone").val();
var password = $("#password").val();
$.ajax({
method: "post",
url: "{{route('store')}}",
data: {
name: name,
email: email,
position: position,
phone: phone,
password: password
},
success: function(res) {
if (res.status == 'success') {
$("#exampleModal").modal('hide');
$("#modalform")[0].reset();
$(".table").load(location.href + ' .table');
}
},
error: function(err) {
let error = err.responseJSON;
$.each(error.errors, function(index, value) {
$(".errorMessage").append('<span class="text-
danger">' + value +'</span><br>');
});
}
});
});
});
</script>
Here is my route:
Route::post('/add/teacher', [TeacherController::class,'store'])->name('store');
and my controller code is:
public function store(Request $request)
{
$request->validate(
[
"name" => "required",
"email" => "requied|unique:teachers",
"position" => "requied",
"phone" => "requied|unique:tachers",
"password" => "requied",
],
[
"name.required" => "Name is required",
"email.required" => "Email is required",
"email.unique" => "Email already exists",
"position.required" => "Postion is required",
"phone.required" => "Phone is required",
"phone.unique" => "Phone already exixts",
"password.required" => "password is required",
]
);
$teacher = new Teacher();
$teacher->name = $request->name;
$teacher->email = $request->email;
$teacher->position = $request->position;
$teacher->phone = $request->phone;
$teacher->password = $request->password;
$teacher->save();
return response()->json([
'status' => 'success',
]);
}
Now I need an exact solution to go farther. Thank you.

I get the error: 401 on POST https://texttospeech.googleapis.com/v1/text:synthesize

I get the error: 401 on POST https://texttospeech.googleapis.com/v1/text:synthesize (Request had invalid authentication credentials. Expected OAuth 2 access)
In my Laravvel app I have following route defined:
Route::post('/text-to-speech', function (Request $request) {
$text = $request->input('text');
// Make a request to the Google Text-to-Speech API
$client = new Client();
Log::debug('test: ', ['text' => $text]);
$response = $client->post('https://texttospeech.googleapis.com/v1/text:synthesize', [
'headers' => [
'Authorization' => 'Bearer ' . 'my_apikey',
'Content-Type' => 'application/json; charset=utf-8',
],
'json' => [
'input' => [
'text' => $text
],
'voice' => [
'languageCode' => 'en-US',
'ssmlGender' => 'NEUTRAL'
],
'audioConfig' => [
'audioEncoding' => 'MP3'
]
]
]);
Log::debug('API response: ', [
'response' => json_decode($response->getBody(), true),
'input' => $request->all()
]);
$data = json_decode($response->getBody(), true);
return response()->json($data);
});
this is my js function:
// text to speech
document.getElementById("text-to-speech-button").addEventListener("click", function(){
var text = document.getElementById("text-input").value;
console.log("Text: " + text);
// Make an AJAX request to the backend
fetch('/text-to-speech', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify({
text: text
})
})
.then(function(response) {
return response.json();
})
.then(function(data) {
// Handle the response from the backend
// ...
});
});
and this is the button in my blade file:
<input type="text" id="text-input">
#csrf
Text to Speech
when i click it i get following log:
[2023-02-12 19:20:34] local.DEBUG: test: {"text":"speak this text"}
[2023-02-12 19:20:35] local.ERROR: Client error: `POST https://texttospeech.googleapis.com/v1/text:synthesize` resulted in a `401 Unauthorized` response:
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access (truncated...)
{"userId":11,"exception":"[object] (GuzzleHttp\\Exception\\ClientException(code: 401): Client error: `POST https://texttospeech.googleapis.com/v1/text:synthesize` resulted in a `401 Unauthorized` response:
{
\"error\": {
\"code\": 401,
\"message\": \"Request had invalid authentication credentials. Expected OAuth 2 access (truncated...)
at /var/www/html/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:113)

When using JWT with Laravel and Axios, JWT works but other routes always returns 401 Unauthenticated

I have successfully implemented JWT in Laravel and everything is working fine. Login, Logout.
But every time I want to add a new route in my routes/api.php and try to use it with an controller, I get a 401 Unauthenticated. It seems as if the controller is not addressed because no var_dump comes back.
My config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
My Request:
let myConfig = {
headers: {
Authorization: "Bearer " + 'JWT_SECRET'
}
}
axios.get('http://localhost/api/getcashboxes', myConfig)
.then(res => {
console.log(res.data)
}).catch(err => {
console.log(err)
})
My routes/api.php
use Illuminate\Http\Request;
Route::get('getcashboxes', 'CashboxController#getcashboxes')->name('getcashboxes');
Route::group(['prefix' => 'auth'], function ($router) {
Route::post('login', 'AuthController#login');
Route::post('logout', 'AuthController#logout');
Route::post('refresh', 'AuthController#refresh');
Route::post('me', 'AuthController#me');
});
My CashboxController.php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class CashboxController extends Controller
{
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
//$this->middleware('auth:api'); //Does not work too
}
public function getcashboxes()
{
var_dump("Hello World :)");
}
}
Request-Header:
Accept
application/json, text/plain,
Accept-Encoding
gzip, deflate
Accept-Language
de,en-US;q=0.7,en;q=0.3
Authorization
Bearer 5zqyH1BrQUbiDRefurc5XaduzaTyCXPlcPXbodDHhsgdjJHU1dDHhsgdjefur
Cache-Control
max-age=0
Connection
keep-alive
Cookie
XSRF-TOKEN=eyJpdiI6IlVjRFwvYkRvaSs3cDBCVFkxMFZtZkhRPT0iLCJ2YWx1ZSI6Imw4WlZ3RlwvVjNQemZOaTRQXC84MDlrd01uc0dFeUZ4eCtuR29jcWFqTGZBY3RkdzdFdFpzWjB4UDZOQzhQYXRTUCIsIm1hYyI6ImE5ODFkODM1ODU3M2Q3ZDEyZDY1NjdiYTY3ZGJkN2FlNGIzYWRiZTcxNjI4ODc3MmZkYzg3MGZjMzhmODlhODkifQ%3D%3D; buywatchvue_session=eyJpdiI6IjFSb25ZdmthalF2Tm1TWjM4cnUzRkE9PSIsInZhbHVlIjoiQms0SDNGNlFPcXM1YVR2MTZzeEJpRkc1MnlPeFlGOW5IRlRVR0pTZWI3MUk2XC9RMUdsYVp5SWQyTnJRcitoazIiLCJtYWMiOiI0ZmYyNjQxOWY0NjAxNzM4NzY2YzBmYjY2Mjc2OTcyNTE0ZjVkYzdlMDU2YzE0MWE4Y2U2MzZmNTFkNzU2MWY4In0%3D; laravel_session=eyJpdiI6Ik4wNGRJM3Q5QU5KRUlIbWZoaUE4VkE9PSIsInZhbHVlIjoiNmNLb09JeU5DRksxM1VNMElPNDNQb1NseVNJYWdTdXpZdnZyS1RuMlVRY043OUJcLzREMXhkaWR3TmR6ZklWaWIiLCJtYWMiOiJmYWViNzIxMmZjYTU1N2UwYmI2OTU3YzAyNmFmNzM3NjAyMzY5N2Q2MTAyMzEwNzc1MDZlZWQzMjE4YWZiN2UyIn0%3D
Host
localhost
Referer
http://localhost
User-Agent
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
X-CSRF-TOKEN
DClSR93LzbeM4SZQ9OzZJ89s5jtiNFOfkWabYMSJ
X-Requested-With
XMLHttpRequest
X-XSRF-TOKEN
eyJpdiI6IlVjRFwvYkRvaSs3cDBCVFkxMFZtZkhRPT0iLCJ2YWx1ZSI6Imw4WlZ3RlwvVjNQemZOaTRQXC84MDlrd01uc0dFeUZ4eCtuR29jcWFqTGZBY3RkdzdFdFpzWjB4UDZOQzhQYXRTUCIsIm1hYyI6ImE5ODFkODM1ODU3M2Q3ZDEyZDY1NjdiYTY3ZGJkN2FlNGIzYWRiZTcxNjI4ODc3MmZkYzg3MGZjMzhmODlhODkifQ==
Answer-Header:
Cache-Control
no-cache, private
Connection
Keep-Alive
Content-Length
30
Content-Type
application/json
Date
Sat, 22 Aug 2020 14:12:25 GMT
Keep-Alive
timeout=5, max=99
Server
Apache/2.4.37 (Win32) OpenSSL/1.1.1a PHP/7.3.0
Vary
Authorization
X-Powered-By
PHP/7.3.0
Anyone an idea?
You can create an instance of axios and use it like I did.
const instance = axios.create({
baseURL: 'http://localhost/api'
});
instance.defaults.headers.common['Authorization'] = `Bearer ${token}`;
instance.get('/getcashboxes')
.then(res => {
console.log(res.data)
}).catch(err => {
console.log(err)
})
Please let me know if it helps you fix the issue.
I found the solution. It was a lack of knowledge about how JSON Web Tokens (JWT) works.
The principle explained simply:
If the user is logged in from the server, he gets a JWT back. This JWT must now be saved on the client side either as a cookie or in local storage. This is needed for later server inquiries.
If a request, that requires authorization, is sent to the server, the JWT must be sent in the header.
So it has to be read from the cookie or the local storage. With Laravel, Vue, Vuex, Axios and the local storage it can look like this:
Get token from server (AuthController.php):
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
Login mutation in vuex (store.js):
mutations: {
loginSuccess(state, payload) {
state.auth_error = null;
state.isLoggedIn = true;
state.loading = false;
state.currentUser = Object.assign({}, payload.user, {token: payload.access_token});
localStorage.setItem("user", JSON.stringify(state.currentUser));
},
},
Get the local user, included the JWT token:
function getLocalUser() {
const userStr = localStorage.getItem("user");
if(!userStr) {
return null;
}
return JSON.parse(userStr);
}
Create an axios-request using the JWT-Token from the local store:
const instance = axios.create({
baseURL: 'http://localhost/api'
});
instance.defaults.headers.common['Authorization'] = `Bearer ${getLocalUser().token}`;
instance.get('/test')
.then(res => {
console.log(res.data)
}).catch(err => {
console.log(err)
})
I hope I can help someone with this. Otherwise there is more information about JWT here. Thanks to Jonathan Akwetey Okine for the inspiration.

Codeigniter 3 multiple forms with ajax and csrf tokens working on one form only

I have admin page with multiple settings, each setting have different form in different tab.
I am using ajax and to save data, and i didn't had any problems so far with csrf token when i had only one form on a page, or when i disable csrf token.
On each ajax request new token is generated in controller and sent back to ajax which is updating hidden field with name="csrf_token" but with different id's.
After first form is submitted all is good, but when i try to submit other form csrf token doesn't work anymore i am getting message "The action you have requested is not allowed." with page 403 in console output even after i reload page and try to submit other form that didn't worked.
Is there way to have multiple forms with csrf protection on same page and how to handle that ?
Here is code examples
Forms with ajax
<form id="upload-icon" method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" id="csrf_token_1" value="<?php echo $this->security->get_csrf_hash(); ?>">
<input type="file" id="favicon_image" name="favicon_image" accept=".ico">
<button type="button" id="upload-icon-btn">Upload</button>
</form>
<form id="update-settings" method="POST">
<input type="hidden" name="csrf_token" id="csrf_token_2" value="<?php echo $this->security->get_csrf_hash(); ?>">
<input type="text" name="settings_one">
<input type="text" name="settings_two">
<input type="text" name="settings_three">
<button type="button" id="update-settings-btn">Update settings</button>
</form>
<script>
$(document).ready(function() {
var csrf_token = '';
// upload favicon form
$('#upload-favicon-form-btn').on('click', function(e) {
e.preventDefault();
var fd = new FormData();
var files = $('#favicon_image')[0].files[0];
fd.append('favicon_image', files);
var favicon = $('#favicon_image').val();
if (favicon == '') {
loadModal('Warning', 'Please select <strong>favicon.ico</strong> icon file.');
} else {
$.ajax({
type: 'POST',
url: '<?php echo base_url('admin/settings/upload_ico'); ?>',
data: fd,
contentType: false,
cache: false,
processData: false,
dataType: 'json',
success: function(response) {
csrf_token = response.csrf_token;
$('#csrf_token_1').val(csrf_token);
// messages output
},
error: function() {
// error message output
}
});
}
});
// update settings form
$('#update-settings-btn').on('click', function(e) {
e.preventDefault();
$.ajax({
type: 'POST',
url: '<?php echo base_url('admin/settings/update_settings'); ?>',
data: $('#update-settings').serialize(),
dataType: 'json',
success: function(response) {
csrf_token = response.csrf_token;
$('#csrf_token_2').val(csrf_token);
// messages output
},
error: function() {
// error message output
}
});
});
});
</script>
Settings controller
public function update_settings()
{
$csrf_token = $this->security->get_csrf_hash();
$this->form_validation->set_rules('settings_one', 'Setting one', 'trim|required|xss_clean');
$this->form_validation->set_rules('settings_two', 'Setting two', 'trim|required|xss_clean');
$this->form_validation->set_rules('settings_three', 'Setting three', 'trim|required|xss_clean');
if ($this->form_validation->run()) {
if ($this->Settings_model->UpdateSettings($this->input->post('settings_one'), $this->input->post('settings_two'), $this->input->post('settings_three'))) {
$data = array(
'success' => true,
'message' => 'Settings updated.',
'csrf_token' => $csrf_token
);
} else {
$data = array(
'error' => true,
'message' => 'Settings was not updated.',
'csrf_token' => $csrf_token
);
}
} else {
$data = array(
'error' => true,
'settings_one_error' => form_error('settings_one'),
'settings_two_error' => form_error('settings_two'),
'settings_three_error' => form_error('settings_three'),
'csrf_token' => $csrf_token
);
}
echo json_encode($data);
}
public function upload_ico()
{
$csrf_token = $this->security->get_csrf_hash();
$favicon_upload_path = './upload/';
if (isset($_FILES['favicon_image']['name'])) {
$config['upload_path'] = $favicon_upload_path;
$config['allowed_types'] = 'ico';
$this->load->library('upload', $config);
if (!$this->upload->do_upload('favicon_image')) {
$data = array(
'error' => true,
'message' => $this->upload->display_errors(),
'csrf_token' => $csrf_token
);
} else {
$data = array(
'success' => true,
'message' => 'Favicon uploaded.',
'csrf_token' => $csrf_token
);
}
} else {
$data = array(
'error' => true,
'message' => 'No file selected.',
'csrf_token' => $csrf_token
);
}
echo json_encode($data);
}
Config.php
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_token';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
$config['csrf_regenerate'] = TRUE;
$config['csrf_exclude_uris'] = array(
'admin/settings'
);
I found issue.
I forgot to send token on first form, those 2 lines fix it
var token = $('#csrf_token_1').val(); // read token value from input
fd.append('csrf_token', token);
Then in script
<script>
$(document).ready(function() {
var csrf_token = '';
// upload favicon form
$('#upload-favicon-form-btn').on('click', function(e) {
e.preventDefault();
var fd = new FormData();
var files = $('#favicon_image')[0].files[0];
fd.append('favicon_image', files);
// fix for token that should be sent to controller over ajax
var token = $('#csrf_token_1').val(); // read token value from input
fd.append('csrf_token', token); // append token value to data that need to be send to controller
and on each success in ajax it should update all forms id's with token returned from controller because i use $config['csrf_regenerate'] = TRUE; that is make new token on each request, so i made js function that updates token for all id's on all forms
// function that updates token on all forms
function updateToken(token) {
$('#csrf_token_1, #csrf_token_2').val(token);
}
function in usage
success: function(response) {
csrf_token = response.csrf_token;
updateToken(csrf_token);
}
Maybe it's not best solution but works for me. If anyone have better one please feel free to post it.

Fetch in react native doesn't send the post with codeIgniter API

Fetch post is not working with the json api coded with codeIgniter. The get method works but there's issue in post method. The key is not recognized between the react native and code igniter. Any help is appreciated. Thankyou
fetch('http://zzz.com/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: 'abc',
}),
})
.then(response => response.json())
.then(responseJson => {
console.log('responseJson', responseJson);
})
.catch((error) => {
console.error('fetchError', error);
});
CodeIgniter controller
public function login()
{
$un = $this->input->post('username'); //why doesn't the key 'username' is working here
echo json_encode($un);
}
CONSOLE LOG:
responseJson false
Updates:
1)Using json_decode, gives error
"fetchError SyntaxError: Unexpected end of JSON input"
public function login()
{
$Data = json_decode(file_get_contents('php://input'), false);
echo $Data;
}
2)Using json_encode gives following outcomes:
responseJson {"username":"abc"}
public function login()
{
$Data = json_encode(file_get_contents('php://input'), false);
echo $Data;
}
Update 1:
1) Using only file_get_contents gives the output as: responseJson {username: "abc"}
public function login()
{
$Data = (file_get_contents('php://input'));
echo $Data;
}
2)using var_dump and json_decode in server code, it gives following error in the app console
public function login()
{
$Data = json_decode(file_get_contents('php://input'), true);
var_dump ($Data);
}
Error:
fetchError SyntaxError: Unexpected token a in JSON at position 0
at parse (<anonymous>)
at tryCallOne (E:\zzz\node_modules\promise\setimmediate\core.js:37)
at E:\zzz\node_modules\promise\setimmediate\core.js:123
at E:\zzz\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:295
at _callTimer (E:\zzz\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:152)
at _callImmediatesPass (E:\zzz\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:200)
at Object.callImmediates (E:\zzz\node_modules\react-native\Libraries\Core\Timers\JSTimers.js:464)
at MessageQueue.__callImmediates (E:\zzz\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:320)
at E:\zzz\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:135
at MessageQueue.__guard (E:\zzz\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:297)
console log the response as following gives the array in app console:
fetch('http://zzz.com/login', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: 'abc',
})
})
.then((response) => console.log('response', response))
Console:
response
Response {type: "default", status: 200, ok: true, statusText: undefined, headers: Headers, …}
headers:Headers {map: {…}}
ok:true
status:200
statusText:undefined
type:"default"
url:"http://zzz.com/login"
_bodyInit:"array(1) {↵ ["username"]=>↵ string(3) "abc"↵}↵"
_bodyText:"array(1) {↵ ["username"]=>↵ string(3) "abc"↵}↵"
__proto__:Object
Try to add same-origin mode like below :
fetch('http://zzz.com/login', {
method: 'POST',
credentials: 'same-origin',
mode: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
username: 'abc',
}),
})
.then(response => response.json())
.then(responseJson => {
console.log('responseJson', responseJson);
})
.catch((error) => {
console.error('fetchError', error);
});
From the front end, send the data as a form data (as shown below).
const formData = new FormData();
formData.append("name", "RandomData");
formData.append("operation", "randomOperation");
In your Codeigniter controller, receive it as...
$name = $this->input->post("name");
$operation = $this->input->post("operation");

Resources