我的Go项目遇到问题,其中一条路线可以很好地处理CSS,而另一条路线的CSS则损坏了。 CSS曾经在两个页面上都可以使用,但是现在不为/login.html加载。
我知道我正确地删除了/ static /文件夹的前缀,因为它在一个地方而不是另一个地方工作。我还直接将标题代码从工作页面复制并粘贴到不工作的页面(请小心使用正确的CSS文件)。
Negroni显示该应用程序正在正确的位置进行呼叫:
999.3µs | localhost:8080 | GET /static/css/splash.css
正确运行的html文件index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Pando</title>
<link rel="stylesheet" href="/static/css/index.css" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Bitter|Nunito:400,700" rel="stylesheet">
</head>
<body>
<div id="sidebar">
<p id="logo"><img src="/static/img/logo.svg" height="14px">Pando</p>
<span id="all-files" class="selected">All Files</span>
<p id="shared-collections">Shared Collections<img src="/static/img/gear.svg" /></p>
<div class="collections">
<span>Collection 1</span>
<span>Collection 2</span>
</div>
<p id="my-collections">My Collections<img src="/static/img/gear.svg" /></p>
<div class="collections">
<span>Collection 1</span>
<span>Collection 2</span>
</div>
</div>
<div id="header">
<input type="button" id="upload-launch-button" value="Upload" onclick="showUploadDialog()"></button>
<form id="search">
<input type="search" placeholder="Search..">
<input type="button"><img src="/static/img/search.svg"></button>
</form>
<div id="user">
<img src="/static/img/user.svg">{{.User}}<a href="/logout">(Log out)</a>
</div>
</div>
<!-- <span id="filter">Latest Files</span> -->
<div id="results">
{{range .Files}}
<div class="img-container">
<img src="/files/{{.Name}}" id="file-{{.PK}}">
<div class="hover-actions">
<a href="/files/{{.Name}}" download><img src="/static/img/download.svg"></a>
<img src="/static/img/edit.svg">
<img src="/static/img/delete.svg" onclick="deleteFile('/files/{{.Name}}', {{.PK}})">
</div>
</div>
{{end}}
</div>
<div class="dialog" id="upload-dialog">
<div class="dialog-name">Upload</div>
<form id="upload" enctype="multipart/form-data" action="/upload" method="post">
<input type="file" id="selectedFile" name="file" /> <!--multiple later display none-->
<input id="upload-button" type="submit" value="Upload" onclick="hideUploadDialog()" />
</form>
</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script type="text/javascript" src="/static/js/script.js"></script>
</body>
</html>
Login.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Pando</title>
<link rel="stylesheet" href="/static/css/splash.css" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Bitter|Nunito:400,700" rel="stylesheet">
</head>
<body>
<section class="section-a">
<div id="logo"><img src="/static/img/logo.svg">Pando</div>
<p id="welcome">Join Pando.</p>
<div id="buttoncont">
<a href="/static/html/index.html"><span id="enter" class="button">Enter</span></a>
</div>
</section>
<section class="section-b">
<form id="login-form">
<div>
<label>Email</label><input type="email" name="username" required>
</div>
<div>
<label>Password</label><input type="password" name="password" required>
</div>
<div>
<input type="submit" value="Register" name="register">
<input type="submit" value="Log In" name="login">
</div>
<div id="error">{{.Error}}</div>
</form>
</section>
</body>
</html>
完整的go文件:
package main
import (
"database/sql"
"fmt"
"html/template"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"time"
"net/http"
"gopkg.in/gorp.v1"
_ "github.com/go-sql-driver/mysql"
"encoding/json"
"golang.org/x/crypto/bcrypt"
sessions "github.com/goincremental/negroni-sessions"
"github.com/goincremental/negroni-sessions/cookiestore"
gmux "github.com/gorilla/mux"
"github.com/urfave/negroni"
)
/*File struct
PK primary key
Name is the original name of the file; new file location at /files/{name}
File Type is the extension of the file; valid file types are image formats, PDF, .AI, .PSD, and MS Word docs
Upload Date is a static value indicating when the file was uploaded
Last Modified records if any changes are made to the file while it's on the server
User is the uploading user
Eventually I will probably want to refactor this so that I can allow for files with the same name to coexist. Not sure how to do that right now elegantly.
*/
type File struct {
PK int64 `db:"pk"`
Name string `db:"name"`
FileType string `db:"type"`
UploadDate string `db:"uploadtime"`
LastModified string `db:"modtime"`
User string `db:"user"`
}
// Tag struct
type Tag struct {
PK int64 `db:"pk"`
FilePK int64 `db:"filepk"`
Name string `db:"name"`
}
// Collection struct
type Collection struct {
PK int64 `db:"pk"`
Name string `db:"name"`
ContentName string `db:"contentname"`
ContentType string `db:"type"`
}
// User struct
type User struct {
Username string `db:"username"`
Secret []byte `db:"secret"`
}
// Page struct
type Page struct {
Files []File
Filter string
User string
}
// LoginPage struct
type LoginPage struct {
Error string
}
// UploadPage struct
type UploadPage struct {
Error string
}
var db *sql.DB
var dbmap *gorp.DbMap
func main() {
initDb()
index := template.Must(template.ParseFiles("html/index.html"))
login := template.Must(template.ParseFiles("html/login.html"))
upload := template.Must(template.ParseFiles("html/upload.html"))
mux := gmux.NewRouter()
defer db.Close()
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
mux.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
mux.PathPrefix("/img/").Handler(http.StripPrefix("/img/", http.FileServer(http.Dir("img"))))
mux.PathPrefix("/files/").Handler(http.StripPrefix("/files/", http.FileServer(http.Dir("files"))))
// Login
mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
var p LoginPage
if r.FormValue("register") != "" {
secret, _ := bcrypt.GenerateFromPassword([]byte(r.FormValue("password")), bcrypt.DefaultCost)
user := User{r.FormValue("username"), secret}
if err := dbmap.Insert(&user); err != nil {
p.Error = err.Error()
} else {
sessions.GetSession(r).Set("User", user.Username)
http.Redirect(w, r, "/", http.StatusFound)
return
}
} else if r.FormValue("login") != "" {
user, err := dbmap.Get(User{}, r.FormValue("username"))
if err != nil {
p.Error = err.Error()
} else if user == nil {
p.Error = "No user account exists for the username " + r.FormValue("username")
} else {
u := user.(*User)
if err = bcrypt.CompareHashAndPassword(u.Secret, []byte(r.FormValue("password"))); err != nil {
p.Error = err.Error()
} else {
sessions.GetSession(r).Set("User", u.Username)
http.Redirect(w, r, "/", http.StatusFound)
return
}
}
}
if err := login.ExecuteTemplate(w, "login.html", p); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
// Upload
mux.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
var p UploadPage
// Checks filesize against max upload size (10MB)
if err := r.ParseMultipartForm(10 << 20); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// reads file
fileType := r.PostFormValue("type")
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer file.Close()
fileBytes, err := ioutil.ReadAll(file)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// checks the filetype against expected mime types
mimetype := http.DetectContentType(fileBytes)
if mimetype != "image/jpeg" && mimetype != "image/jpg" &&
mimetype != "image/gif" && mimetype != "image/png" &&
mimetype != "application/pdf" && mimetype != "image/vnd.adobe.photoshop" && mimetype != "application/illustrator" && mimetype != "image/vnd.microsoft.icon" &&
mimetype != "application/msword" && mimetype != "application/x-photoshop" && mimetype != "application/photoshop" && mimetype != "application/psd" {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
filename := header.Filename
newPath := filepath.Join("files/", filename)
fmt.Printf("FileType: %s, File: %s\n", fileType, newPath)
t := time.Now().String()
currentTime, _ := time.Parse(time.Stamp, t)
// Creates a File struct-type object out of the file information from
f := File{
PK: -1,
Name: filename,
FileType: fileType,
UploadDate: currentTime.String(),
LastModified: currentTime.String(),
User: getStringFromSession(r, "User"),
}
// Inserts the file information into the database
if err = dbmap.Insert(&f); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
newFile, err := os.Create(newPath)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer newFile.Close()
if _, err := newFile.Write(fileBytes); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// w.Write([]byte("SUCCESS"))
if err := upload.ExecuteTemplate(w, "upload.html", p); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}).Methods("POST")
// Sort
mux.HandleFunc("/files", func(w http.ResponseWriter, r *http.Request) {
var b []File
if !getFileCollection(&b, r.FormValue("sortBy"), getStringFromSession(r, "Filter"), getStringFromSession(r, "User"), w) {
return
}
sessions.GetSession(r).Set("sortBy", r.FormValue("sortBy"))
if err := json.NewEncoder(w).Encode(b); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}).Methods("GET").Queries("sortBy", "{sortBy:title|author|classification}")
// Default page
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
p := Page{Files: []File{}, Filter: getStringFromSession(r, "Filter"), User: getStringFromSession(r, "User")}
if !getFileCollection(&p.Files, getStringFromSession(r, "SortBy"), getStringFromSession(r, "Filter"), p.User, w) {
return
}
if err := index.ExecuteTemplate(w, "index.html", p); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}).Methods("GET")
mux.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) {
sessions.GetSession(r).Set("User", nil)
sessions.GetSession(r).Set("Filter", nil)
http.Redirect(w, r, "/login", http.StatusFound)
})
// Deletes file from database; currently not working :(
mux.HandleFunc("/files/{name}", func(w http.ResponseWriter, r *http.Request) {
pk, _ := strconv.ParseInt(gmux.Vars(r)["pk"], 10, 64)
fmt.Printf("pk is %d", pk)
var f File
if err := dbmap.SelectOne(&f, "select * from files where pk=?", pk); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
if _, err := dbmap.Delete(&f); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}).Methods("DELETE")
// Session management
n := negroni.Classic()
n.Use(sessions.Sessions("pando", cookiestore.New([]byte("hubert88"))))
n.Use(negroni.HandlerFunc(verifyDatabase))
n.Use(negroni.HandlerFunc(verifyUser))
n.UseHandler(mux)
n.Run(":8080")
} // end main
// Opens the database connection to SQL and creates tables if they don't exist
func initDb() {
db, _ = sql.Open("mysql", "root:secret@tcp(127.0.0.1:3306)/pando")
dbmap = &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
// creates tables, specifies the fields on the struct that map to table primary keys
dbmap.AddTableWithName(File{}, "files").SetKeys(true, "pk")
dbmap.AddTableWithName(Tag{}, "tags").SetKeys(true, "pk")
dbmap.AddTableWithName(Collection{}, "collections").SetKeys(true, "pk")
dbmap.AddTableWithName(User{}, "users").SetKeys(false, "username")
dbmap.CreateTablesIfNotExists()
}
// Checks to make sure the database is alive
func verifyDatabase(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
if err := db.Ping(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
next(w, r)
}
func getStringFromSession(r *http.Request, key string) string {
var strVal string
if val := sessions.GetSession(r).Get(key); val != nil {
strVal = val.(string)
}
return strVal
}
func verifyUser(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
if r.URL.Path == "/login" {
next(w, r)
return
}
if username := getStringFromSession(r, "User"); username != "" {
if user, _ := dbmap.Get(User{}, username); user != nil {
next(w, r)
return
}
}
http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
}
我正竭尽全力尝试对此进行调试,而我得到的所有搜索结果都只涉及剥离前缀(我已经在这样做了)。
在我的浏览器的“网络”选项卡中,CSS和图像文件返回307临时重定向错误。
运行整理器的curl命令给出以下输出:
HTTP/1.1 307 Temporary Redirect
Content-Type: text/html; charset=utf-8
Location: /login
Date: Sun, 05 May 2019 22:16:17 GMT
Content-Length: 42
这就是我处理静态文件的方式。
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
答案 0 :(得分:0)
正如评论所暗示的那样,另一个处理程序正在拦截您的静态文件路由。尝试简化您的路线。减少:
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
mux.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
mux.PathPrefix("/img/").Handler(http.StripPrefix("/img/", http.FileServer(http.Dir("img"))))
mux.PathPrefix("/files/").Handler(http.StripPrefix("/files/", http.FileServer(http.Dir("files"))))
到一条路线:
mux.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
并将所有静态文件移动到static
目录下-相应地更新所有html / css路径。