QPANC são as iniciais de Quasar PostgreSQL ASP NET Core.
- Source
- Introdução
- Parte I - ASP.NET - Inicializando os Projetos
- Parte 2 - PostgreSQL
- Parte 3 - ASP.NET - Registrando Serviços e Lendo Variáveis de Ambiente
- Parte 4 - ASP.NET - Entity Framework e ASP.NET Core Identity
- Parte 5 - ASP.NET - Documentação Interativa com Swagger
- Parte 6 - ASP.NET - Regionalização
- Parte 7 - ASP.NET - Autenticação e Autorização
- Parte 8 - ASP.NET - CORS
- Parte 9 - Quasar - Criação e Configuração do Projeto
- Parte 10 - Quasar - Configurações e Customizações
- Parte 11 - Quasar - Componentes - Diferença entre SPA e SSR
- Parte 12 - Quasar - Serviços
- Parte 13 - Quasar - Regionalização e Stores
- Parte 14 - Quasar - Consumindo a API
- Parte 15 - Quasar - Login
- Parte 16 - Quasar - Áreas Protegidas
- Parte 17 - Quasar - Registro
- Parte 18 - Docker - Maquina Virtual Linux
- Parte 19 - Docker - Registro e Build
- Parte 20 - Docker - Traefik e Publicação
- Demo Online
30 Axios
Durante a criação do projeto do Quasar, você pode optar por criar ou não o boot do axios
, mas mesmo que tenha o criado, ele não está adequado para uma aplicação SSR
.
O nosso primeiro passo, será configurar uma rede interna no arquivo de configuração do docker-compose
, para que o aplicativo do Quasar possa se comunicar com a API
.
QPANC/docker-compose.yml
services:
qpanc.api:
networks:
qpanc.network:
qpanc.internal:
ipv4_address: "172.18.18.3"
qpanc.app:
networks:
qpanc.network:
qpanc.internal:
ipv4_address: "172.18.18.6"
qpanc.database:
networks:
qpanc.network:
qpanc.internal:
ipv4_address: "172.18.18.9"
networks:
qpanc.internal:
internal: true
driver: bridge
ipam:
driver: default
config:
- subnet: 172.18.18.0/24
qpanc.network:
Durante o desenvolvimento, o comando quasar dev
não é executado durante a construção da imagem, ele será executado quando tentamos levantar o container
com a imagem de desenvolvimento.
por isto, precisamos setar as variáveis API_CLIENT_URL
e API_SERVER_URL
direto no comando, junto ao quasar dev
. lembrando que esta alteração deve ser feita no docker-compose.override.yml
.
QPANC/docker-compose.override.yml
services:
qpanc.app:
command: /bin/sh -c "yarn && API_CLIENT_URL=https://localhost:34513/ API_SERVER_URL=https://172.18.18.3:443/ quasar dev -m ssr"
Agora, precisamos modificar o quasar.config.js > build > extendWebpack
, para injetamos a URL da API.
QPANC.App/quasar.config.js
const { DefinePlugin } = require('webpack')
module.exports = function (ctx) {
return {
build: {
extendWebpack (cfg, { isServer }) {
const definePlugin = cfg.plugins.find(plugin => plugin instanceof DefinePlugin)
const apiUrl = JSON.stringify(isServer ? process.env.API_SERVER_URL : process.env.API_CLIENT_URL)
definePlugin.definitions['process.env'].API_URL = apiUrl
}
}
}
}
Estamos usando o extendWebpack
, pois o API_URL
deve ter um valor diferente no build: CLIENT
quando comparado com o build: SERVER
, quando não é necessário fazer esta diferenciação, podemos usar o quasar.config.js
> build
> env
.
Agora, voltemos a nossa atenção para o boot
do axios
.
import axios from 'axios'
import inject from './inject'
import { Notify } from 'quasar'
export default inject(({ store, router }) => {
const instance = axios.create({
baseURL: process.env.API_URL
})
instance.interceptors.request.use((config) => {
const token = store.state.app.token
const locale = store.getters['app/locale']
config.headers['Accept-Language'] = locale
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
}, (error) => {
return Promise.reject(error)
})
instance.interceptors.response.use((response) => {
return response
}, (error) => {
const { status } = error.response || {}
let message = store.$t('http.generic')
switch (status) {
case 400: message = store.$t('http.badRequest'); break
case 401: message = store.$t('http.unauthorized'); break
case 403: message = store.$t('http.forbidden'); break
case 422:
if (error.response.data) {
const { title, errors } = error.response.data
message = title
message += '<ul>'
for (const key in errors) {
const error = errors[key]
for (const msg of error) {
message += '<li>' + msg + '</li>'
}
}
message += '</ul>'
store.$root.$emit('unprocessable', errors)
} else {
message = store.$t('http.unprocessable')
}
break
case 500: message = store.$t('http.serverError'); break
case 503: message = store.$t('http.serviceUnavailable'); break
}
Notify.create({
type: 'negative',
html: true,
message: message
})
if (status === 401) {
store.commit('app/token', undefined)
router.push('/login')
}
return Promise.reject(error)
})
return {
axios: instance
}
})
Um pequeno detalhamento sobre o que está sendo feito acima:
const instance = axios.create({
baseURL: process.env.API_URL
})
Aqui estamos verificando se estamos do lado do servidor ou do cliente, e criando uma instancia do axios
com a URL apropriada.
instance.interceptors.request.use((config) => {
const token = store.state.app.token
const locale = store.getters['app/locale']
config.headers['Accept-Language'] = locale
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
}, (error) => {
return Promise.reject(error)
})
Aqui estamos interceptando todas as requisições feitas com o axios
, e adicionando o header de autenticação Authorization: Bearer ${token}
, assim como a linguagem definida pelo usuário (que pode ser diferente da do browser/OS).
instance.interceptors.response.use((response) => {
return response
}, (error) => {
const { status } = error.response || {}
let message = i18n.t('http.generic')
switch (status) {
case 400: message = store.$t('http.badRequest'); break
case 401: message = store.$t('http.unauthorized'); break
case 403: message = store.$t('http.forbidden'); break
case 422:
...
break
case 500: message = store.$t('http.serverError'); break
case 503: message = store.$t('http.serviceUnavailable'); break
}
Notify.create({
type: 'negative',
html: true,
message: message
})
if (status === 401) {
...
}
return Promise.reject(error)
})
Neste ponto, estamos interceptando as requisições que falharam, e exibindo uma notificação não intrusiva.
case 422:
if (error.response.data) {
const { title, errors } = error.response.data
message = title
message += '<ul>'
for (const key in errors) {
const error = errors[key]
for (const msg of error) {
message += '<li>' + msg + '</li>'
}
}
message += '</ul>'
store.$root.$emit('unprocessable', errors)
} else {
message = store.$t('http.unprocessable')
}
break
Aqui, estamos adicionando um tratamento adicional para o erro 422
, estaremos personalizando a notificação para exibir os erros retornados pela API, assim como, emitindo um erro com estes eventos. Não estamos usando o $t
para localizar a mensagem, por que a API
já está retornando o texto localizado.
Este evento será interceptado pelos componentes, para que eles possam atualizar a UI para exibir o erro da maneira correta.
if (status === 401) {
store.commit('app/token', undefined)
router.push('/login')
}
E caso a aplicação retorne um 401, possivelmente houve a tentativa de acessar um recurso protegido usando um token
invalido, note que, uma falha na autorização resultaria em um error 403
E como estamos regionalizando as mensagens de erro, precisamos incluir os devidos textos os arquivos de internacionalização.
Quasar.App/src/i18n/en-us/index.js
export default {
http: {
generic: 'Something not right happened',
badRequest: 'We aren\'t able of to do your request, please review all the fields',
unauthorized: 'You aren\'t authorized, please login',
forbidden: 'You aren\'t allowed to do that action',
unprocessable: '@:http.badRequest',
serverError: 'An unexpected error occurred at the API',
serviceUnavailable: 'An error occurred at the API, mostly because a third party service'
}
}
Quasar.App/src/i18n/pt-br/index.js
export default {
http: {
generic: 'Algo de errado aconteceu',
badRequest: 'Não foi possivel realizar esta ação, por favor revise os campos',
unauthorized: 'Você não está logado',
forbidden: 'Você não está autorizado a realizar esta ação',
unprocessable: '@:http.badRequest',
serverError: 'Ocorreu um erro inesperado na API',
serviceUnavailable: 'Ocorreu um erro na API, possivelmente ao tentar acessar um serviço externo'
}
}
Top comments (0)