Dies ist Qiitas erster Beitrag. Es ist ein halbes Jahr her, seit ich ohne Erfahrung in der IT-Branche den Job gewechselt habe. Deshalb habe ich eine WEB-Anwendung erstellt, um die bisherigen Ergebnisse des Go-Sprachenlernens auszugeben. In diesem Artikel werde ich diese WEB-Anwendung vorstellen. (Da ich ein Anfänger bin, lassen Sie es mich bitte in den Kommentaren wissen, wenn Sie Fehler haben: bow_tone1 :)
** URL der WEB-Anwendung ** https://workbook-292312.an.r.appspot.com/
** WEB-Anwendungs-Repository ** https://github.com/Gompei/WorkBookApp
-** Erstellen ** Basierend auf den aus dem Formular erhaltenen Kontoinformationen werden die Daten mit dem im Datenspeicher ausgegebenen Primärschlüssel registriert. In Anbetracht der Funktion zur erneuten Ausgabe von Passwörtern haben wir außerdem implementiert, dass die E-Mail-Adresse beim Anmelden mit einem externen Konto nicht registriert wird. Auf diese Weise ist die E-Mail-Adresse im Datenspeicher immer eindeutig.
handler.go
/*CreateAccount ist eine Funktion, die eine Benutzerstruktur erstellt und im Datenspeicher speichert (automatisch für den Primärschlüssel generiert).*/
func (a *App) CreateAccount(w http.ResponseWriter, r *http.Request) {
password := r.FormValue(password)
user := UserAccount{
Name: r.FormValue(userName),
OauthFlg: false,
Mail: r.FormValue(email),
ProfileImg: Environment.ImgUrl + "NoImage.jpg ",
CreateTimeStamp: time.Now(),
UpdateTimeStamp: time.Now(),
}
(Kürzung)
//Passwort ist verschlüsselt (Hashwert)
hash, err := HashFiled(password)
if err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, nil)
a.ReadTemplate(w, PageAccountCreate, showAccountCreate, SendDate)
return
}
user.HashPassword = hash
//Benutzer-ID auf der Datenspeicherseite ausgeben ・ Daten mit dem Primärschlüssel registrieren
err = a.DB.InsertUserAccount(user)
if err != nil || SendDate["message"] != "" {
a.WriteLog(ErrDBProcessing, err)
a.ReadTemplate(w, PageAccountCreate, showAccountCreate, SendDate)
return
}
}
(Kürzung)
-** Einloggen ** Das aus dem Formular erhaltene Kennwort und der Hashwert werden mit dem Paket bcrypto verglichen.
hadler.go
/*ValidateLoginData ist eine Funktion, die eine Entität aus dem Datenspeicher unter Verwendung der E-Mail-Adresse als Primärschlüssel abruft und prüft, ob sie mit dem Kennwort übereinstimmt.*/
func (a *App) ValidateLoginData(w http.ResponseWriter, r *http.Request) {
user := UserAccount{
Mail: r.FormValue(email),
}
password := r.FormValue(password)
(Kürzung)
//Die E-Mail-Adresse ist in einem eindeutigen Zustand registriert
err, user := a.DB.CheckUserLogin(user, password)
if err != nil {
a.ReadTemplate(w, PageLogin, showLogin, SendDate)
return
}
//Erfassen Sie eine Sitzung und speichern Sie die aus der Datenbank erfassten Benutzerinformationen im Sitzungsspeicher.
session, err := GetSession(r)
if err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, nil)
a.ReadTemplate(w, PageLogin, showLogin, SendDate)
return
}
CreateSession(w, r, session, user)
(Kürzung)
}
handler.go
/*UpdateAccount ist eine Funktion, die die Daten im Datenspeicher basierend auf den eingegebenen Benutzerinformationen aktualisiert.*/
func (a *App) UpdateAccount(w http.ResponseWriter, r *http.Request) {
user := UserAccount{
Name: r.FormValue(userName),
Mail: r.FormValue(email),
UpdateTimeStamp: time.Now(),
}
if !NameValidate(user.Name) || !MailValidate(user.Mail) {
(Kürzung)
}
if r.FormValue(password) != "" || PasswordValidate(password) {
(Kürzung)
}
id := SendDate["user"].(map[string]interface{})["id"].(int64)
err, tmp := a.DB.UpdateUserAccount(id, user)
if err != nil {
a.WriteLog(ErrDBProcessing, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
}
session, err := GetSession(r)
if err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
CreateSession(w, r, session, tmp)
(Kürzung)
}
handler.go
/*DeleteAccount ist eine Funktion, mit der das im Datenspeicher registrierte Benutzerkonto gelöscht wird.*/
func (a *App) DeleteAccount(w http.ResponseWriter, r *http.Request) {
id := SendDate["user"].(map[string]interface{})["id"].(int64)
err := a.DB.DeleteUserAccount(id)
if err != nil {
a.WriteLog(ErrDBProcessing, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
}
(Kürzung)
}
handler.go
/*ExternalAuthenticationFaceBook ist eine Funktion zum Erwerben eines Facebook-Kontos, zum Registrieren in der Datenbank und zum Anmelden.*/
func (a *App) ExternalAuthenticationFaceBook(w http.ResponseWriter, r *http.Request) {
user, err := a.Facebook.FetchFacebookAccount(r)
if err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, nil)
a.ReadTemplate(w, PageLogin, showLogin, SendDate)
return
}
user, err = a.CheckDBOauthAccount("Facebook", user)
if err != nil {
a.WriteLog(ErrDBProcessing, err)
SetMessage(errorMessage, nil)
a.ReadTemplate(w, PageLogin, showLogin, SendDate)
return
}
(Kürzung)
}
auth.go
/*FetchHttpClient ist eine Funktion, die die für die externe Authentifizierung erforderlichen Clientinformationen zurückgibt.*/
func FetchHttpClient(conf *oauth2.Config, r *http.Request) (*http.Client, error) {
code := r.URL.Query()["code"]
if code == nil {
err := errors.New("Externer Authentifizierungsfehler")
return nil, err
}
ctx := context.Background()
tok, err := conf.Exchange(ctx, code[0])
if err != nil {
return nil, err
}
return conf.Client(ctx, tok), nil
}
/*Jeder func Fetch~Ist eine Funktion, die die Benutzerkontoinformationen jeder Anwendung von der URL abruft, sie in eine Struktur auffüllt und zurückgibt.*/
func (f *FacebookClient) FetchFacebookAccount(r *http.Request) (UserAccount, error) {
client, err := FetchHttpClient(f.Conf, r)
if err != nil {
return UserAccount{}, err
}
(Kürzung)
res, err := session.Get("/me?fields=id,name,email,picture", nil)
if err != nil {
return UserAccount{}, err
}
//In den Typ ini64 umwandeln, damit er im Datenspeicher registriert werden kann
id := res["id"]
userId, err := strconv.ParseInt(id.(string), 10, 64)
if err != nil {
return UserAccount{}, err
}
(Kürzung)
user := UserAccount{
FacebookId: userId,
OauthFlg: true,
Name: res["name"].(string),
ProfileImg: pictureUrl,
CreateTimeStamp: time.Now(),
UpdateTimeStamp: time.Now(),
}
return user, nil
}
handler.go
/*SendReissueEmail ist eine Funktion, die eine E-Mail zur erneuten Ausgabe eines Passworts an eine bereits registrierte E-Mail-Adresse sendet.*/
func (a *App) SendReissueEmail(w http.ResponseWriter, r *http.Request) {
searchMail := r.FormValue(email)
(Kürzung)
//Konto basierend auf E-Mail-Adresse zurückgeben
id, err := a.DB.SelectUserAccountMail(searchMail)
if err != nil {
(Kürzung)
}
//Senden Sie eine E-Mail an die E-Mail-Adresse des gesuchten Benutzers
err = gmailSend(searchMail, id)
if err != nil {
(Kürzung)
}
(Kürzung)
}
func (a *App) ShowRecoverPasswordPage(w http.ResponseWriter, r *http.Request) {
/*Inhalt des Tokens
1 Benutzer-ID
2 Erstellungszeitstempel
3 Ablaufdatum
*/
tokenString := r.URL.Query().Get("token")
if tokenString == "" {
SetMessage(errorTokenMessage, nil)
a.ReadTemplate(w, PageAccountCreate, showAccountCreate, SendDate)
return
}
claims := jwt.MapClaims{}
_, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(Environment.JwtSing), nil
})
if err != nil {
(Kürzung)
}
//Geben Sie in der Sitzung keine Benutzer-ID ein
SendDate["id"] = claims["id"]
(Kürzung)
}
mail.go
/*body ist eine Funktion, die den Inhalt einer E-Mail erstellt*/
func (m mail) body() string {
return "To: " + m.to + "\r\n" +
"Subject: " + m.sub + "\r\n\r\n" +
m.msg + "\r\n"
}
/*gmailSend ist eine Funktion, die Google Mail-Mail an die Argument-Mail-Adresse sendet*/
func gmailSend(send string, id int64) error {
token, err := CreateToken(id)
if err != nil {
return err
}
m := mail{
from: Environment.MailAddress,
username: Environment.MailName,
password: Environment.MailPassWord,
to: send,
sub: "Workbook |Passwort zurücksetzen",
}
m.msg = "So schließen Sie das Anmeldekennwort für die Arbeitsmappe zurück" + "\r\n" +
"Bitte greifen Sie auf die unten stehende URL zu und legen Sie ein neues Passwort fest." + "\r\n" +
"https://workbook-292312.an.r.appspot.com/login/recover-password/page?token=" + token + "\r\n" +
"* Die URL zum Zurücksetzen des Passworts ist eine Stunde nach ihrer Ausgabe gültig. Bitte beachten Sie, dass Sie nach dem Ablaufdatum nicht mehr von der oben genannten URL zurücksetzen können." + "\r\n" +
"―――――――――――――――――――――――――――――――――――" + "\r\n" +
"Gehen Sie für diese Nachricht zur Arbeitsmappe, um das Anmeldekennwort zurückzusetzen." + "\r\n" +
"Es wurde an den Kunden gesendet, der es hergestellt hat." + "\r\n" +
"Wenn Sie es nicht wissen, hat sich jemand anderes mit der falschen E-Mail-Adresse registriert." + "\r\n" +
"Es gibt eine Möglichkeit." + "\r\n" +
"In diesem Fall tut es mir leid, Sie zu belästigen." + Environment.MailAddress + "Bitte kontaktieren Sie uns." + "\r\n" +
"―――――――――――――――――――――――――――――――――――"
smtpSvr := "smtp.gmail.com:587"
auth := smtp.PlainAuth("", m.username, m.password, "smtp.gmail.com")
if err := smtp.SendMail(smtpSvr, auth, m.from, []string{m.to}, []byte(m.body())); err != nil {
return err
}
return nil
}
auth.go
/*CreateToken ist eine Funktion, die eine JWT mit einer Benutzer-ID für die Neuausgabe des Kennworts ausgibt.*/
func CreateToken(id int64) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["id"] = strconv.FormatInt(id, 10)
claims["iat"] = time.Now()
claims["exp"] = time.Now().Add(time.Hour * 1).Unix()
tokenString, err := token.SignedString([]byte(Environment.JwtSing))
return tokenString, err
}
-** Erstellen ** Sie können bis zu 20 Fragen erstellen. Bei Fragen werden die Daten in Schichten der Struktur gespeichert. Wenn Sie gleichzeitig Bilddaten aus dem Formular abrufen, müssen Sie die ParseMultipartForm-Methode verwenden.
entity.go
//Problemsammlung (Benutzer-ID+Titel+Fragenanordnung)
type WorkbookContent struct {
UserId int64
BookId int64
ShareId int64
Author string
Category string
Title string
Image string
Contents []Content
CreateTimeStamp time.Time
UpdateTimeStamp time.Time
}
//Frage
type Content struct {
ProblemNumber string
ProblemStatement string
Choice1 string
Choice2 string
Choice3 string
Choice4 string
Answer string
Explanation string
}
handler.go
/*CreateWorkBook ist eine Funktion, die die aus dem Formular erhaltenen Datensammlungsinformationen basierend auf der bookId im Datenspeicher registriert.*/
func (a *App) CreateWorkBook(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(32 << 20)
if err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
mf := r.MultipartForm.Value
if len(mf) == 0 {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
var UpFileName string
file, fileHeader, err := r.FormFile(image)
if err != nil {
a.WriteLog(ErrLog, err)
UpFileName = "NoImage.jpg "
} else {
UpFileName = fileHeader.Filename
}
workbook := WorkbookContent{
UserId: SendDate["user"].(map[string]interface{})["id"].(int64),
Author: SendDate["user"].(map[string]interface{})["name"].(string),
Image: Environment.ImgUrl + UpFileName,
CreateTimeStamp: time.Now(),
UpdateTimeStamp: time.Now(),
}
workbook.Title = mf["title"][0]
workbook.Category = mf["category"][0]
(Kürzung)
workbook.Contents = make([]Content, 0)
for i := 1; i <= total; i++ {
s := strconv.Itoa(i)
content := Content{
ProblemNumber: mf["problem"+s][0],
ProblemStatement: mf["statement"+s][0],
Choice1: mf["choices"+s+"-1"][0],
Choice2: mf["choices"+s+"-2"][0],
Choice3: mf["choices"+s+"-3"][0],
Choice4: mf["choices"+s+"-4"][0],
Answer: mf["answer"+s][0],
Explanation: mf["commentary"+s][0],
}
workbook.Contents = append(workbook.Contents, content)
}
(Kürzung)
err = a.DB.InsertWorkbook(workbook)
if err != nil {
a.WriteLog(ErrDBProcessing, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
(Kürzung)
}
handler.go
/*DeleteWorkBook ist eine Funktion, die die Daten löscht, die der aus dem Formular erhaltenen Buch-ID entsprechen.*/
func (a *App) DeleteWorkBook(w http.ResponseWriter, r *http.Request) {
id := r.FormValue(bookId)
if id == "" {
SetMessage("Der Löschvorgang konnte nicht ausgeführt werden.", "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
bookId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
userId := SendDate["user"].(map[string]interface{})["id"].(int64)
err = a.DB.DeleteWorkBook(bookId, userId)
if err != nil {
a.WriteLog(ErrSTProcessing, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
(Kürzung)
}
-** Lernen ** Für den Lernbildschirm wird die Anzeige- / Nichtanzeigeverarbeitung in JavaScript ohne Seitenübergang für jede Frage implementiert.
handler.go
/*CheckAnswerWorkBook ist eine Funktion, die basierend auf dem aus dem Formular erhaltenen Wert antwortet und das Ergebnis mit der Karte erstellt.*/
func (a *App) CheckAnswerWorkBook(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
questionTotal := r.FormValue("question-total")
total, err := strconv.Atoi(questionTotal)
if err != nil {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
//Extrahieren Sie nur die Problemnummer(Solange Sie den aktivierten Wert kennen, können Sie antworten.)
//Go1.12+Verwenden Sie den Mechanismus, nach dem die Kartenschlüssel automatisch sortiert werden
answer := make(map[string]string)
choice := make(map[string]string)
for k, v := range r.Form {
if v[0] == "on" {
checked := strings.Replace(k, "check", "", 1)[0:1]
answer[checked] = k
choice[checked] = k
}
}
//Wenn der Benutzer zwangsweise einreicht, ohne auszuwählen
if total != len(choice) {
a.WriteLog(ErrLog, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
for i := 1; i <= total; i++ {
s := strconv.Itoa(i)
//Entfernen Sie den Wert aus der Karte, wenn er falsch ist
if answer[s][7:] != r.FormValue("Answer"+s) {
delete(answer, s)
}
}
bookId := r.FormValue(bookId)
err, book := a.DB.SelectWorkbook(bookId)
if err != nil {
a.WriteLog(ErrDBProcessing, err)
SetMessage(errorMessage, "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
kind := r.FormValue("kind")
SendDate["learningBook"] = book
SendDate["checkBook"] = answer
SendDate["choiceBook"] = choice
SendDate["kind"] = kind
(Kürzung)
}
handler.go
/*Das Hochladen von WorkBook ist eine Freigabe im Datenspeicher_Eine Sammlung von Problemen aus dem Formular im Buch (Art)(Nach der Suche basierend auf bookId)Funktion zum Registrieren*/
func (a *App) UploadWorkBook(w http.ResponseWriter, r *http.Request) {
bookId := r.FormValue(bookId)
if bookId == "" {
SetMessage("Das Teilen konnte nicht durchgeführt werden.", "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
err := a.DB.InsertShareWorkbook(bookId)
if err != nil {
a.WriteLog(ErrSTProcessing, err)
SetMessage("Bereits hochgeladen", "block")
a.ReadTemplate(w, PageHome, showHome, SendDate)
return
}
(Kürzung)
}
database.go
/*InsertShareWorkbook teilt die Informationen, die basierend auf der bookID erhalten wurden._Funktion zur Registrierung im Buch Art*/
func (c *Client) InsertShareWorkbook(bookId string) error {
ctx := context.Background()
err, book := c.SelectWorkbook(bookId)
if err != nil {
return err
}
var workbook api.WorkbookContent
query := datastore.NewQuery("share_workbook").Filter("BookId =", book.BookId)
it := c.DataStore.Run(ctx, query)
_, err = it.Next(&workbook)
if err == nil {
return errors.New("Es wurde bereits hochgeladen.")
}
parentKey := datastore.IDKey("user_account", book.UserId, nil)
childKey := datastore.IncompleteKey("share_workbook", parentKey)
childKey.ID = book.BookId
_, err = c.DataStore.Put(ctx, childKey, &book)
if err != nil {
return err
}
return nil
}
workbook_share.html
<div class="article-cta workbook_learning_start">
{{if checkOwner $book.UserId}}
<a href="#" class="btn btn-danger" data-toggle="modal"data-target="#modal-danger">Löschen</a>
{{end}}
<a href="#" class="btn btn-primary" data-toggle="modal" data-target="#modal-default">Start</a>
<input type="hidden" class="book-id" name="" value="{{$book.BookId}}">
</div>
data.go
FuncMap = template.FuncMap{
//Wird vom Autor verwendet, um eine löschbare Schaltfläche auf der freigegebenen Seite anzuzeigen
"checkOwner": func(checkId interface{}) bool {
userId := SendDate["user"].(map[string]interface{})["id"].(int64)
checkUserId := checkId.(int64)
if checkUserId == userId {
return true
}
return false
},
}
(1)Go Da Java ursprünglich erstellt wurde, habe ich Go als nächste zu lernende Sprache ausgewählt, da es eine ähnliche Entwicklung ermöglicht. Zu Beginn der Entwicklung habe ich nicht verstanden, wie die Struktur / Schnittstelle verwendet wird, daher habe ich sie wie folgt implementiert, aber ich glaube, ich konnte am Ende der Entwicklung Go-ähnlichen Code schreiben.
database.go(Initiale)
//NewClient ist eine Funktion, die einen Datenspeicher-Client erstellt
func NewClient(ctx context.Context) (*datastore.Client, bool) {
var client *datastore.Client
client, err := datastore.NewClient(ctx, project_id)
if err != nil {
return nil, false
}
return client, true
}
//CheckUserLogin ist eine Funktion, die E-Mail-Adressen und Kennwörter vergleicht und boolesche Informationen und Benutzerkontoinformationen zurückgibt.
func CheckUserLogin(user UserAccount, password string) (bool, UserAccount) {
ctx := context.Background()
client, flg := NewClient(ctx)
if flg == false {
return false, user
}
defer client.Close()
(Kürzung)
}
datbase.go(Aktuell)
/*NewClient ist eine Funktion, die einen Datenspeicher-Client erstellt*/
func NewClient(ctx context.Context) (*Client, error) {
client, err := datastore.NewClient(ctx, api.Environment.ProjectId)
if err != nil {
return nil, err
}
return &Client{
DataStore: client,
}, nil
}
/*CheckUserLogin ist eine Funktion, die basierend auf der E-Mail-Adresse abfragt und das Benutzerkonto zurückgibt, wenn Fehler und Kennwort übereinstimmen.*/
func (c *Client) CheckUserLogin(user api.UserAccount, password string) (error, api.UserAccount) {
ctx := context.Background()
(Kürzung)
}
DB, ST, LD sind Schnittstellen, die die für Datenspeicher und Cloud-Speicher erforderlichen Methoden implementieren. Außerdem handelt es sich bei den Inhalten von handler.go hauptsächlich um App-Methoden (Strukturmethoden).
handler.go(Aktuell)
/*NewApp ist DB,Storage,Logging,Eine Funktion, die externe Kontoverbindungsinformationen zu einer kombiniert*/
func NewApp(d Repository, s Storage, l Logging, google GoogleClient, facebook FacebookClient, github GithubClient) *App {
return &App{
DB: d,
ST: s,
LD: l,
Google: google,
Facebook: facebook,
Github: github,
}
}
main.go(Aktuell)
/*run ist eine Funktion, die jeden Client erstellt und den Server basierend auf der registrierten Handlerfunktion startet.*/
func run() error {
ctx := context.Background()
d, err := database.NewClient(ctx)
if err != nil {
return err
}
s, err := api.NewStorageClient(ctx)
if err != nil {
return err
}
l, err := api.NewLoggingClient(ctx)
if err != nil {
return err
}
google := api.NewGoogleClient()
facebook := api.NewFacebookClient()
github := api.NewGithubClient()
app := api.NewApp(d, s, l, google, facebook, github)
router := api.Route(app)
(Kürzung)
}
entity.go(Aktuell)
//DB-Repository
type Repository interface {
CheckUserLogin(user UserAccount, password string) (error, UserAccount)
InsertUserAccount(user UserAccount) error
(Kürzung)
}
//Speicher- und Protokollierungs-Repository
type Client struct {
CloudStorage *storage.Client
Logging *logging.Client
}
//Verbindungsinformationen protokollieren
type Logging interface {
WriteStackDriverLog(appErr *ApplicationError)
}
//Informationen zur Speicherverbindung
type Storage interface {
UploadImg(file multipart.File, fileHeader *multipart.FileHeader) error
}
//Bewerbungs informationen
type App struct {
DB Repository
ST Storage
LD Logging
Google GoogleClient
Facebook FacebookClient
Github GithubClient
}
(2)Datastore Dies liegt daran, dass wir den von GCP bereitgestellten Speicheroptionsfluss befolgt haben. Ich wollte auch NoSQL ausprobieren. (Derzeit ist dieses Bild nicht auf der offiziellen Website ... richtig?) Bei der tatsächlichen Verwendung gibt es einige Unannehmlichkeiten, z. B. wird keine partielle Übereinstimmungssuche (LIKE) bereitgestellt, aber ich bin der Meinung, dass der Datenspeicher für die Entwicklung, bei der nur einfache Datenstrukturen verwendet werden, bequemer ist als SQL. .. (Als nächstes werde ich Cloud SQL wählen ...)
Wenn Sie mehr über die von GCP bereitgestellte Datenbank erfahren möchten, klicken Sie bitte hier. ↓ Google Cloud-Datenbank
(3)CloudStorage Dies folgt auch den Best Practices von GCP. Sie können problemlos Dateien hochladen und es ist sehr praktisch, daher möchte ich es auch in Zukunft weiter verwenden.
(4)GoogleAppEngine Dies liegt auch daran, dass wir die Best Practices von GCP befolgen. (Der Grund für die Wahl von GCP ist, dass ich Google ʕ◔ϖ◔ʔ mag. Leser sollten sorgfältig überlegen und eine Cloud-Umgebung wählen.) Cloud-Funktionen können nicht die gesamte Anwendung bereitstellen, daher stelle ich sie für GAE SE bereit. GAE bietet die Möglichkeit, Anforderungen nach Version aufzuteilen und HTTPS kostenlos zu verwenden. (Es gibt mehr Funktionen!)
Die Auswahlkriterien für die Anwendungsausführungsumgebung finden Sie hier ↓ Hosten von Apps in Google Cloud Was soll ich verwenden, GCE oder GAE? GCP-Operationsdiagnose für diejenigen, die sagen GCP --GAE, GCE, GKE - Entscheidungsbaum
Da es mit GCP entwickelt wurde, wird das Hauptprodukt CloudBuild sein. .Circleci / config.yml wurde erstellt, damit es mit CircleCI getestet und bereitgestellt werden kann. Dieses Mal habe ich mich für CloudBuild entschieden, was das Erstellen von Konfigurationsdateien vereinfacht, aber ich möchte CircleCI in Zukunft verwenden. Ich empfehle diesen Artikel auch, weil er die Vorteile von CloudBuild auf leicht verständliche Weise erklärt. ↓ Wenn Sie GCP verwenden, warum nicht Cloud Build?
(6)Docker Ich entwickle unter Windows, aber ich habe es containerisiert, damit es unter anderen als Windows ausgeführt werden kann. Es ist sehr praktisch, die App unabhängig von der Umgebung mit nur Dockerfile und docker-compose.yml ausführen zu können. (Ich möchte, dass Sie es auf der aktuellen Website einbinden ...)
Nutzung von Strukturen und Schnittstellen Ich habe dies in der Technologieauswahl erwähnt, daher werde ich es nicht im Detail schreiben, aber ich glaube, ich konnte Go-ähnlichen Code schreiben.
Aufbau einer CI / CD-Pipeline Was ist CI / CD? Da es aus einer solchen Situation heraus entwickelt wurde, dauerte es einige Zeit, es zu erstellen, aber ich konnte es automatisieren. Der Testcode ist noch lange nicht vollständig, aber die Vorteile der automatisierten Bereitstellung waren enorm. Als nächstes möchte ich mehr Testcode schreiben und entwickeln, damit ich CI / CD optimal nutzen kann. (Ich möchte, dass Sie es auf der aktuellen Website einbinden ...)
Was sind Go, GCP, Docker, CI / CD? Es war sehr gut, dass ich es von Anfang an im Staat geschafft habe und die Erfahrung hatte, etwas zu machen. Um die Grundkenntnisse des Programmierens zu verbessern, möchte ich mich in Zukunft der Herausforderung stellen, Algorithmen zu lernen und ein Niveau zu entwickeln, das tatsächlich als Dienstleistung bereitgestellt werden kann.
[Go / Development Environment Construction] [Verwenden Sie Goland Winter 2019](https://qiita.com/junpayment/items/f66e85af6a854ca2a296#go%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9% E3% 83% 88% E3% 83% BC% E3% 83% AB% E3% 81% 97% E3% 81% BE% E3% 81% 97% E3% 82% 87% E3% 81% 86)
[Go / Grammatik] [Los] Grundlegende Grammatik ① (Grundlegend)
[Go / Package Management] Go scheint eine Standardverzeichnisstruktur zu haben. [Go go lang] Importiere dein eigenes Paket
[Go / Session Management] Beispiel für eine Benutzeranmeldefunktion mit Gorilla / Mux, Gorilla / Kontext, Gorilla / Sitzungen in der Sprache Go
[Go / Externe Kontoauthentifizierung] Bis Sie Ihre E-Mail-Adresse von Oauth erhalten, der Google, Twitter und Facebook in der Sprache Go authentifiziert
[Go / JWT] Praktische Implementierung der JWT-Authentifizierung in Go-Sprache
【Datastore】 Dokumentation zum Firestore im Datenspeichermodus Hanashi gibt sein Bestes mit Cloud Datastore-Abfragen
【CloudStorage】 Abrufen von Dateien aus Google Cloud Storage in der Sprache Go (golang) Hochladen von Dateien in Google Cloud Storage in der Sprache Go (golang)
【GAE】 Erstellen Sie Go-Apps mit App Engine (https://cloud.google.com/appengine/docs/standard/go/building-app?hl=ja).
【Cloud Build】 Wenn Sie GCP verwenden, warum nicht Cloud Build?
【Docker】 Erstellen einer Go-Entwicklungsumgebung mit Docker Remote-Debuggen der Go-Webanwendung auf Docker
[Referenz zum Schreiben eines Qiita-Artikels] [Python + Flask] Einfache Buchverwaltungsanwendung mit Web-API [Vue.js, Elasticsearch]
【Bonus】 Ein Ort, an dem Anfänger der Go-Sprache glücklich sein können, wenn sie sie sehen #golang
Recommended Posts