Fermer

octobre 26, 2018

Construire une application d'une page avec Go et Vue –


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.

 Utilisez la vue-cli pour initialiser un programme

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:

 Page default page

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

 Ajouter vueify

Réglez à nouveau votre SPA pour voir vuetify en action.


 Page Vuetify

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.

 Page d'inscription Okta

Une fois connecté, créez une nouvelle application en en cliquant sur "Ajouter une application".

 Okta ajouter une application

Sélectionnez l'option de plate-forme "Application à une page".

 Okta crée une application

L'application par défaut les paramètres doivent être identiques à ceux illustrés.

 Paramètres de l'application Okta

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:

 Vue de la page de destination

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]  Se connecter à Okta

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 .

 Vue app

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 gulpgruntand 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