adding headers to nuxt for making api rest call with axios - vuejs2

Inside my nuxt application i need to make call to an external api like this (file.vue) :
<template>
<div class="container">
<h1>{{ post.title }}</h1>
<pre>{{ post.body }}</pre>
<p><nuxt-link to="/posts">Back to the list</nuxt-link></p>
</div>
</template>
<script>
export default {
async asyncData({ app }) {
let { data } = await app.$axios.$get(`http://my.api.adress:8001/competition/sport/4?_format=json&limit=20&offset=0`)
return { post: data }
},
head() {
return {
title: this.post.title
}
}
}
</script>
To make this call works i need to pass tree arguments to my headers. Anyone has an idea on how to do that to make it work for all api call in nuxt ?

You can set headers using the axios module for nuxt (which you already do).
Taken from their documentation:
setHeader(name, value, scopes='common')
name: Name of the header
value: Value of the header
scopes: Send only on specific type of requests.
Examples:
// Adds header: `Authorization: 123` to all requests
this.$axios.setHeader('Authorization', '123')
// Overrides `Authorization` header with new value
this.$axios.setHeader('Authorization', '456')
// Adds header: `Content-Type: application/x-www-form-urlencoded` to only
// post requests
this.$axios.setHeader('Content-Type', 'application/x-www-form-urlencoded', [
'post'
])
// Removes default Content-Type header from `post` scope
this.$axios.setHeader('Content-Type', false, ['post'])
Please refer to the documentation for more information: https://github.com/nuxt-community/axios-module#setheadername-value-scopescommon

Related

Making request to Spring Boot Admin server from custom view?

I'm trying to add a custom view with some administrative utilities to Spring Boot Admin. The idea is to implement these as endpoints in Springboot Admin and call these endpoints from my custom view, but I don't know how to make a call to the server itself.
When a custom view has parent: 'instances' it will get an axios client for connecting to the current instance, but since the view I'm building isn't tied to a specific instance it doesn't have this. I'm aware I can install axios as a dependency, but I'd like to avoid that if possible to reduce build times. Since SBA itself depends on axios it seems I shouldn't have to install it myself.
Based on this sample, this is what I have right now:
index.js
/* global SBA */
import example from './example';
import exampleEndpoint from './example-endpoint';
SBA.use({
install({viewRegistry}) {
viewRegistry.addView({
name: 'example',
path: '/example',
component: example,
label: 'Example',
order: 1000,
});
}
});
example.vue
<template>
<div>
<h1>Example View</h1>
<p>
<b>GET /example:</b> <span v-text="exampleResponse" />
</p>
</div>
</template>
<script>
export default {
props: {
applications: {
type: Array,
required: true
}
},
data: () => ({ exampleResponse: "No response" }),
async created() {
const response = await this.axios.get("example");
this.exampleResponse = response.response;
},
};
</script>
ExampleController.kt
#RestController
#RequestMapping("/example")
class ExampleController {
#GetMapping
fun helloWorld() = mapOf("response" to "Hello world!")
}
Console says that it can't read property get of undefined (i.e. this.axios is undefined). Text reads "GET /example: No response"
I'm not sure if this is the best way to do it, but it is a way.
I noticed that I do have access to the desired axios instance within the SBA.use { install(...) { } } block, and learned that this can be passed as a property down to the view.
index.js
/* global SBA */
import example from './example';
import exampleEndpoint from './example-endpoint';
SBA.use({
install({viewRegistry, axios}) {
viewRegistry.addView({
name: 'example',
path: '/example',
component: example,
label: 'Example',
order: 1000,
// this is where we pass it down with the props
// first part is the name, second is the value
props: { "axios": axios },
});
}
});
example.vue
<template>
<div>
<h1>Example View</h1>
<p>
<b>GET /example:</b> <span v-text="exampleResponse" />
</p>
</div>
</template>
<script>
export default {
props: {
applications: { type: Array, required: true },
// this is where we retrieve the prop. the name of the field should
// correspond to the name given above
axios: { type: Object, required: true },
},
data: () => ({
exampleResponse: "No response",
}),
async created() {
// Now we can use our axios instance! And it will be correctly
// configured for talking to Springboot Admin
this.axios.get("example")
.then(r => { this.exampleResponse = r.data.response; })
.catch(() => { this.exampleResponse = "Request failed!" });
},
};
</script>
Based on the code given, it looks like you don't have axios initialized to how you want to use it.
You're calling it via this.axios but it's not in your component i.e
data() {
return {
axios: require("axios") // usually this is imported at the top
}
}
or exposed globally i.e
Vue.prototype.axios = require("axios")
You can simply just import axios and reference it.
<script>
import axios from 'axios';
export default {
created() {
axios.get()
}
}
</script>

using `$router.push` when validating a dynamic route

I am using Nuxt and I have created a have a dynamic route which fetches data using vuex and an axios call. I want to route the user back to index when no id params are specified. I see that i can use validate on dynamic routes https://nuxtjs.org/api/pages-validateWhen
I am using the code below, when I navigate to localhost:3000/settings/ i get the following error Cannot read property 'push' of undefined
pages/settings/_id.vue
export default {
validate({ params }) {
if (params.id !== null) {
this.$router.push({ name: 'index' })
}
return false
}
}
The validate is called every time before navigating to a new route. It will be called server-side once. This means the validate is executed before your component is created. So you can't access this.$router because this dont exist at this time.
Take a look in Fetch Hook and Nuxt Lifecycle to better explanations:
https://nuxtjs.org/blog/understanding-how-fetch-works-in-nuxt-2-12/#fetch-hook-and-nuxt-lifecycle
Basiclly what we need to do is import the redirect and route from the nuxt context to use it insite or validate function. The ROUTE have all the informations about your request, and the redirect is a function from nuxt is used inside middlewares.
I have created a exemple to you:
<template>
<v-row>
<v-col cols="12">
<pre>
{{ params }}
</pre>
</v-col>
</v-row>
</template>
<script>
export default {
data() {
return {
params: []
}
},
validate({ redirect, route }) {
if (route.params) {
// eslint-disable-next-line no-console
console.log('RouteParams:', route.params)
this.params = route.params
}
redirect({ name: 'index' })
// return false
}
}
</script>

