Why is Axios sending an extra 204 preflight request with my POST request? - vue.js

Whenever I send a POST request using Vue.js (3.x), an additional request to the same URL is being made with HTTP status code 204 & type of "preflight".
What is this preflight request and how can I fix it so it isn't sent as a duplicate?
Register.vue
async submit() {
this.button = true;
try {
const response = await axios.post(`register`, this.form);
if(response.data.success == false)
{
console.log(response.data.message);
}
else
{
this.$router.push('/');
}
}
catch (error)
{
let { errors } = error.response.data;
this.button = false;
this.errors = {};
Object.keys(errors).forEach(element => {
this.errors[element] = errors[element][0];
});
}
},

This is not an issue and is controlled by the browser by design.
It is not something Axios or any other HTTP client decides to send.
A preflight request is a CORS OPTIONS request & are automatically sent by browsers specifically to check if the server would support the call you are trying to make in terms of method, headers and origin.
You can safely ignore the requests if they do not fail as that means that the server will not be rejecting your request on the basis of the aforementioned factors.
Your issue relates to the endpoint not existing as you are getting a 404 Not Found error - check to see if the endpoint exists or if you are calling it correctly.

Related

Vue axios doesnt change header

I am quite new to vue and I am trying to send a request to my api using axios.
I build an interceptor which seems to work (logging is happening)
export default function setup() {
console.log('Http interceptor starting...')
Axios.interceptors.request.use((request) => {
const token = store.getters.token;
if (token) {
request.headers.Authorization = `Bearer ${token}`;
}
console.log(request);
return request
}, (err) => {
return Promise.reject(err);
});
}
If I check the console I can see the request including the token. If I check my network tab in the browser i can see the same request without the token. If I check the console of my api the token is null. Any Ideas?
Edit: If I use postman with the same request and the same token it is working as it shuld

Handling an authentication page returned by an axios request in vue

