Cet article a été publié à l'origine sur le blog des développeurs d'Okta . Merci de soutenir les partenaires qui rendent SitePoint possible.
Les applications à page unique (SPA) améliorent l'expérience utilisateur en offrant des interactions d'interface utilisateur riches, un retour rapide d'informations et le soulagement de savoir que vous n'avez pas besoin de télécharger et d'installer une application traditionnelle. . Les navigateurs sont maintenant des systèmes d'exploitation et les sites Web sont des applications. Bien qu'un SPA ne soit pas toujours la solution, les applications qui reposent sur une interaction utilisateur rapide sont de plus en plus courantes.
Pour l'utilisateur final, un SPA bien conçu se présente comme une arcs-en-ciel et des licornes. Du point de vue des développeurs, la réalité peut être bien au contraire. Les problèmes difficiles résolus depuis longtemps sur le serveur, tels que l'authentification, le routage, la gestion des états, la liaison de données, etc., deviennent des défis fastidieux pour les clients frontaux. Heureusement pour nous, des frameworks JavaScript tels que Vue, React et Angular existent pour nous aider à concevoir des applications puissantes et à consacrer plus de temps à la fonctionnalité critique et à ne pas réinventer la roue.
À propos de Vue.js
Qui mieux décrire Vue que son créateur, Evan You?
Vue (prononcé
/ vjuː /
comme vue) est un cadre évolutif permettant de créer des interfaces utilisateur. Il est conçu dès le départ pour être progressivement adoptable et peut facilement évoluer entre une bibliothèque et un cadre en fonction de différents cas d'utilisation. Elle consiste en une bibliothèque principale accessible, centrée uniquement sur la couche de visualisation, et en un écosystème de bibliothèques de support qui vous aident à gérer la complexité d'applications volumineuses à page unique.
Voici certains des avantages de Vue:
- Une courbe d'apprentissage douce et barrière d'accès réduite
- Offre la possibilité d'amorcer votre application avec
vue-cli
ce qui vous évite d'avoir à configurer le Webpack et les pipelines de construction complexes - Croissance explosive des communautés! Vue a maintenant plus d’étoiles sur GitHub que React et Angular
- Il est suffisamment flexible pour adopter à un rythme raisonnable composante par composante
Créez votre application Vue + Go
Dans ce didacticiel, vous allez créer une page unique. application qui montre l'amour pour ouvrir des projets source sur GitHub. Pour le front-end, vous utiliserez Vue et des outils populaires tels que vuex
vue-cli
vuetify
et vue-router
. Sur le backend, vous utiliserez Go pour écrire une API REST et conserver vos données dans MongoDB.
L'authentification et la gestion des utilisateurs pouvant être un problème majeur, vous devrez donc utiliser l'authentification basée sur JSON Web Token (JWT) pour les requêtes. depuis le SPA et le Go JWT Verifier d'Okta en tant que middleware sur votre backend pour valider le jeton de l'utilisateur à chaque requête.
Une fois l'opération terminée, les utilisateurs pourront s'authentifier via OpenID Connect (OIDC), puis recherchez projets sur GitHub, favorisez-les et ajoutez-y des notes si nécessaire!
Création de la structure de répertoires Vue et Go
Par souci de simplicité, écrivez l'API REST et le SPA dans le même projet, en commençant par le projet répertoire dans l'espace de travail Go.
Les projets Go résident dans le répertoire dans lequel la variable d'environnement $ GOPATH
pointe. Pour trouver la valeur $ GOPATH
actuelle, exécutez: go env GOPATH
. Pour en savoir plus sur GOPATH et savoir comment le configurer vous-même, reportez-vous à la documentation officielle Go sur le sujet.
Si vous êtes totalement novice dans Go, consultez cet article . ] afin de comprendre comment les projets sont organisés dans le répertoire GOPATH.
Lorsque GOPATH est défini, vous pouvez maintenant créer un répertoire pour votre projet:
mkdir -p $ GOPATH / src / github.com / {YOUR_GITHUB_USERNAME} / kudo-oos
Pour lancer rapidement votre spa, utilisez les fonctionnalités d'échafaudage de vue-cli . La CLI vous proposera une série d'options – choisissez la technologie appropriée pour ce projet: vue.js
vuex
et webpack
.
Installez vue-cli
en exécutant:
fil global add @ vue / cli
Créez ensuite un nouveau projet Vue:
mkdir -p pkg / http / web
cd pkg / http / web
vue créer une application
On vous posera une série de questions sur les détails de construction du projet. Pour cette application, choisissez tous les choix par défaut.
Félicitations, vous avez créé votre SPA Vue.js! Essayez-le en lançant:
cd app
fil installer
fil servir
Ouvrez cette URL: http: // localhost: 8080 dans votre navigateur et vous devriez voir le texte suivant:
Ensuite, faisons en sorte que votre SPA moderne et réactif utilisant vuetify
.
Ajouter Vuetify
Vuetify est une collection de composants de Vue.js qui résument les concepts de 1945 [Designs]. Vuetify fournit des fonctionnalités prêtes à l'emploi, notamment un système de grille, une typographie, une présentation de base, ainsi que des composants tels que des cartes, des boîtes de dialogue, des puces, des onglets, des icônes, etc. Vuetify vous ouvrira la voie à une interface utilisateur riche!
Lors de l'installation de Vuetify, vous serez invité à poser une série de questions. Par souci de simplicité, reprenez simplement les choix par défaut.
vue add vuetify
Réglez à nouveau votre SPA pour voir vuetify en action.
Ajoutez l'authentification à votre application Vue avec Okta
L'écriture d'authentification d'utilisateur sécurisée et la création de pages de connexion sont faciles à obtenir et peuvent entraîner la perte d'un nouveau projet. Okta simplifie la mise en œuvre rapide et sécurisée de toutes les fonctionnalités de gestion des utilisateurs. Commencez par ouvrir un compte gratuit pour développeur et créer une application OIDC à Okta.
Une fois connecté, créez une nouvelle application en en cliquant sur "Ajouter une application".
Sélectionnez l'option de plate-forme "Application à une page".
L'application par défaut les paramètres doivent être identiques à ceux illustrés.
Ensuite, installez le SDK Okta Vue en exécutant la commande suivante:
fil add @ okta / okta-vue
Créez vos routes Vue App
Pour cette application, vous n'avez besoin que de 4 routes, qui nécessitent toutes une authentification, à l'exception de la route de connexion.
La route racine /
est notre page de destination où le composant de connexion sera rendu. Une fois que l'utilisateur s'est authentifié, nous le redirigeons vers la route / me
où se trouve l'essentiel de la fonctionnalité: l'utilisateur doit pouvoir interroger les projets OSS via l'API REST de GitHub, les projets favoris retournés à partir de la requête , voir plus de détails sur le projet, et laissez une note décrivant l’importance du projet pour eux.
Prenez note que les / me
et repo /: id
ont une meta: {requireAuth: true}
propriété spécifiant que l'utilisateur doit être authentifié pour accéder à cette zone de l'application. Le plugin Okta l'utilisera pour rediriger l'utilisateur vers la page de connexion de Okta s'il n'est pas authentifié.
Créez d'abord pkg / http / web / app / src / routes.js
et définissez les itinéraires suivants:
importer Vue de 'vue';
importer VueRouter depuis 'vue-router';
importer Auth de '@ okta / okta-vue'
import Home de './components/Home';
importer le login depuis './components/Login';
importer GitHubRepoDetails à partir de './components/GithubRepoDetails';
Vue.use (VueRouter);
Vue.use (Auth, {
émetteur: {ADD_YOUR_DOMAIN},
client_id: {ADD_YOUR_CLIENT_ID},
redirect_uri: 'http: // localhost: 8080 / implicit / callback',
scope: 'email de profil openid'
})
exporter le nouveau VueRouter par défaut ({
mode: 'histoire',
itinéraires: [
{ path: '/', component: Login },
{ path: '/me', component: Home, meta: { requiresAuth: true }},
{ name: 'repo-details', path: '/repo/:id', component: GitHubRepoDetails, meta: { requiresAuth: true } },
{ path: '/implicit/callback', component: Auth.handleCallback() }
]
});
Assurez-vous d'ajouter les domaines
et client_id
à l'emplacement indiqué. Ces valeurs se trouvent sur la page de présentation de l'application dans Okta Developer Console. L'appel de Vue.use (Auth, ...)
injectera un objet authClient
dans votre instance Vue accessible en appelant this. $ Auth
n'importe où à l'intérieur votre instance de Vue. C’est ce que vous utiliserez pour vous assurer que l’utilisateur est connecté et / ou pour le forcer à s’identifier!
Création de composants Vue (19659005) La bibliothèque de vue-router
contient un certain nombre de composants pour aider les développeurs à créer des interfaces utilisateur riches et dynamiques. L'un d'eux, router-view, restitue le composant de la route correspondante. Dans notre cas, lorsque l'utilisateur accède à la route racine
/
de vue-router rend le composant
Login tel que configuré dans
routers.js ».
Ouvrir ./ kudo-oos / pkg / http / web / app / src / components / App.vue
et copier le code suivant:
Pour chaque itinéraire autre que le composant d'itinéraire correspondant, Vue rendra le composant Footer
. Créez ./ kudo-oos / pkg / http / web / app / src / components / Footer.vue
et copiez-le dans le code suivant pour créer ce composant de pied de page.
Votre page de destination devrait maintenant ressembler à ceci:
Avec notre composant de connexion affiché, l'utilisateur sera redirigé vers la page de connexion après avoir cliqué sur le bouton de connexion. [19659003]
Et après une connexion réussie, l'utilisateur est redirigé vers votre application vers la route configurée. Dans notre application, il s'agit de la route / me
.
La route / me
a été configurée pour rendre la page d'accueil
composant, qui à son tour rend la barre latérale
les Kudos et Search vuetify tabs
. Chaque onglet génère un ensemble spécifique d'éléments GitHubRepo
. Créez-vous le ./ kudo-oos / pkg / http / web / app / src / components / Home.vue
composant.
Le SearchBar
est le premier composant rendu dans Home
. Lorsque l'utilisateur entre une requête dans la saisie de texte de la Sidebar
le composant déclenche un appel à l'API Github. La barre de recherche
émet simplement un événement à son parent, Home
qui contient le githubQuery
.
./ kudo-oos / pkg / http / web / app / src / components / SearchBar.vue
devrait ressembler à ceci:
Merci à @ keyup.enter = "onSearchSubmition"
chaque fois que l'utilisateur clique dessus, entrez surSearchSubmition
émet la recherche soumise
avec la valeur de la requête. Comment pouvons-nous capturer cet événement, vous pouvez demander? Simple! Sur le composant Accueil, lorsque vous avez monté le composant Sidebar
vous avez également ajouté un "auditeur" v-on: search-submit = "githubQuery"
qui appelle githubQuery
. sur chaque recherche-soumise
soumise.
La barre latérale
est également responsable de la déconnexion de l'utilisateur. Okta Vue SDK offre une méthode pratique pour nettoyer la session en utilisant la méthode this. $ Auth.logout ()
. Chaque fois que l'utilisateur se déconnecte, il peut être redirigé vers la page de connexion.
Le deuxième composant rendu dans Home
est le GithupRepo
. Ce composant est utilisé dans deux onglets: le premier onglet Kudos
représente les projets OSS favoris de l’utilisateur et l’onglet Search
affiche les projets OSS renvoyés de GitHub.
Votre SPA utilise vuex
pour gérer l'état dans un magasin centralisé accessible à tous les composants. Vuex
garantit également que l'accès au magasin est effectué de manière prévisible en respectant quelques règles. Pour lire l'état, vous devez définir getters
les modifications synchrones de l'état doivent être effectuées via mutations
et les modifications asynchrones s'effectuent via les actions
. [19659003] Pour installer Vuex, lancez:
fil add vuex
Vous devez maintenant créer des actions ./ kudo-oos / pkg / http / web / app / src / store.js
avec
et Getters
. Vos données initiales sont {kudos: {}, repos: []}
. kudos
contient tous les projets OSS favoris de l’utilisateur en tant qu’objet JavaScript, la clé correspondant à l’ID du projet et la valeur au projet lui-même. repos
est un tableau contenant les résultats de la recherche.
Il existe deux cas dans lesquels vous pouvez avoir besoin de passer d'un état à un autre. Tout d’abord, lorsque l’utilisateur se connecte, vous devez récupérer ses projets OSS favoris sur le serveur Go et définir le repo
dans le magasin en appelant resetRepos
. Deuxièmement, lorsque l’utilisateur favorise ou désapprouve un projet OSS, vous devez mettre à jour le kudos
dans le magasin en appelant resetKudos
pour refléter ce changement sur le serveur.
reset.
est une méthode synchrone appelée par actions
dans les fonctions asynchrones après chaque appel au serveur Go.
Le composant Home
utilise les getters
allKudos.
et repos
pour rendre la liste des Kudos et SearchResults. Pour savoir si un repo
a été favorisé ou non, votre application doit appeler le getter isKudo
.
Créez votre ./ kudo-oos / pkg / http / web / app / src / store.js
avec le code ci-dessous:
import Vue Vue de 'vue';
importer Vuex depuis 'vuex';
importer APIClient à partir de './apiClient';
Vue.use (Vuex);
const store = new Vuex.Store ({
Etat: {
bravo: {},
repos: [],
},
mutations: {
resetRepos (état, repos) {
state.repos = repos;
},
resetKudos (état, félicitations) {
state.kudos = kudos;
}
},
getters: {
allKudos (état) {
return Object.values (state.kudos);
},
kudos (état) {
retourne l'état.kudos;
},
repos (état) {
return state.repos;
},
isKudo (état) {
return (repo) => {
return !! state.kudos [repo.id];
};
}
},
actes: {
getKudos ({commit}) {
APIClient.getKudos (). Then ((data) => {
commit ('resetKudos', data.reduce ((acc, kudo) => {
retour {[kudo.id]: bravo, ... acc}
}, {}))
})
},
updateKudo ({commit, state}, repo) {
const kudos = {... state.kudos, [repo.id]: repo};
retourne APIClient
.updateKudo (repo)
.then (() => {
commit ('resetKudos', bravo)
});
},
toggleKudo ({commit, state}, repo) {
si (! state.kudos [repo.id]) {
retourne APIClient
.createKudo (repo)
.then (kudo => commit ('resetKudos', {[kudo.id]: kudo, ... state.kudos}))
}
const kudos = Object.entries (state.kudos) .reduce ((acc, [repoId, kudo]) => {
return (repoId == repo.id)? acc
: {[repoId]: kudo, ... acc};
}, {});
retourne APIClient
.deleteKudo (repo)
.then (() => commit ('resetKudos', félicitations));
}
}
});
exportez le magasin par défaut;
À l'intérieur de actions
vous effectuez des appels ajax sur le serveur Go. Chaque demande faite au serveur doit être authentifiée ou le serveur répondra avec une erreur du client. Lorsque l'utilisateur se connecte, un jeton d'accès est créé et est accessible en appelant: wait Vue.prototype. $ Auth.getAccessToken ()
. Cette fonction asynchrone renvoie un jeton d'accès requis pour envoyer des requêtes authentifiées au serveur.
Le serveur Go expose une API REST pour la ressource kudo
. Vous allez implémenter des méthodes pour effectuer des appels ajax afin de créer avec createKudo
mettre à jour avec updateKudo
supprimer avec deleteKudo
et répertorier tous les kudos avec getKudos
. Notez que ces méthodes appellent la méthode exécute la méthode
en passant le noeud final et le verbe HTTP. performer
à son tour, renseigne l'en-tête de demande Authorization
avec le jeton d'accès afin que le serveur Go puisse valider la demande.
Créez votre ./ kudo-oos / pkg /http/web/app/src/apiClient.js
avec le code ci-dessous.
import Vue Vue de 'vue';
importer axios à partir d'axios;
const BASE_URI = 'http: // localhost: 4444';
const client = axios.create ({
baseURL: BASE_URI,
json: true
});
const APIClient = {
createKudo (repo) {
renvoie this.perform ('post', '/ kudos', repo);
},
deleteKudo (repo) {
return this.perform ('delete', `/ kudos / $ {repo.id}`);
},
updateKudo (repo) {
return this.perform ('put', `/ kudos / $ {repo.id}`, repo);
},
getKudos () {
return this.perform ('get', '/ kudos');
},
getKudo (repo) {
return this.perform ('get', `/ kudo / $ {repo.id}`);
},
async perform (méthode, ressource, données) {
laisser accessToken = attendre Vue.prototype. $ auth.getAccessToken ()
client de retour ({
méthode,
url: ressource,
Les données,
en-têtes: {
Autorisation: `Bearer $ {accessToken}`
}
}). then (req => {
retourne req.data
})
}
}
exportation par défaut APIClient;
Chaque GithubRepo
possède un routeur-link
à / repo /: id
qui rend le composant GithubRepoDetails
. GithubRepoDetails
fournit des détails sur le projet OSS, notamment le nombre de fois où le projet a été étoilé et le nombre de problèmes en suspens. L'utilisateur peut également laisser une note décrivant les raisons pour lesquelles le projet est spécial en cliquant sur le bouton Kudo. Le message est envoyé au bouton de serveur Go en appelant updateKudo
.
Créez votre ./ kudo-oos / pkg / http / web / app / src / components / GithubRepoDetails.js
avec le code ci-dessous.
Maintenant que votre routeur, votre magasin et vos composants sont en place, modifiez ./ kudo-oos / pkg / http / web / app / src / main.js
pour initialiser correctement votre SPA.
import '@ babel / polyfill'
importer Vue de 'vue'
importer './plugins/vuetify'
importer l'application depuis './App.vue'
importer magasin de './store'
importer le routeur de './routes'
Vue.config.productionTip = process.env.NODE_ENV == 'production';
router.beforeEach (Vue.prototype. $ auth.authRedirectGuard ())
nouveau Vue ({
le magasin,
routeur,
rendre: h => h (App)
}). $ mount ('# app')
Notez que nous appelons router.beforeEach (Vue.prototype. $ Auth.authRedirectGuard ())
pour rechercher des itinéraires étiquetés avec meta: {requireAuth: true}
et une redirection l'utilisateur vers le flux d'authentification s'il n'est pas connecté.
Créer une API REST avec Go
Maintenant que les utilisateurs peuvent s'authentifier en toute sécurité sur le serveur, vous devez créer un serveur HTTP écrit dans Go pour gérer les demandes, valider si l'utilisateur est authentifié et effectuer des opérations CRUD.
J'aime utiliser l'outil dep pour gérer les dépendances, assurez-vous donc de l'installer à partir d'ici avant de continuer. [19659021] dep init
dep assure -add github.com/okta/okta-jwt-verifier-golang
dep assure -add github.com/rs/cors
dep assure -add github.com/globalsign/mgo
Vous avez maintenant besoin d'une structure pour représenter un référentiel GitHub. Commencez par créer ./ kudo-oos / pkg / core / kudo.go
et définissez la structure suivante pour représenter un «kudo» (quelqu'un donnant des félicitations à un rapport spécifique).
// Kudo représente un oos kudo.
tapez Kudo struct {
ID utilisateur chaîne `json:" user_id "bson:" userId "`
Chaîne RepoID `json:" id "bson:" repoId "`
RepoName chaîne `json:" nom complet "bson:" repoName "`
Chaîne RepoURL `json:" html_url "bson:" repoUrl "`
Chaîne de langue `json:" language "bson:" language "`
Chaîne de description `json:" description "bson:" description "`
Notes chaîne de caractères `json:" notes "bson:" notes "`
}
Créez ensuite le fichier ./kudo-oos / pkg / core / repository.go
et ajoutez l'interface suivante pour représenter une API pour toute couche de persistance que vous souhaitez utiliser. Dans cet article, nous allons utiliser MongoDB.
// Le référentiel définit l'API qu'une implémentation de référentiel doit suivre.
type interface de référentiel {
Find (id string) (* Kudo, error)
FindAll (interface de sélecteur map [string] {}) ([] * Kudo, error)
Erreur Delete (kudo * Kudo)
Erreur de mise à jour (kudo * Kudo)
Erreur Create (kudo ... * Kudo)
Count () (int, erreur)
}
Enfin, créez le référentiel MongoDB qui implémente l’interface que vous venez de créer. Créez ./ kudo-oos / pkg / storage / mongo.go
et ajoutez le code suivant.
importation (
"bûche"
"os"
"github.com/globalsign/mgo"
"github.com/globalsign/mgo/bson"
"github.com/{YOUR_GITHUB_USERNAMEBuch/kudo-oos/pkg/core"
)
const (
collectionName = "kudos"
)
func GetCollectionName () string {
retour collectionName
}
tapez MongoRepository struct {
logger * log.Logger
session * mgo.Session
}
// Find récupère un kudo de mongo en fonction des critères de requête fournis.
func (r MongoRepository) Find (chaîne de repoID) (* core.Kudo, error) {
session: = r.session.Copy ()
reporter la session.Fermer ()
coll: = session.DB (""). C (collectionName)
var kudo core.Kudo
err: = coll.Find (bson.M {"repoId": repoID, "userId": kudo.UserID}). Un (& kudo)
si err! = nil {
r.logger.Printf ("erreur:% v n", erreur)
retourne zéro, err
}
retour et kudo, nil
}
// FindAll récupère les félicitations de la base de données.
func (r MongoRepository) FindAll (interface de sélecteur carte [string] {}) ([] * core.Kudo, error) {
session: = r.session.Copy ()
reporter la session.Fermer ()
coll: = session.DB (""). C (collectionName)
var kudos [] * core.Kudo
err: = coll.Find (sélecteur) .All (& kudos)
si err! = nil {
r.logger.Printf ("erreur:% v n", erreur)
retourne zéro, err
}
rendre kudos, nil
}
// Supprimer supprime un kudo de mongo en fonction des critères de requête fournis.
func (r MongoRepository) Supprimer l'erreur (kudo * core.Kudo) {
session: = r.session.Copy ()
reporter la session.Fermer ()
coll: = session.DB (""). C (collectionName)
renvoyer coll.Remove (bson.M {"repoId": kudo.RepoID, "userId": kudo.UserID})
}
// Mise à jour met à jour un kudo.
func (r MongoRepository) Erreur de mise à jour (kudo * core.Kudo) {
session: = r.session.Copy ()
reporter la session.Fermer ()
coll: = session.DB (""). C (collectionName)
return coll.Update (bson.M {"repoId": kudo.RepoID, "userId": kudo.UserID}, kudo)
}
// Créer des félicitations dans la base de données.
func (r MongoRepository) Crée une erreur (kudos ... * core.Kudo) {
session: = r.session.Copy ()
reporter la session.Fermer ()
coll: = session.DB (""). C (collectionName)
pour _, kudo: = range kudos {
_, err: = coll.Upsert (bson.M {"repoId": kudo.RepoID, "userId": kudo.UserID}, kudo)
si err! = nil {
retourne err
}
}
retourne zéro
}
// Count count documents pour une collection donnée
func (r MongoRepository) Count () (int, erreur) {
session: = r.session.Copy ()
reporter la session.Fermer ()
coll: = session.DB (""). C (collectionName)
retourne coll.Count ()
}
// NewMongoSession compose le nom de mongodb et crée une session.
func newMongoSession () (* mgo.Session, error) {
mongoURL: = os.Getenv ("MONGO_URL")
si mongoURL == "" {
log.Fatal ("MONGO_URL non fourni")
}
retour mgo.Dial (mongoURL)
}
func newMongoRepositoryLogger () * log.Logger {
return log.New (os.Stdout, "[mongoDB]", 0)
}
func NewMongoRepository () core.Repository {
logger: = newMongoRepositoryLogger ()
session, err: = newMongoSession ()
si err! = nil {
logger.Fatalf ("Impossible de se connecter à la base de données:% v n", erreur)
}
retourne MongoRepository {
session: session,
enregistreur: enregistreur,
}
}
Ajoutez le backend
Avant de pouvoir créer des gestionnaires HTTP, vous devez écrire du code pour gérer les charges utiles des demandes entrantes.
Create ./ kudo-oos / pkg / kudo / service.go
et insérer le code ci-dessous.
package kudo
importation (
"strconv"
"github.com/{YOUR_GITHUB_USERNAMEBuch/kudo-oos/pkg/core"
)
tapez GitHubRepo struct {
RepoID int64 `json:" id "`
Chaîne RepoURL `json:" html_url "`
RepoName chaîne `json:" nom complet "`
Chaîne de langue `json:" language "`
Chaîne de description `json:" description "`
Notes chaîne `json:" notes "`
}
tapez Service struct {
chaîne utilisateur
repo core.Repository
}
func (s Service) GetKudos () ([] * core.Kudo, error) {
retourne s.repo.FindAll (map [string] interface {} {"userId": s.userId})
}
func (service) CreateKudoFor (githubRepo GitHubRepo) (* core.Kudo, erreur) {
kudo: = s.githubRepoToKudo (githubRepo)
err: = s.repo.Create (kudo)
si err! = nil {
retourne zéro, err
}
retourne kudo, nil
}
func (service) UpdateKudoWith (githubRepo GitHubRepo) (* core.Kudo, erreur) {
kudo: = s.githubRepoToKudo (githubRepo)
err: = s.repo.Create (kudo)
si err! = nil {
retourne zéro, err
}
retourne kudo, nil
}
func (service) RemoveKudo (githubRepo GitHubRepo) (* core.Kudo, erreur) {
kudo: = s.githubRepoToKudo (githubRepo)
err: = s.repo.Delete (kudo)
si err! = nil {
retourne zéro, err
}
retourne kudo, nil
}
func (Service Service) githubRepoToKudo (githubRepo GitHubRepo) * core.Kudo {
return & core.Kudo {
ID utilisateur: s.userId,
RepoID: strconv.Itoa (int (githubRepo.RepoID)),
RepoName: githubRepo.RepoName,
RepoURL: githubRepo.RepoURL,
Langue: githubRepo.Language,
Description: githubRepo.Description,
Notes: githubRepo.Notes,
}
}
func NewService (noyau du référentiel, référentiel userId) Service {
service de retour {
repo: repo,
userId: userId,
}
}
Définissez les gestionnaires HTTP Go
Votre API REST expose la ressource kudo
destinée à prendre en charge des clients tels que votre SPA. Un SPA normal exposera les ordinateurs d'extrémité afin que les clients puissent créer, mettre à jour, supprimer et répertorier des ressources. Par exemple, lorsque l'utilisateur se connecte, une requête est demandée pour extraire tous les félicitations de l'utilisateur authentifié via GET /kudos
.[19659135vre# Récupère tous les projets open source favorisés par l'utilisateur
GET / kudos
# Recherche un projet open source favori par id
GET / kudos /: id
# Crée (ou favoris) un projet open source pour l'utilisateur connecté
POST / kudos
# Met à jour un projet open source favori
PUT / kudos /: id
# Supprime (ou défavorise) un projet open source favori
DELETE / kudos /: id
Pour supporter cela, vous devez ajouter un nouveau fichier nommé ./ kudo-oos / pkg / http / handlers.go
et définir vos gestionnaires HTTP à l'aide de la fabuleuse httprouter library .
paquet http
importation (
"encodage / json"
"io / ioutil"
"net / http"
"strconv"
"github.com/julienschmidt/httprouter"
"github.com/{YOUR_GITHUB_USERNAMEBuch/kudo-oos/pkg/core"
"github.com/{YOUR_GITHUB_USERNAMEBuch/kudo-oos/pkg/kudo"
)
tapez Service struct {
repo core.Repository
Routeur http.Handler
}
Nouveau service func (repo core.Repository) {
service: = service {
repo: repo,
}
routeur: = httprouter.New ()
router.GET ("/ kudos", service.Index)
router.POST ("/ kudos", service.Create)
router.DELETE ("/ kudos /: id", service.Delete)
router.PUT ("/ kudos /: id", service.Update)
service.Router = UseMiddlewares (routeur)
service de retour
}
func (s Service) Index (w http.ResponseWriter, r * http.Request, paramètres httprouter.Params) {
service: = kudo.NewService (s.repo, r.Context (). Value ("userId"). (string))
félicitations, err: = service.GetKudos ()
si err! = nil {
w.WriteHeader (http.StatusInternalServerError)
revenir
}
w.WriteHeader (http.StatusOK)
json.NewEncoder (w) .Encode (félicitations)
}
func (s Service) Create (w http.ResponseWriter, r * http.Request, paramètres httprouter.Params) {
service: = kudo.NewService (s.repo, r.Context (). Value ("userId"). (string))
charge utile, _: = ioutil.ReadAll (r.Body)
githubRepo: = kudo.GitHubRepo {}
json.Unmarshal (charge utile, & githubRepo)
kudo, err: = service.CreateKudoFor (githubRepo)
si err! = nil {
w.WriteHeader (http.StatusInternalServerError)
revenir
}
w.WriteHeader (http.StatusCreated)
json.NewEncoder (w) .Encode (kudo)
}
func (s Service) Supprimer (w http.ResponseWriter, r * http.Request, paramètres httprouter.Params) {
service: = kudo.NewService (s.repo, r.Context (). Value ("userId"). (string))
repoID, _: = strconv.Atoi (params.ByName ("id"))
githubRepo: = kudo.GitHubRepo {RepoID: int64 (repoID)}
_, err: = service.RemoveKudo (githubRepo)
si err! = nil {
w.WriteHeader (http.StatusInternalServerError)
revenir
}
w.WriteHeader (http.StatusOK)
}
func (s Service Update) (avec http.ResponseWriter, r * http.Request, paramètres httprouter.Params) {
service: = kudo.NewService (s.repo, r.Context (). Value ("userId"). (string))
charge utile, _: = ioutil.ReadAll (r.Body)
githubRepo: = kudo.GitHubRepo {}
json.Unmarshal (charge utile, & githubRepo)
kudo, err: = service.UpdateKudoWith (githubRepo)
si err! = nil {
w.WriteHeader (http.StatusInternalServerError)
revenir
}
w.WriteHeader (http.StatusOK)
json.NewEncoder (w) .Encode (kudo)
}
Vérifier les jetons Web JSON (JWT) avec Go
Il s'agit du composant le plus crucial de votre serveur d'API REST. Sans ce middleware, tout utilisateur peut effectuer des opérations CRUD sur la base de données.
Si aucun JWT valide n'est fourni dans l'en-tête d'autorisation HTTP, l'appel d'API est abandonné et une erreur est renvoyée au client.
Create ./ kudo-oos / pkg / http / middlewares.go
et coller le code suivant: package
http
importation (
"le contexte"
"bûche"
"net / http"
"cordes"
jwtverifier "github.com/okta/okta-jwt-verifier-golang"
"github.com/rs/cors"
)
func OktaAuth(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
accessToken := r.Header["Authorization"]
jwt, err := validateAccessToken(accessToken)
if err != nil {
w.WriteHeader(http.StatusForbidden)
w.Write([]byte(err.Error()))
revenir
}
ctx := context.WithValue(r.Context(), "userId", jwt.Claims["sub"].(string))
h.ServeHTTP(w, r.WithContext(ctx))
})
}
func validateAccessToken(accessToken []string) (*jwtverifier.Jwt, error) {
parts := strings.Split(accessToken[0]" ")
jwtVerifierSetup := jwtverifier.JwtVerifier{
Issuer: "{DOMAIN}",
ClaimsToValidate: map[string]string{"aud": "api://default", "cid": "{CLIENT_ID}"},
}
verifier := jwtVerifierSetup.New()
return verifier.VerifyIdToken(parts[1])
}
func JSONApi(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
h.ServeHTTP(w, r)
})
}
func AccsessLog(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s: %s", r.Method, r.RequestURI)
h.ServeHTTP(w, r)
})
}
func Cors(h http.Handler) http.Handler {
corsConfig := cors.New(cors.Options{
AllowedHeaders: []string{"Origin", "Accept", "Content-Type", "X-Requested-With", "Authorization"},
AllowedMethods: []string{"POST", "PUT", "GET", "PATCH", "OPTIONS", "HEAD", "DELETE"},
Debug: true,
})
return corsConfig.Handler(h)
}
func UseMiddlewares(h http.Handler) http.Handler {
h = JSONApi(h)
h = OktaAuth(h)
h = Cors(h)
return AccsessLog(h)
}
As you can see, the middleware OktaAuth
uses okta-jwt-verifier-golang to validate the user’s access token.
Define Your Go REST API Entry Point
Open up ./kudo-oos/pkg/cmd/main.go
and add the following code to spin up your Go webserver.
package main
import (
"log"
"net/http"
"os"
web "github.com/{YOUR_GITHUB_USERNAME}/kudo-oos/pkg/http"
"github.com/{YOUR_GITHUB_USERNAME}/kudo-oos/pkg/storage"
)
func main() {
httpPort := os.Getenv("PORT")
repo := storage.NewMongoRepository()
webService := web.New(repo)
log.Printf("Running on port %sn", httpPort)
log.Fatal(http.ListenAndServe(httpPort, webService.Router))
}
Run the Go +Vue SPA
There are many ways to run backend and frontend apps. The simplest way (for development purposes) is to just use good old fashioned Make.
A Makefile contains build instructions for your website. It’s like an old-school version of gulp
grunt
and the more hip Node tools. To get started, create a file named Makefile
in the root of your project folder and copy in the following code.
setup: run_services
@go run ./cmd/db/setup.go
run_services:
@docker-compose up --build -d
run_server:
@MONGO_URL=mongodb://mongo_user:mongo_secret@0.0.0.0:27017/kudos PORT=:4444 go run cmd/main.go
run_client:
@/bin/bash -c "cd $$GOPATH/src/github.com/klebervirgilio/kudo-oos/pkg/http/web/app && yarn serve"
Create a Dockerfile
Next, you’ll want to create a Dockerfile. This file tells Docker how to run your application and spares you the effort of deploying a real MongoDB instance for testing purposes.
All you need to do here is create a file named docker-compose.yml
and copy in the following code.
version: '3'
services:
mongo:
image: mongo
restart: always
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: mongo_user
MONGO_INITDB_ROOT_PASSWORD: mongo_secret
Your app is now ready to test! Run the following commands to get going.
make setup
make run_server
make run_client
Your Go webserver should be listening on 0.0.0.0:4444
and your SPA should be serving files from http://localhost:8080
. Visit http://localhost:8080
to play around with your new app!
Learn More About Go and Vue
Vue.js is a powerful and straightforward framework with phenomenal adoption and community growth. In this tutorial, you learned to build a fully-functional, secure SPA with Vue and Go.
To learn more about Vue.js, head over to https://vuejs.org or check out these other great resources from the @oktadev team:
If you have any questions, please let us know in the comments or follow and tweet us @oktadev.
Source link