As axios GitHub page states, default response of axios request is:
{
// `data` is the response that was provided by the server
data: {},
// `status` is the HTTP status code from the server response
status: 200,
// `statusText` is the HTTP status message from the server response
statusText: 'OK',
// `headers` the headers that the server responded with
// All header names are lower cased
headers: {},
// `config` is the config that was provided to `axios` for the request
config: {},
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance in the browser
request: {}
}
Problem:
My API response schema is:
{
success: true,
message: '',
data: {}
}
So every time I make a request, I have to handle the response like:
(...).then((res) => res.data.data);
How can I change the response schema of axios to avoid doing .data.data every time?
You can use a response interceptor to change the value of the promise:
axios.interceptors.response.use(function (response) {
return response.data.data
})
You'd just do this once and then it would apply to all requests made via the default axios instance. A similar approach can be taken if you're creating your own axios instances using axios.create.
You may also need to consider how to handle error cases but the approach is much the same.
Documentation: https://github.com/axios/axios#interceptors
Update:
If you need access to success, message and data you would just need this instead:
axios.interceptors.response.use(function (response) {
return response.data
})
Destructuring would potentially be useful when you write the then handler:
(...).then(({ data, success, message }) => {
});
Related
I have a Vue.js application that uses axios to send request to ApiGee Server.
This is the code i use to send request to APIgee.
const headers = {
'X-API-Key': 'randomKey123123'
}
return axios({
method: 'get',
url: url,
headers: headers
}).then(response => {
console.log(response)
})
from the ApiGee, I can see OPTIONS request being received first since I done the request from the client side browser.
I can also see the X-API-Key header key but the value is missing.
The error I'm getting from the browser console is
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource
Below code helped me you can try this format:
created() {
// POST request using axios with set headers
const article = { title: "Vue POST Request Example" };
const headers = {
'X-API-Key': 'randomKey123123'
};
axios.post(url, article, { headers })
.then(response => console.log(response.data)
);
}
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
I'm working with Vue to interact with an external API on a Drupal website, but in order to do so dynamically per Drupal user, I need to get a token from Drupal first. To do so, I'm trying to do two GET requests. The first gets me a bearer token out of Drupal, and the second uses it to authenticate the third-party API request.
Below is what I'm trying – I'm able to get the token successfully in the first request's response, but not when I try to use it in the header of my second request. If I try hardcoding the token that I see in the console log, it does work, so I know none of that is the issue. It's just that this.jwt['data']['token'] in the second request's headers seems to not pull back the token.
What do I need to adjust in order to access the token from the first response as part of the headers of my second request?
created() {
axios
.get('/jwt/token')
.then(response => {
this.jwt = response
console.log(this.jwt['data']['token']) // this does show what I want to access later
})
},
mounted() {
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: "Bearer " + this.jwt['data']['token'] } // ...but this doesn't work
})
.then(response => {
this.comments = response
})
},
It's likely the response to the token request has not finished by the time the component mounts, at which point this.jwt is not yet assigned.
I would move the token request into the mounted hook, fetching comments only when the token request succeeds:
export default {
mounted() {
axios
.get('/jwt/token')
.then(tokenResp => {
this.jwt = tokenResp
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: 'Bearer ' + this.jwt['data']['token'] }
})
.then(commentsResp => {
this.comments = commentsResp
})
})
}
}
Post Functionality in Vue.js is returning a null value.
The API Call is local to my machine on a different port. The GET Functionality work as expected. The POST functionality doesn't work as expected only returns null.
fetch('http://localhost:8080/Exercise/lists/add', {
method: 'POST',
headers: {
"Content-Type" : "application/json",
},
body: JSON.stringify(this.user)
})
.then((response) => {
if(response.ok) {
console.log('Response is Ok.');
}
})
.catch((err) => {console.log(err)});
}
Expected to add a user. Rather returns a null value.
Console.log output here..
PostMan "post" service
PostMan "post" service working..
According to this site, the body of the post request is formated like 'foo=bar&lorem=ipsum'. But in your case the data are a JSON stringified object like "{"x":5,"y":6}". This could make a difference for your backend.
Also you can control the requests between your browser and the backend with the browser's network insepector (for Firefox it's Ctrl+Maj+J, then Network tab). It will tell you what data you send to your server and what is the response.
You should use Axios for API calls in Vue. You can find the Axios reference from the documentation https://v2.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html.
According to the documentation from Google when you use the POST request method you need to pass body and the image you uploaded shows you used firstName param. So either change your API and use body to get the first name or you can do something like this:
fetch('http://localhost:8080/Exercise/lists/add?firstName='+JSON.stringify(this.user), {
method: 'POST',
headers: {
"Content-Type" : "application/json",
},
body: ''
})
.then((response) => {
if(response.ok) {
console.log('Response is Ok.');
}
})
.catch((err) => {console.log(err)});
}
Even though my code is inside one of my components in Vue, the problem is with Axios, let me explain why. So, I'm trying to get some information, like this:
axios.get('http://localhost:8181/roles/1',
{
headers: {
'Api-Token': 'tokenTOKEN',
'Content-Type': 'application/json'
}
}
)
.then(response => {console.log(response)})
.catch(response => {
console.log(response);
})
So, yes, I'm importing Axios correctly. Yes, I know we should not be sending a Content-Type header in a GET request. However, I already read the RFC 7231 and it doesn't say is impossible, is just not common. So, we want to send a Content-Type header in my request.
So, how do I know it doesn't work? Well, one of my middlewares in my Lumen API goes like this:
<?php
namespace App\Http\Middleware;
use Closure;
class JsonVerifier
{
public function handle($request, Closure $next)
{
if($request->isJson())
{
return $response = $next($request);
}
else
{
return response('Unauthorized.', 401);
}
}
}
I tried to use Postman to send that specific GET request, and it works. I tried to use fetch() like this:
var miInit = { method: 'GET',
headers: {
'Api-Token': 'tokenTOKEN',
'Content-Type': 'application/json'
},
mode: 'cors',
cache: 'default' };
fetch('http://localhost:8181/roles/1',miInit)
.then(function(response) {
console.log(response);
})
and it works! In both cases (with Postman and fetch()) my API returns the desire data.
However, when I try with Axios, I get a 401 response with the "Unauthorized" word, meaning that Axios didn't send the header correctly.
Now, the question. Is there any other way to send headers in an axios GET request? How can I force Axios to send the headers no matter what as it seem to be case with fetch() and Postman?
Axios automatically (as it should) removes the Content-Type header if you're sending a request without a body, as you do with any GET request.
https://github.com/axios/axios/blob/2ee3b482456cd2a09ccbd3a4b0c20f3d0c5a5644/lib/adapters/xhr.js#L112
// Add headers to the request
if ('setRequestHeader' in request) {
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
// Remove Content-Type if data is undefined
delete requestHeaders[key];
} else {
// Otherwise add header to the request
request.setRequestHeader(key, val);
}
});
}
You're probably looking for the Accepts header and $request->wantsJson() (or acceptsJson()) instead.