I have a vue app that sits behind a firewall, which controls authentication. When you first access the app you need to authenticate after which you can access the app and all is well until the authentication expires. From the point of view of my app I only know that the user needs to re-authenticate when I use axios to send off an API request and instead of the expected payload I receive a 403 error, which I catch with something like the following:
import axios from 'axios'
var api_url = '...'
export default new class APICall {
constructor() {
this.axios = axios.create({
headers: {
'Accept': 'application/json',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
},
withCredentials: true,
baseURL: api_url
});
}
// send a get request to the API with the attached data
GET(command) {
return this.axios.get(command)
.then((response) => {
if (response && response.status === 200) {
return response.data; // all good
} else {
return response; // should never happen
}
}).catch((err) => {
if (err.message
&& err.message=="Request failed with status code 403"
&& err.response && err.response.data) {
// err.response.data now contains HTML for the authentication page
// and successful authentication on this page resends the
// original axios request, which is in err.response.config
}
})
}
}
Inside the catch statement, err.response.data is the HTML for the authentication page and successfully authenticating on this page automatically re-fires the original request but I can't for the life of me see how to use this to return the payload I want to my app.
Although it is not ideal from a security standpoint, I can display the content of err.response.data using a v-html tag when I do this I cannot figure out how to catch the payload that comes back when the original request is fired by the authentication page, so the payload ends up being displayed in the browser. Does anyone know how to do this? I have tried wrapping everything inside promises but I think that the problem is that I have not put a promise around the re-fired request, as I don't have direct control of it.
Do I need to hack the form in err.response.data to control how the data is returned? I get the feeling I should be using an interceptor but am not entirely sure how they work...
EDIT
I have realised that the cleanest approach is to open the form in error.response.data in a new window, so that the user can re-authenticate, using something like:
var login_window = window.open('about:blank', '_blank');
login_window.document.write(error.response.data)
Upon successful re-authentication the login_window now contains the json for the original axios get request. So my problem now becomes how to detect when the authentication fires and login_window contains the json that I want. As noted in Detect form submission on a page, extracting the json from the formatting window is also problematic as when I look at login_window.document.body.innerText "by hand" I see a text string of the form
JSON
Raw Data
Headers
Save
Copy
Collapse All
Expand All
status \"OK\"
message \"\"
user \"andrew\"
but I would be happy if there was a robust way of determining when the user submits the login form on the page login_window, after which I can resend the request.
I would take a different approach, which depends on your control over the API:
Option 1: you can control (or wrap) the API
have the API return 401 (Unauthorized - meaning needs to authenticate) rather than 403 (Forbidden - meaning does not have appropriate access)
create an authentication REST API (e.g. POST https://apiserver/auth) which returns a new authentication token
Use an Axios interceptor:
this.axios.interceptors.response.use(function onResponse(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// no need to do anything here
return response;
}, async function onResponseError(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if ("response" in error && "config" in error) { // this is an axios error
if (error.response.status !== 401) { // can't handle
return error;
}
this.token = await this.axios.post("auth", credentials);
error.config.headers.authorization = `Bearer ${this.token}`;
return this.axios.request(config);
}
return error; // not an axios error, can't handler
});
The result of this is that the user does not experience this at all and everything continues as usual.
Option 2: you cannot control (or wrap) the API
use an interceptor:
this.axios.interceptors.response.use(function onResponse(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// no need to do anything here
return response;
}, async function onResponseError(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if ("response" in error && "config" in error) { // this is an axios error
if (error.response.status !== 403) { // can't handle
return error;
}
if (!verifyLoginHtml(error.response.data)) { // this is not a known login page
return error;
}
const res = await this.axios.post(loginUrl, loginFormData);
return res.data; // this should be the response to the original request (as mentioned above)
}
return error; // not an axios error, can't handler
});
One solution is to override the <form>'s submit-event handler, and then use Axios to submit the form, which gives you access to the form's response data.
Steps:
Query the form's container for the <form> element:
// <div ref="container" v-html="formHtml">
const form = this.$refs.container.querySelector('form')
Add a submit-event handler that calls Event.preventDefault() to stop the submission:
form.addEventListener('submit', e => {
e.preventDefault()
})
Use Axios to send the original request, adding your own response handler to get the resulting data:
form.addEventListener('submit', e => {
e.preventDefault()
axios({
method: form.method,
url: form.action,
data: new FormData(form)
})
.then(response => {
const { data } = response
// data now contains the response of your original request before authentication
})
})
demo

ionic 2: http get request not working (proxy added)

I'm using Http from #angular/http to send GET requests, but the server is not receiving the request. The generated urls are correct because when I log them and open them in browser (I've tried all of Chrome, Firefox and Safari), the server does receive these requests.
This is how I am doing this:
let logButtonUrl = this.urlGenerator.generateTiramisuUrlTemp(this.servletPath,
argMap);
console.log("logButtonUrl:"+logButtonUrl);
return this.http.get(logButtonUrl).map(this.writeSuccess);
Function writeSuccess:
private writeSuccess(res: Response) {
let body = res.json();
let rows_affected = body.data[0].rowsAffected;
if (rows_affected == "1") {
return true;
} else {
return false;
}
}
I got no error message in browser console, so it's probably not because of the CORS issue discussed here:
http://blog.ionic.io/handling-cors-issues-in-ionic/
I also tried using a proxy. I added this in ionic.config.json:
{
"path": "/backendTemp",
proxyUrl": "http://128.237.217.70:8080" /*the ip address of the target server*/
}
And replace the ip address in my generated urls with "/backendTemp". Still not working.
Any suggestions/thoughts on this? Thanks a lot!
Use the $http (https://docs.angularjs.org/api/ng/service/$http):
.controller('RequestCtrl', function ($http) {
$http({
method: 'GET',
url: 'http://128.237.217.70:8080/backendTemp'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});

How to redirect PUT request to POST request in express router route handler?

I am handling PUT /api/checkout route with express router:
this.router.put('/:id/checkout', (req, response, next) => { /*...*/ });
Now as I am handling different types of payments with different third party services one of which expects POST request I am looking for a way handle the PUT request by executing the POST request to third party service endpoint. How do I do that ?
What i did at this point is execute POST request directly with request.post.
The challenge at this point is how to handle the resource moved response.
request.post({
url : 'https://paymentgateway.com/charge',
form : {
product_id : product_id,
amount : amount
}
}, (err, httpResponse, body) => {
The response I get here is httpResponse.statucCode === 302 and body === <html><head><title>Object moved</title></head><body>. Not sure how to handle this. HTML form with same request parameters automatically redirects the client to redirect URL.
Returning the httpResponse to the client requesting the checkout action does the job. Client handles redirection automatically.
}, (err, httpResponse, body) => {
if (err) {
logger.error(err);
return response.status(err.code || 500).send(err);
}
return response.status(httpResponse.statusCode).send(httpResponse);
});

CORS doesn't work

i was trying to make asynchronous call to Yahoo's symbol suggest JSONP API, so there's cross domain problem, I have read this document and try to change it's url , the following are the codes i use
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
function makeCorsRequest() {
// All HTML5 Rocks properties support CORS.
// var url = 'http://updates.html5rocks.com';
var url = 'http://autoc.finance.yahoo.com/autoc?query=google&callback=YAHOO.Finance.SymbolSuggest.ssCallback';
var xhr = createCORSRequest('GET', url);
if (!xhr) {
alert('CORS not supported');
return;
}
// Response handlers.
xhr.onload = function() {
var text = xhr.responseText;
console.log(text);
};
xhr.onerror = function() {
alert('Woops, there was an error making the request.');
};
xhr.send();
}
but the problem still not solved:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
does anyone know why? Also, I compared the code in document with regular ajax code, they are almost the same, how does CORS work?
thanks
For CORS to work, the server needs to set the Access-Control-Allow-Origin header. If you do not control the server, and the server hasn't set that header, then I'm afraid you're out of luck.
CORS replaces JSONP as the way to load cross-domain json content, but with JSONP the server also needs to implement it.
If the owner of the content doesn't want you to use it, the browser will reject it.
Edit: of course you can avoid the cross-browser issue by having your server get the content from the original server, and having the browser get it from your own server. More work, but it's not cross-browser anymore.