How can I spot a 302 response in Sencha Touch Ajax Request - ajax

I am making an Ajax.request to a backend I don't control.
This request sometimes redirects me to the login page, and my response.status is 200 instead of 302. So far I have tried this:
Ext.Ajax.on("requestexception", function(conn, response, options, eOpts){
console.log(conn);
console.log(response);
console.log(options);
console.log(eOpts);
});
Ext.Ajax.request({
url : 'someUrl'
params : params
});
Obviously this redirection is not what I expected so I need to spot when a 304 happened.
There most be some kind of work around.
Any ideas?
Regards.

As far as I know http redirects are handled entirely by the browser. So there is no way to detect a redirect if you don't have access to the backend.
When you are redirected to the login page it seems that your session is expired and you need to authenticate again.
You could create a function that sends the login information as soon as the login page is detected in the actual response.
sendLogin: function ( params, successCallback, failureCallback, scope ) {
Ext.Ajax.request({
url: "loginurl",
params: params,
success: function ( response, options ) {
successCallback.call( scope || this, response, options );
},
failure: function ( response, options ) {
failureCallback.call( scope || this, response, options );
}
});
}
doRequest: function ( params, successCalback, failureCallback, scope ) {
var me = this;
Ext.Ajax.request({
url: "someurl",
success: function ( response, options ) {
if ( isLoginPage( response ) ) {
this.sendLogin(
loginParams,
function ( successResponse, successOptions ) {
me.doRequest( params, successCallback, failureCallback, scope );
},
function ( failureResponse, failureOptions ) {
failureCallback.call( scope || this, failureResponse, failureOptions );
},
me
);
} else {
successCallback.call( scope || this, response, options );
}
},
failure: function ( response, options ) {
failureCallback.call ( scope || this, response, options );
}
});
}
Use the doRequset to send your actual request. The success case checks if the response is the login page. If so, it sends the login request. When the login request is successful the doRequest function will be call again with its parameters.

Related

Ajax request on site in wp-admin directory on wordpress server returns 400 Bad request

Well Thank you in advance for taking the time to read through my question.
The situation is as follows:
I have a Wrodpress server with a singular site in the wp-admin/sites/contactform/index.php
From that site I want to make an Ajax call however I get a '/wp-admin/admin-ajax.php:1  Failed to load resource: the server responded with a status of 400 (Bad Request) ' error upon the initial loading of the site and a
' https://THECORRECTURL/wp-admin/admin-ajax.php 400 (Bad Request)' error when I execute the method.
My suspicion is that the errors are provoked by missing permissions to access the admin-ajax.php file.
I'd like to know what you think.
Here is the Code:
In a js file in the directory of 'contactform/dt/wb.js':
jQuery(document).ready(function($) {
$('#btn-x').click(function() {
$.ajax({
url: '../../admin-ajax.php', // The URL of the WordPress AJAX handler
type: 'POST', // The HTTP method of the request
data: { // The data to be sent with the request
action: 'usystorequest',
},
success: function(response) { // A function to be called if the request is successful
if (response) {$('#mail_sent').show(); //mail got sent
}
else { $('#mail_not_sent').show(); //mail didnt get sent
}
}
});
});
});
In the functions.php File in 'contactform':
add_action('wp_enqueue_scripts', function(){
wp_enqueue_script('wb', get_stylesheet_directory_uri() . '/dt/wb.js',['jquery'], '1.0', false);
wp_enqueue_script('jquery', get_stylesheet_directory_uri() . '/dt/jquery-3.4.1.min.js',[], '1.0', false);
});
//Function executed by ajax call
add_action("wp_ajax_usystorequest", "usystorequest");
add_action("wp_ajax_nopriv_usystorequest", "usystorequest");
function usystorequest(){
echo ':test:';
wp_die();
}
(I am not using a Contactform plugin because i am sending Information that gets calculated in js and the site is seperated from the usual structure with themes and plugins because of conflicts with the Plugins and Themes - its simply easier this way. A prior version of what I currently want to update already runs there just without ajax)
I added a nopriv ajax call so you dont need admin permissions (be logged in) to execute the ajax function.
The usystorequest function is kept as easy as possible to exclude the source of the error is the code.
I tried doing it over a custom ajax handler, here is the code. It got me the same error:
FUNCTIONS.PHP
// Step 1: Add a new endpoint in your functions.php file
add_action( 'init', 'add_my_endpoint' );
function add_my_endpoint() {
add_rewrite_endpoint( 'my-endpoint', EP_ROOT );
}
// Step 2: Add a function to handle the request
add_action( 'template_redirect', 'my_endpoint_handler' );
function my_endpoint_handler() {
global $wp_query;
if ( ! isset( $wp_query->query_vars['my-endpoint'] ) ) {
return;
}
// Handle the request
if ( $_SERVER['REQUEST_METHOD'] == 'GET' && isset( $_GET['action'] ) && $_GET['action'] == 'usystorequest' ) {
usystorequest();
exit;
}
}
JAVASCRIPT WB.JS FILE
// Step 3: Update your AJAX call to use the new endpoint
jQuery(document).ready(function($) {
$('#btn-x').click(function() {
$.ajax({
url: '/?my-endpoint=1', // The URL of the custom AJAX handler
type: 'GET', // The HTTP method of the request
data: { // The data to be sent with the request
action: 'usystorequest', // The name of the action to be triggered in the WordPress backend (functions.php)
},
success: function(response) { // A function to be called if the request is successful
if (response) {$('#mail_sent').show(); //mail got sent
}
else { $('#mail_not_sent').show(); //mail didnt get sent
}
}
});
});
});