Returning json data from url with Vue.js

I am working on a simple app that returns one value from a json resource at certain url.
Although I've created a vue.config.js file to avoid CORS problem, still getting on execution the message:
Access to fetch at 'https://api.darksky.net/forecast/xxx/37.8267,-122.4233' from origin 'http://localhost:8080' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
What am I missing? Thank you very much!
Location.vue
<template>
<div>
<h1>{{ forecast.timezone }}</h1>
</div>
</template>
<script>
export default {
name: 'Location',
props: {
forecast: Array
}
}
</script>
App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<location v-bind:forecast="forecast" />
</div>
</template>
<script>
import Location from './components/Location.vue'
export default {
name: 'App',
components: {
Location
},
data() {
return {
forecast: []
}
},
mounted() {
this.getTimeZone()
},
methods: {
async getTimeZone() {
try {
const response = await fetch('https://api.darksky.net/forecast/xxx/37.8267,-122.4233')
const data = await response.json()
this.forecast = data
} catch (error) {
console.error(error)
}
}
}
}
</script>
vue.config.js
module.exports = {
devServer: {
proxy: 'https://api.darksky.net/forecast/xxx/'
}
}
You need to setup CORS policy for external API. It's not related to vue.js.
Alternatively, if the external API is a 3rd-party API and you cannot change the CORS policy, then you can consume the external API in your server-side code and create your own API in your server-side code that will return whatever value you get from the external API.
From Vue Cli docs:
WARNING
When devServer.proxy is set to a string, only XHR requests will be proxied. If you want to test an API URL, don't open it in the browser, use an API tool like Postman instead.
So, setting the devServer.proxy property, does not solve the issue, as api requests won't be proxied. For a permanent solution, as Circuit Breaker suggested, you have to allow the CORS request on the api server.

Axios + Vue executing same POST twice

While executing a POST in Axios, the request is always sent twice. This is not a CORS issue, as I get two pairs of requests (CORS and POST, and then ANOTHER CORS and POST).
I scaffolded a sample application using the hello-world template and it still exhibits the same behaviour:
<template>
<div class="hello">
Test
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'HelloWorld',
props: {
msg: String
},
mounted() {
axios.create({
baseURL: 'https://api.example.com'
}).post('/login', {
token: 'tokenString'
}).then((a) => {
console.log(a)
})
.catch((err) => {
console.log('err login')
console.log(err)
})
}
}
</script>
<style scoped>
</style>
I only get the console.log of the second request, but the network tab clearly shows the two pairs of requests, and the API also indicates that two POST requests (with exactly the same parameters) have been made...
This is actually a problem with an extension called Privacy Possum, so it has nothing to do with axios in particular.

Rails API responding to request, but axios not retrieving data

I have a Rails 5.2.2 API that is serving JSON data at localhost:3000/v1/stories. I am trying to set up a Vuejs app to consume it with Axios. The rails terminal shows the request and throws no errors:
Started GET "/v1/stories" for 127.0.0.1 at 2018-12-19 18:06:44 -0500
Processing by V1::StoriesController#index as HTML
Story Load (0.8ms) SELECT "stories".* FROM "stories"
↳ app/controllers/v1/stories_controller.rb:6
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::JsonApi (6.25ms)
Completed 200 OK in 10ms (Views: 8.7ms | ActiveRecord: 0.8ms)
MyIn the vue app, I have the following relevant files:
Api.js retrieves the base connection:
import axios from 'axios'
export default() => {
return axios.create({
baseURL: `http://localhost:3000/v1`
headers: {
'Accept': 'applicaton/json',
'Content-Type': 'application/json'
}
})
}
StoriesService.js:
import Api from '#/services/Api'
export default {
fetchStories () {
return Api().get('/stories')
}
}
and in the view (components/Stories.vue):
<template>
<div class="stories">
<h1>Stories</h1>
<div v-if="stories" class="table-wrap">
<div v-for="story in stories" :key="story.id">
<h3>Title: {{story.title}}</h3>
<p>Summary: {{story.description}}</p>
</div>
</div>
<div v-else>
There are no stories... Let's add one now <br><br>
<!-- <router-link v-bind:to="{ name: 'NewStory' }" class="add_story_link">Add Story</router-link> -->
</div>
</div>
</template>
<script>
import StoriesService from '#/services/StoriesService'
export default {
data () {
return {
stories: []
}
},
mounted () {
this.getStories()
},
methods: {
async getStories () {
const response = await StoriesService.fetchStories()
this.stories = response.data.stories
}
}
}
</script>
In the Rails app, cors.rb is:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*',
:headers => :any,
methods: %i(get post put patch delete options head)
end
end
I made the suggested adjustment. here is the response from the GET request to the back end in the. It shows the data coming through, but for some reason, it does not display from the v-for loop.
Your Rails API endpoint is returning the stories array assigned to a data property, eg
{
"data":[...]
}
The response body object is assigned to response.data via Axios so what you actually want is
this.stories = response.data.data