英文:
Go GAE app works locally, when deployed I get 404/nothing
问题
我正在处理一个 PartyCon 项目的服务器端部分。它使用 Golang 在 Google App Engine 平台上编写。我刚刚实现了一些在本地完美运行的新功能。然而,当部署后,我无法访问 console.go 脚本。
这是我的 app.yaml 配置(抱歉,这是 stackoverflow 显示 yaml 文件的方式):
application: party-serverside
version: alpha-1
runtime: go
api_version: go1
handlers:
#handlers for api
- url: /api/.*
script: api/api.go
#handlers for console and webpage routing
- url: /redirect
script: redirecter/redirecter.go
- url: /admin_console/choose
script: admin_console/choose.go
- url: /post-request
script: webpage/post-request.go
- url: /console
script: console/console.go
#handlers for static files
- url: /css/console.css
static_files: console/page/css/console.css
upload: console/page/css/console.css
- url: /console/page
static_dir: console/page
- url: /
static_files: webpage/index.html
upload: webpage/index.html
- url: /
static_dir: webpage
- url: /css
static_dir: webpage/css
- url: /js
static_dir: webpage/js
- url: /img
static_dir: webpage/img
- url: /fonts
static_dir: webpage/fonts
这是我的 console.go 文件:
package console
import (
"appengine"
"appengine/user"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"html/template"
"io/ioutil"
"net/http"
"strconv"
"time"
)
//for deployment
var dbConnectString string = "****************************"
//for local testing
//var dbConnectString string = "root@/party"
func init() {
http.HandleFunc("/console", consoleHandler)
}
func consoleHandler(w http.ResponseWriter, r *http.Request) {
redirectIfNeeded(w, r)
c := appengine.NewContext(r)
u := user.Current(c)
logoutUrl, e := user.LogoutURL(c, "/redirect")
if e != nil {
panic(e)
}
email := u.Email
data := WebpageData{LogoutUrl: logoutUrl, UserName: email, NewPartyUrl: "/console/newparty"}
template := template.Must(template.New("template").Parse(generateUnsignedHtml(u)))
err := template.Execute(w, data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func generateUnsignedHtml(u *user.User) string {
firstPart := fileValue("./console/page/firstPart.html")
table := generateTable(u)
secondPart := fileValue("./console/page/secondPart.html")
html := firstPart + table + secondPart
return html
}
func generateTable(u *user.User) string {
con, e := sql.Open("mysql", dbConnectString)
if e != nil {
panic(e)
}
defer con.Close()
var parties []Party
partyRows, err := con.Query("select id, name, datetime, host, location from parties where author='" + u.Email + "';")
if err != nil {
panic(err)
}
var id int
var name string
var datetime string
var host string
var location string
for partyRows.Next() {
partyRows.Scan(&id, &name, &datetime, &host, &location)
parties = append(parties, Party{Id: id, Name: name, DatetimeString: datetime, Host: host, Location: location})
}
html := ""
for i, party := range parties {
actionsHtml := "<a href=\"/console/edit?id=" + strconv.Itoa(party.Id) + "\" class=\"uk-button uk-button-primary editButton\">Edit</a> <a href=\"/console/delete?id=" + strconv.Itoa(party.Id) + "\" class=\"uk-button uk-button-danger\">Delete</a>"
html += "<tr>" + makeTd(strconv.Itoa(i+1)) + makeTd(party.Name) + makeTd(party.DatetimeString) + makeTd(party.Host) + makeTd(party.Location) + makeTd(actionsHtml) + "</tr>"
}
html += "</table>"
return html
}
func makeTd(content string) string {
return "<td>" + content + "</td>"
}
func redirectIfNeeded(w http.ResponseWriter, r *http.Request) {
expire := time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC)
cookie := &http.Cookie{Name: "ACSID", Value: "", Expires: expire, HttpOnly: true}
http.SetCookie(w, cookie)
cookie2 := &http.Cookie{Name: "SACSID", Value: "", Expires: expire, HttpOnly: true}
http.SetCookie(w, cookie2)
c := appengine.NewContext(r)
u := user.Current(c)
if u == nil {
url, err := user.LoginURL(c, r.URL.String())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Location", url)
w.WriteHeader(http.StatusFound)
return
}
con, e := sql.Open("mysql", dbConnectString)
if e != nil {
panic(e)
}
defer con.Close()
//check whether user is admin
admRows, error := con.Query("select email from admin_users;")
if error != nil {
panic(error)
}
var email string
isAdmin := false
for admRows.Next() {
admRows.Scan(&email)
if email == u.Email {
isAdmin = true
}
}
//check if he is validated user
validRows, error2 := con.Query("select email from party_validated_users;")
if error2 != nil {
panic(error2)
}
email = ""
isValidated := false
for validRows.Next() {
validRows.Scan(&email)
if email == u.Email {
isValidated = true
}
}
var url string
if user.IsAdmin(c) || isAdmin {
//user is declared as admin in db or is admin of gae app
//we are allready here
url = "/console"
} else if isValidated {
//user is validated
//we are allready here
url = "/console"
} else {
//user is not validated yet
url = "/redirect"
w.Header().Set("Location", url)
w.WriteHeader(http.StatusFound)
}
}
func fileValue(path string) string {
content, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
return string(content)
}
type WebpageData struct {
LogoutUrl string
UserName string
NewPartyUrl string
}
type Party struct {
Id int
Name string
DatetimeString string
Host string
Location string
}
有什么想法为什么会发生这种情况吗?提前感谢你的帮助!
英文:
I'm working on a serverside part of a PartyCon project. It is written in Golang on Google App Engine platform. I have just implemented a few new things which work perfectly LOCALLY. However, when deployed, I cannot rich console.go script.
Here is my app.yaml configuration (sorry, this is how stackoverflow shows yaml files):
application: party-serverside version: alpha-1 runtime: go
api_version: go1
handlers:
#handlers for api
- url: /api/.*
script: api/api.go
#handlers for console and webpage routing
- url: /redirect
script: redirecter/redirecter.go
- url: /admin_console/choose
script: admin_console/choose.go
- url: /post-request
script: webpage/post-request.go
- url: /console
script: console/console.go
#handlers for static files
- url: /css/console.css
static_files: console/page/css/console.css upload: console/page/css/console.css
- url: /console/page
static_dir: console/page
- url: /
static_files: webpage/index.html
upload: webpage/index.html
- url: /
static_dir: webpage
- url: /css
static_dir: webpage/css
- url: /js
static_dir: webpage/js
- url: /img
static_dir: webpage/img
- url: /fonts
static_dir: webpage/fonts
And my console.go file:
package console
import (
"appengine"
"appengine/user"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"html/template"
"io/ioutil"
"net/http"
"strconv"
"time"
)
//for deployment
var dbConnectString string = "****************************"
//for local testing
//var dbConnectString string = "root@/party"
func init() {
http.HandleFunc("/console", consoleHandler)
}
func consoleHandler(w http.ResponseWriter, r *http.Request) {
redirectIfNeeded(w, r)
c := appengine.NewContext(r)
u := user.Current(c)
logoutUrl, e := user.LogoutURL(c, "/redirect")
if e != nil {
panic(e)
}
email := u.Email
data := WebpageData{LogoutUrl: logoutUrl, UserName: email, NewPartyUrl: "/console/newparty"}
template := template.Must(template.New("template").Parse(generateUnsignedHtml(u)))
err := template.Execute(w, data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func generateUnsignedHtml(u *user.User) string {
firstPart := fileValue("./console/page/firstPart.html")
table := generateTable(u)
secondPart := fileValue("./console/page/secondPart.html")
html := firstPart + table + secondPart
return html
}
func generateTable(u *user.User) string {
con, e := sql.Open("mysql", dbConnectString)
if e != nil {
panic(e)
}
defer con.Close()
var parties []Party
partyRows, err := con.Query("select id, name, datetime, host, location from parties where author='" + u.Email + "';")
if err != nil {
panic(err)
}
var id int
var name string
var datetime string
var host string
var location string
for partyRows.Next() {
partyRows.Scan(&id, &name, &datetime, &host, &location)
parties = append(parties, Party{Id: id, Name: name, DatetimeString: datetime, Host: host, Location: location})
}
html := ""
for i, party := range parties {
actionsHtml := "<a href=\"/console/edit?id=" + strconv.Itoa(party.Id) + "\" class=\"uk-button uk-button-primary editButton\">Edit</a> <a href=\"/console/delete?id=" + strconv.Itoa(party.Id) + "\" class=\"uk-button uk-button-danger\">Delete</a>"
html += "<tr>" + makeTd(strconv.Itoa(i+1)) + makeTd(party.Name) + makeTd(party.DatetimeString) + makeTd(party.Host) + makeTd(party.Location) + makeTd(actionsHtml) + "</tr>"
}
html += "</table>"
return html
}
func makeTd(content string) string {
return "<td>" + content + "</td>"
}
func redirectIfNeeded(w http.ResponseWriter, r *http.Request) {
expire := time.Date(2000, 1, 1, 1, 1, 1, 0, time.UTC)
cookie := &http.Cookie{Name: "ACSID", Value: "", Expires: expire, HttpOnly: true}
http.SetCookie(w, cookie)
cookie2 := &http.Cookie{Name: "SACSID", Value: "", Expires: expire, HttpOnly: true}
http.SetCookie(w, cookie2)
c := appengine.NewContext(r)
u := user.Current(c)
if u == nil {
url, err := user.LoginURL(c, r.URL.String())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Location", url)
w.WriteHeader(http.StatusFound)
return
}
con, e := sql.Open("mysql", dbConnectString)
if e != nil {
panic(e)
}
defer con.Close()
//check whether user is admin
admRows, error := con.Query("select email from admin_users;")
if error != nil {
panic(error)
}
var email string
isAdmin := false
for admRows.Next() {
admRows.Scan(&email)
if email == u.Email {
isAdmin = true
}
}
//check if he is validated user
validRows, error2 := con.Query("select email from party_validated_users;")
if error2 != nil {
panic(error2)
}
email = ""
isValidated := false
for validRows.Next() {
validRows.Scan(&email)
if email == u.Email {
isValidated = true
}
}
var url string
if user.IsAdmin(c) || isAdmin {
//user is declared as admin in db or is admin of gae app
//we are allready here
url = "/console"
} else if isValidated {
//user is validated
//we are allready here
url = "/console"
} else {
//user is not validated yet
url = "/redirect"
w.Header().Set("Location", url)
w.WriteHeader(http.StatusFound)
}
}
func fileValue(path string) string {
content, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
return string(content)
}
type WebpageData struct {
LogoutUrl string
UserName string
NewPartyUrl string
}
type Party struct {
Id int
Name string
DatetimeString string
Host string
Location string
}
Any idea why this happens? Thanks in advance
答案1
得分: 2
对于Go应用程序,请将脚本处理程序设置为"_go_app"。例如:
handlers:
url: /api/.*
script: _go_app
AppEngine将所有针对Go应用程序的请求分派给一个单独的编译可执行文件。这与Python不同,Python可以为每个处理程序指定不同的脚本。
英文:
For Go applications, set the script handler to "_go_app". For example:
handlers:
url: /api/.*
script: _go_app
AppEngine dispatches all requests for Go applications to a single compiled executable. This is different from Python where you can specify a different script for each handler.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论