Laravel/Sanctum user fetch problem, with auth-next

I'm trying to create an SPA for a laravel REST API protected by Sanctum
Right now, on my SPA the user can log in without a problem and the API sends back the token; but then the SPA doesn't fetch the user. I mean, it doesn't even try to fetch it; no error, no request, no nothing. Login and logout work flawlessly, but I'm unable to fetch the user.
Here's my config for auth module ( v5 ):
auth: {
strategies: {
laravelSanctum: {
provider: 'laravel/sanctum',
url: 'https://XXXXXXXXXXXXX/api',
token: {
property: 'access_token',
required: true,
type: 'Bearer'
},
endpoints: {
login: { url: '/login', method: 'post' },
logout: { url: '/logout', method: 'post' },
user: { url: '/user', method: 'get' }
},
user: {
autoFetch: true
}
}
},
My login function. If I understand correctly, just after the login the laravel/sanctum provider should fetch the user data:
async login() {
try {
let response = await this.$auth.loginWith('laravelSanctum', { data: this.form })
console.log('response -> ', response)
this.$router.push('/')
} catch (error) {
console.log('Error Response login -> ', error.response)
}
},
My logout function, just for completion ( it shouldn't have anything to do with the problem ):
async logout() {
try {
let response = await this.$auth.logout()
console.log('responselogout -> ', response)
this.$router.push('/login')
} catch (error) {
console.log('Error Response -> ', error.response)
}
},
Out of despair, I even created a function to try to fetch the user manually 😅 :
async fetch() {
try {
let responseuser = await this.$auth.fetchUser()
console.log('responseuser -> ', responseuser)
let loggedin = await this.$auth.loggedIn
console.log('loggedin -> ', loggedin)
} catch (erroruser) {
console.log('Error Response user -> ', erroruser.response)
}
},
On login, everything's fine but there is no request to the user endpoint:
Login request
When I try to fetch it manually, there is no request either:
Undefined response
And then on logout, everything works as it should:
Logout request
If it made the request to the /user endpoint ( either automatically after login, or manually when I use the fetch function ) and the API rejected it, or if there was an empty answer ... I would have something to work with ( I'm in control of the API too ), but with no request I just don't know where to start debugging the problem.
Any tip would be useful. Thanks in advance!
Just passing by to say I could finally solve the problem! Yay! 🥳
There was no request to /user because there was no XSRF-TOKEN cookie. And there was no XSRF-TOKEN because of browser security.
Long story short, this solution worked for me -> https://github.com/nuxt-community/auth-module/issues/1164#issuecomment-839199946
I hope this is helpful for anyone on the same situation :)

Vuejs Laravel Passport - what should I do if access token is expired?

I am using Vuejs SPA with Laravel API as backend. I successfully got the personal access token and store in localStorage and Vuex state like below.
token: localStorage.getItem('token') || '',
expiresAt: localStorage.getItem('expiresAt') || '',
I use the access token every time I send axios request to laravel api. Every thing works well. However, initially the token was set to 1 year expiration so when I develop I didn't care about token being expired and today suddenly I thought what is going to happen if token expired. So I set token expiry to 10 seconds in laravel AuthServiceProvier.php.
Passport::personalAccessTokensExpireIn(Carbon::now()->addSecond(10));
and then I logged in and after 10 seconds, every requests stopped working because the token was expired and got 401 unauthorised error.
In this case, how can I know if the token is expired? I would like to redirect the user to login page if token is expired when the user is using the website.
Be as user friendly as possible. Rather than waiting until the token expires, receiving a 401 error response, and then redirecting, set up a token verification check on the mounted hook of your main SPA instance and have it make a ajax call to e.g. /validatePersonalToken on the server, then do something like this in your routes or controller.
Route::get('/validatePersonalToken', function () {
return ['message' => 'is valid'];
})->middleware('auth:api');
This should return "error": "Unauthenticated" if the token is not valid. This way the user will be directed to authenticate before continuing to use the app and submitting data and then potentially losing work (like submitting a form) which is not very user friendly.
You could potentially do this on a component by component basis rather than the main instance by using a Vue Mixin. This would work better for very short lived tokens that might expire while the app is being used. Put the check in the mounted() hook of the mixin and then use that mixin in any component that makes api calls so that the check is run when that component is mounted. https://v2.vuejs.org/v2/guide/mixins.html
This is what I do. Axios will throw error if the response code is 4xx or 5xx, and then I add an if to check if response status is 401, then redirect to login page.
export default {
methods: {
loadData () {
axios
.request({
method: 'get',
url: 'https://mysite/api/route',
})
.then(response => {
// assign response.data to a variable
})
.catch(error => {
if (error.response.status === 401) {
this.$router.replace({name: 'login'})
}
})
}
}
}
But if you do it like this, you have to copy paste the catch on all axios call inside your programs.
The way I did it is to put the code above to a javascript files api.js, import the class to main.js, and assign it to Vue.prototype.$api
import api from './api'
Object.defineProperty(Vue.prototype, '$api', { value: api })
So that in my component, I just call the axios like this.
this.$api.GET(url, params)
.then(response => {
// do something
})
The error is handled on api.js.
This is my full api.js
import Vue from 'vue'
import axios from 'axios'
import router from '#/router'
let config = {
baseURL : process.env.VUE_APP_BASE_API,
timeout : 30000,
headers : {
Accept : 'application/json',
'Content-Type' : 'application/json',
},
}
const GET = (url, params) => REQUEST({ method: 'get', url, params })
const POST = (url, data) => REQUEST({ method: 'post', url, data })
const PUT = (url, data) => REQUEST({ method: 'put', url, data })
const PATCH = (url, data) => REQUEST({ method: 'patch', url, data })
const DELETE = url => REQUEST({ method: 'delete', url })
const REQUEST = conf => {
conf = { ...conf, ...config }
conf = setAccessTokenHeader(conf)
return new Promise((resolve, reject) => {
axios
.request(conf)
.then(response => {
resolve(response.data)
})
.catch(error => {
outputError(error)
reject(error)
})
})
}
function setAccessTokenHeader (config) {
const access_token = Vue.cookie.get('access_token')
if (access_token) {
config.headers.Authorization = 'Bearer ' + access_token
}
return config
}
/* https://github.com/axios/axios#handling-errors */
function outputError (error) {
if (error.response) {
/**
* The request was made and the server responded with a
* status code that falls out of the range of 2xx
*/
if (error.response.status === 401) {
router.replace({ name: 'login' })
return
}
else {
/* other response status such as 403, 404, 422, etc */
}
}
else if (error.request) {
/**
* The request was made but no response was received
* `error.request` is an instance of XMLHttpRequest in the browser
* and an instance of http.ClientRequest in node.js
*/
}
else {
/* Something happened in setting up the request that triggered an Error */
}
}
export default {
GET,
POST,
DELETE,
PUT,
PATCH,
REQUEST,
}
You could use an interceptor with axios. Catch the 401s and clear the local storage when you do then redirect user to appropriate page.

AJAX call in expressJS

I can't seem to get the AJAX call correct. There have been other QA that deal with the $.ajax() function but I'm trying to solve this with $.post().
When the form button is clicked the javascript at the head is executed, which includes a $.post(). The url /login is routed through and passed to loginPost function. There a response is determined and sent back to the javascript (right?). Instead, webpage renders the response (pass || fail).
Why isn't the response from the AJAX call being sent back to get processed?
This is a simple example that I am working with to get me better acquainted to how AJAX in expressJS and jQuery work. Any Help is greatly appreciated!
--views/login.jade
script(src='/_js/jquery-1.8.2.min.js')
script
$(document).ready(function(req, res) {
$('#login').submit(function() {
var formData = $(this).serialize();
console.log(formData);
$.post('/login', formdata, processData).error('ouch');
function processData(data, status) {
console.log(status);
console.log(data);
if (data == 'pass') {
$('#content').html('<p>You have successfully loggin in!</p>');
} else {
if (! $('#fail').length) {
$('#formFrame').prepend('<p id="fail">Incorrect login information. Please try again)</p>');
}
}
} //end processData
}); //end submit
}); //end ready
div.main
h1= title
div#formFrame
form(id='login', action='/login', method='POST')
p
label(for='username') Username:
input(id='username', type='text', name='username')
p
label(for='password') Password:
input(id='password', type='password', name='password')
p
input(id='button', type='submit', name='button', value='Submit')
--routes/index.js
app.post('/login', loginPost);
--routes/loginPost
module.exports.loginPost = function(req, res) {
var password = 'admin'
, username = 'user'
, data = req.body;
if (data.username == username && data.password == password) {
res.send('pass');
} else {
res.send('fail');
}
};
You still have to stop the <form> from submitting via its default action, which can be done with event.preventDefault():
$('#login').submit(function(evt) {
evt.preventDefault();
// ...
});
Otherwise, the <form> will redirect the page to its action (or back to the current address if no action was given), interrupting the $.post request.

How can I send an AJAX request to a node.js server via HTTPS?

I have the following node.js server set up listening to port 9001
var https = require('https');
var fs = require('fs');
var qs = require('querystring');
var options = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
console.log('Request Received!');
console.log(req.method);
if (true || req.method == 'POST') {
var body = '';
req.on('data', function (data) {
body += data;
});
req.on('end', function () {
console.log(body);
var POST = qs.parse(body);
console.log(POST);
});
}
res.end("hello, world\n");
}).listen(9001);
and I am trying to get this server to respond to an AJAX call
function form_save()
{
console.log("submitted!");
var data_obj = {
data1: "item1",
data2: "item2"
}
$.ajax({
url: 'https://adam.testserver.com:9001/',
type: "POST",
dataType: "json",
data: data_obj,
success: function() {
console.log("success!");
},
complete: function() {
console.log("complete!");
}
});
}
There are two problems occurring with my arrangement. The first is that if I start the server and then click the button that triggers my form_save() the node server does not respond and I get the following error:
submitted!
OPTIONS https://adam.testserver.com:9001/ Resource failed to load
jQuery.extend.ajaxjquery.js:3633
$.ajaxjquery.validate.js:1087
form_savew_worksheet.php:64
confirm_deletew_worksheet.php:95
jQuery.event.handlejquery.js:2693
jQuery.event.add.handlejquery.js:2468
w_worksheet.php:73
complete!
At this point if I access that url directy (https://adam.testserver.com:9001/) I get the expected "hello, world" output as well as the console message "Request Received!
GET". From this point on if I click the button to trigger my AJAX call I get a new error.
submitted!
XMLHttpRequest cannot load https://adam.testserver.com:9001/. Origin
https://adam.testserver.com is not allowed by Access-Control-Allow-Origin.
w_worksheet.php:73
complete!
I don't understand why I get this message as both my form and node server reside on the same server. Thanks for taking the time to read, I appreciate any help I can get on this. I've been stuck for a while now!
You've run into the Cross-Origin Resource Sharing (CORS) specification.
Note the OPTIONS in your output. The OPTIONS HTTP Verb is used by the browser to query the web server about the URL, not to GET its contents or POST data to it.
Your server doesn't respond with the correct header data on a CORS request, so your browser assumes it has no rights to access the data, and refuses to GET or POST to the URL.
If you truly want to let any website in the world run that AJAX request, you can do something similar to the following:
function handleOptions(request, response) {
response.writeHead(200, {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Method": "POST, GET, OPTIONS",
"Access-Control-Allow-Headers": request.headers["access-control-request-headers"]
});
response.end();
}
function server(request, response) {
if(request.method == "POST") {
handlePost(request, response);
} else if(request.method == "OPTIONS") {
handleOptions(request, response);
} else {
handleOther(response);
}
}
https.createServer(sslObj, server).listen(9001);
You can fill in the details and whether you should handle GET separately, and so on (handleOther should return an appropriate error code for each request method you don't support).

Resources