企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持知识库和私有化部署方案 广告
![](https://raw.githubusercontent.com/kataras/iris/master/_examples/mvc/login/folder_structure.png) File: mvc/login/datamodels/user.go ~~~ package datamodels import ( "time" "golang.org/x/crypto/bcrypt" ) // User 是我们的用户示例模型. // K请注意标记public-use (for our web app) // 应该保存在其他文件中 "web/viewmodels/user.go" // 可以通过嵌入来包装 datamodels.User or // 定义完全新的字段,但摇动 // 例如,我们将使用此数据模型 // 作为我们应用程序中唯一一个用户模型. type User struct { ID int64 `json:"id" form:"id"` Firstname string `json:"firstname" form:"firstname"` Username string `json:"username" form:"username"` HashedPassword []byte `json:"-" form:"-"` CreatedAt time.Time `json:"created_at" form:"created_at"` } //IsValid可以进行一些非常简单的“低级”数据验证. func (u User) IsValid() bool { return u.ID > 0 } // GeneratePassword将根据我们为我们生成一个哈希密码 // 用户的输入. func GeneratePassword(userPassword string) ([]byte, error) { return bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost) } // ValidatePassword将检查密码是否匹配. func ValidatePassword(userPassword string, hashed []byte) (bool, error) { if err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil { return false, err } return true, nil } ~~~ File: mvc/login/datasource/users.go ~~~ // file: datasource/users.go package datasource import ( "errors" "github.com/kataras/iris/_examples/mvc/login/datamodels" ) // 引擎来自何处获取数据,在这种情况下是用户s. type Engine uint32 const ( //内存代表简单的内存位置; // map[int64] datamodels.User随时可以使用,这是我们在这个例子中的来源. Memory Engine = iota // 螺栓用于boltdb源位置. Bolt // MySQL for mysql-compatible 简体 来源地点. MySQL ) //LoadUsers从内存中返回所有用户(空映射),以简化操作. func LoadUsers(engine Engine) (map[int64]datamodels.User, error) { if engine != Memory { return nil, errors.New("为简单起见,我们使用简单的地图作为数据源") } return make(map[int64]datamodels.User), nil } ~~~ File: mvc/login/main.go ~~~ // file: main.go package main import ( "time" "github.com/kataras/iris/_examples/mvc/login/datasource" "github.com/kataras/iris/_examples/mvc/login/repositories" "github.com/kataras/iris/_examples/mvc/login/services" "github.com/kataras/iris/_examples/mvc/login/web/controllers" "github.com/kataras/iris/_examples/mvc/login/web/middleware" "github.com/kataras/iris" "github.com/kataras/iris/mvc" "github.com/kataras/iris/sessions" ) func main() { app := iris.New() // Y您获得了完整的调试消息,在使用MVC时非常有用 //确保您的代码与Iris的MVC架构保持一致. app.Logger().SetLevel("debug") // 加载模板文件. tmpl := iris.HTML("./web/views", ".html"). Layout("shared/layout.html"). Reload(true) app.RegisterView(tmpl) app.StaticWeb("/public", "./web/public") app.OnAnyErrorCode(func(ctx iris.Context) { ctx.ViewData("Message", ctx.Values(). GetStringDefault("message", "您要查找的页面不存在")) ctx.View("shared/error.html") }) // ---- 服务我们的控制器. ---- // 准备我们的存储库和服务. db, err := datasource.LoadUsers(datasource.Memory) if err != nil { app.Logger().Fatalf("error while loading the users: %v", err) return } repo := repositories.NewUserRepository(db) userService := services.NewUserService(repo) // "/users" 基于mvc的应用程序. users := mvc.New(app.Party("/users")) // 添加基本​​身份验证(admin:password)中间件 // 对于基于/users 的请求 users.Router.Use(middleware.BasicAuth) // “userService”绑定到UserController的Service(接口)字段. users.Register(userService) users.Handle(new(controllers.UsersController)) // "/user" 基于mvc的应用程序. sessManager := sessions.New(sessions.Config{ Cookie: "sessioncookiename", Expires: 24 * time.Hour, }) user := mvc.New(app.Party("/user")) user.Register( userService, sessManager.Start, ) user.Handle(new(controllers.UserController)) // http://localhost:8080/noexist // 和所有控制器的方法一样 // http://localhost:8080/users/1 // http://localhost:8080/user/register // http://localhost:8080/user/login // http://localhost:8080/user/me // http://localhost:8080/user/logout // 基本认证: "admin", "password", see "./middleware/basicauth.go" 源文件. app.Run( // 启动Web服务器 localhost:8080 iris.Addr("localhost:8080"), // 禁用更新程序. iris.WithoutVersionChecker, // 忽略封闭服务器错误日志当Ctrl键/ 1400 + 100按压. iris.WithoutServerError(iris.ErrServerClosed), // 实现更快的json序列化等 iris.WithOptimizations, ) } ~~~ File: mvc/login/repositories/user_repository.go ~~~ package repositories import ( "errors" "sync" "github.com/kataras/iris/_examples/mvc/login/datamodels" ) //Query表示访问者和操作查询. type Query func(datamodels.User) bool // UserRepository处理用户的基本操作entity/model. //它是一个可测试的接口,即内存用户存储库或 //连接到sql数据库。. type UserRepository interface { Exec(query Query, action Query, limit int, mode int) (ok bool) Select(query Query) (user datamodels.User, found bool) SelectMany(query Query, limit int) (results []datamodels.User) InsertOrUpdate(user datamodels.User) (updatedUser datamodels.User, err error) Delete(query Query, limit int) (deleted bool) } // NewUserRepository返回一个新的基于用户内存的存储库, //我们示例中唯一的存储库类型. func NewUserRepository(source map[int64]datamodels.User) UserRepository { return &userMemoryRepository{source: source} } // userMemoryRepository是一个“UserRepository” //使用内存数据源管理用户(地图). type userMemoryRepository struct { source map[int64]datamodels.User mu sync.RWMutex } const ( //ReadOnlyMode将RLock(读取)数据 . ReadOnlyMode = iota // ReadWriteMode将锁定(读/写)数据. ReadWriteMode ) func (r *userMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) { loops := 0 if mode == ReadOnlyMode { r.mu.RLock() defer r.mu.RUnlock() } else { r.mu.Lock() defer r.mu.Unlock() } for _, user := range r.source { ok = query(user) if ok { if action(user) { loops++ if actionLimit >= loops { break // break } } } } return } // 选择接收查询功能 //这是为内部的每个用户模型触发的 // 我们想象中的数据源. // 当该函数返回true时,它将停止迭代. // //它返回查询返回的最后一个已知布尔值 // 和最后一个已知的use模型user model // 帮助呼叫者减少 LOC. // // 它实际上是一个简单但非常聪明的原型函数 //自从我第一次想到它以来,我一直在使用它, // 希望你会发现它也很有用. func (r *userMemoryRepository) Select(query Query) (user datamodels.User, found bool) { found = r.Exec(query, func(m datamodels.User) bool { user = m return true }, 1, ReadOnlyMode) // set an empty datamodels.User if not found at all. if !found { user = datamodels.User{} } return } // SelectMany与Select相同但返回一个或多个 datamodels.User 作为切片. // If limit <=0然后它返回一切. func (r *userMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.User) { r.Exec(query, func(m datamodels.User) bool { results = append(results, m) return true }, limit, ReadOnlyMode) return } // InsertOrUpdate将用户添加或更新到(内存)存储. // // 返回新用户,如果有则返回错误. func (r *userMemoryRepository) InsertOrUpdate(user datamodels.User) (datamodels.User, error) { id := user.ID if id == 0 { // 创建新动作 var lastID int64 // 找到最大的ID,以便没有重复 // 在制作应用中,您可以使用第三方 // 库以生成UUID作为字符串。. r.mu.RLock() for _, item := range r.source { if item.ID > lastID { lastID = item.ID } } r.mu.RUnlock() id = lastID + 1 user.ID = id // map-specific thing r.mu.Lock() r.source[id] = user r.mu.Unlock() return user, nil } // 基于的更新操作 user.ID, // 如果不是空的话,我们将允许更新海报和流派. // 或者我们可以做替代: // r.source[id] = user // 并评论下面的代码; current, exists := r.Select(func(m datamodels.User) bool { return m.ID == id }) if !exists { // IID不是真实的,返回错误. return datamodels.User{}, errors.New("无法更新不存在的用户") } // 或者注释这些和 r.source[id] = user 进行纯替换 if user.Username != "" { current.Username = user.Username } if user.Firstname != "" { current.Firstname = user.Firstname } // map-specific thing r.mu.Lock() r.source[id] = current r.mu.Unlock() return user, nil } func (r *userMemoryRepository) Delete(query Query, limit int) bool { return r.Exec(query, func(m datamodels.User) bool { delete(r.source, m.ID) return true }, limit, ReadWriteMode) } ~~~ File: mvc/login/services/user_service.go ~~~ package services import ( "errors" "github.com/kataras/iris/_examples/mvc/login/datamodels" "github.com/kataras/iris/_examples/mvc/login/repositories" ) // UserService处理用户数据模型的CRUID操作, // 它取决于用户存储库的动作. // 它是将数据源与更高级别的组件分离 // 因此,可以使用不同的存储库类型和相同的逻辑,而无需任何更改. //因此,可以使用不同的存储库类型和相同的逻辑,而无需任何更改 //因为我们可能需要在将来更改或尝试实验性的不同域逻辑. type UserService interface { GetAll() []datamodels.User GetByID(id int64) (datamodels.User, bool) GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool) DeleteByID(id int64) bool Update(id int64, user datamodels.User) (datamodels.User, error) UpdatePassword(id int64, newPassword string) (datamodels.User, error) UpdateUsername(id int64, newUsername string) (datamodels.User, error) Create(userPassword string, user datamodels.User) (datamodels.User, error) } // NewUserService返回默认用户服务. func NewUserService(repo repositories.UserRepository) UserService { return &userService{ repo: repo, } } type userService struct { repo repositories.UserRepository } // GetAll 返回全部 users. func (s *userService) GetAll() []datamodels.User { return s.repo.SelectMany(func(_ datamodels.User) bool { return true }, -1) } // GetByID根据其id返回用户. func (s *userService) GetByID(id int64) (datamodels.User, bool) { return s.repo.Select(func(m datamodels.User) bool { return m.ID == id }) } // GetByUsernameAndPassword根据用户名和passowrd返回用户, // 用于身份验证. func (s *userService) GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool) { if username == "" || userPassword == "" { return datamodels.User{}, false } return s.repo.Select(func(m datamodels.User) bool { if m.Username == username { hashed := m.HashedPassword if ok, _ := datamodels.ValidatePassword(userPassword, hashed); ok { return true } } return false }) } // 更新现有用户的每个字段的更新 User, //通过公众使用是不安全的API, // 但是我们会在它上面使用它 web/controllers/user_controller.go#PutBy // 为了告诉你它是如何工作的. func (s *userService) Update(id int64, user datamodels.User) (datamodels.User, error) { user.ID = id return s.repo.InsertOrUpdate(user) } // UpdatePassword 更新用户password. func (s *userService) UpdatePassword(id int64, newPassword string) (datamodels.User, error) { // update the user and return it. hashed, err := datamodels.GeneratePassword(newPassword) if err != nil { return datamodels.User{}, err } return s.Update(id, datamodels.User{ HashedPassword: hashed, }) } // UpdateUsername 更新用户 username. func (s *userService) UpdateUsername(id int64, newUsername string) (datamodels.User, error) { return s.Update(id, datamodels.User{ Username: newUsername, }) } // 创建插入新用户 User, // userPassword是客户端类型password // 它将在插入我们的存储库之前进行哈希处理. func (s *userService) Create(userPassword string, user datamodels.User) (datamodels.User, error) { if user.ID > 0 || userPassword == "" || user.Firstname == "" || user.Username == "" { return datamodels.User{}, errors.New("unable to create this user") } hashed, err := datamodels.GeneratePassword(userPassword) if err != nil { return datamodels.User{}, err } user.HashedPassword = hashed return s.repo.InsertOrUpdate(user) } // DeleteByID按其ID删除用户. // // 如果删除则返回true,否则返回false. func (s *userService) DeleteByID(id int64) bool { return s.repo.Delete(func(m datamodels.User) bool { return m.ID == id }, 1) } ~~~ File: mvc/login/web/controllers/user_controller.go ~~~ // file: controllers/user_controller.go package controllers import ( "github.com/kataras/iris/_examples/mvc/login/datamodels" "github.com/kataras/iris/_examples/mvc/login/services" "github.com/kataras/iris" "github.com/kataras/iris/mvc" "github.com/kataras/iris/sessions" ) // UserController是我们的 /user controller. // UserController负责处理以下请求: // GET /user/register // POST /user/register // GET /user/login // POST /user/login // GET /user/me // 所有HTTP方法 /user/logout type UserController struct { // 每个请求都由Iris自动绑定上下文, // 请记住,每次传入请求时,iris每次都会创建一个新的UserController, // 所以所有字段都是默认的请求范围,只能设置依赖注入 // 自定义字段,如服务,对所有请求都是相同的(静态绑定) //以及取决于当前上下文的会话(动态绑定). Ctx iris.Context // 我们的UserService,它是一个界面 //从主应用程序绑定。. Service services.UserService // Session,使用依赖注入绑定 main.go. Session *sessions.Session } const userIDKey = "UserID" func (c *UserController) getCurrentUserID() int64 { userID := c.Session.GetInt64Default(userIDKey, 0) return userID } func (c *UserController) isLoggedIn() bool { return c.getCurrentUserID() > 0 } func (c *UserController) logout() { c.Session.Destroy() } var registerStaticView = mvc.View{ Name: "user/register.html", Data: iris.Map{"Title": "User Registration"}, } // GetRegister句柄 GET: http://localhost:8080/user/register. func (c *UserController) GetRegister() mvc.Result { if c.isLoggedIn() { c.logout() } return registerStaticView } // PostRegister handles POST: http://localhost:8080/user/register. func (c *UserController) PostRegister() mvc.Result { // 从表单中获取名字,用户名和密码. var ( firstname = c.Ctx.FormValue("firstname") username = c.Ctx.FormValue("username") password = c.Ctx.FormValue("password") ) //创建新用户时,密码将由服务进行哈希处理. u, err := c.Service.Create(password, datamodels.User{ Username: username, Firstname: firstname, }) // 甚至将用户的ID设置为此会话 if err != nil, // 零id并不重要,因为.getCurrentUserID()会检查它. // If err != nil 然后会显示,见下文 mvc.Response.Err: err. c.Session.Set(userIDKey, u.ID) return mvc.Response{ //如果不是nil则会显示此错误. Err: err, // 重定向到 /user/me. Path: "/user/me", // 从POST重定向到GET请求时,您应该使用此HTTP状态代码, // 但是,如果你有一些(复杂的)替代方案 // 在线搜索甚至HTTP RFC. //态“请参阅其他”RFC 7231,但iris可以自动修复它 //但很高兴知道你可以设置自定义代码; // Code: 303, } } var loginStaticView = mvc.View{ Name: "user/login.html", Data: iris.Map{"Title": "User Login"}, } // GetLogin handles GET: http://localhost:8080/user/login. func (c *UserController) GetLogin() mvc.Result { if c.isLoggedIn() { // 如果它已经登录,则销毁前一个会话 c.logout() } return loginStaticView } // PostLogin handles POST: http://localhost:8080/user/register. func (c *UserController) PostLogin() mvc.Result { var ( username = c.Ctx.FormValue("username") password = c.Ctx.FormValue("password") ) u, found := c.Service.GetByUsernameAndPassword(username, password) if !found { return mvc.Response{ Path: "/user/register", } } c.Session.Set(userIDKey, u.ID) return mvc.Response{ Path: "/user/me", } } // GetMe handles GET: http://localhost:8080/user/me. func (c *UserController) GetMe() mvc.Result { if !c.isLoggedIn() { //如果它没有登录,则将用户重定向到登录页面. return mvc.Response{Path: "/user/login"} } u, found := c.Service.GetByID(c.getCurrentUserID()) if !found { // 如果会话存在但由于某种原因用户不存在于“数据库”中 "database" // t然后注销并重新执行该功能,它会将客户端重定向到 // /user/login page. c.logout() return c.GetMe() } return mvc.View{ Name: "user/me.html", Data: iris.Map{ "Title": "Profile of " + u.Username, "User": u, }, } } // AnyLogout handles All/Any HTTP Methods for: http://localhost:8080/user/logout. func (c *UserController) AnyLogout() { if c.isLoggedIn() { c.logout() } c.Ctx.Redirect("/user/login") } ~~~ File: mvc/login/web/controllers/users_controller.go ~~~ package controllers import ( "github.com/kataras/iris/_examples/mvc/login/datamodels" "github.com/kataras/iris/_examples/mvc/login/services" "github.com/kataras/iris" ) // UsersController是我们的/users API控制器 // GET /users | get all // GET /users/{id:long} | get by id // PUT /users/{id:long} | update by id // DELETE /users/{id:long} | delete by id // 需要基本身份验证. type UsersController struct { // 可选:Iris在每个请求上自动绑定上下文, // 请记住,每次传入请求时,iris每次都会创建一个新的UserController // 所以所有字段都是默认的请求范围,只能设置依赖注入 // 自定义字段,如服务,对所有请求都是相同的(静态绑定). Ctx iris.Context // 我们的UserService,它是一个界面 //从主应用程序绑定. Service services.UserService } // 获取返回列表users. // Demo: // curl -i -u admin:password http://localhost:8080/users // // T如果您有敏感数据,这是正确的方法: // func (c *UsersController) Get() (results []viewmodels.User) { // data := c.Service.GetAll() // // for _, user := range data { // results = append(results, viewmodels.User{user}) // } // return // } // 否则只返回数据模型. func (c *UsersController) Get() (results []datamodels.User) { return c.Service.GetAll() } // GetBy返回a user. // Demo: // curl -i -u admin:password http://localhost:8080/users/1 func (c *UsersController) GetBy(id int64) (user datamodels.User, found bool) { u, found := c.Service.GetByID(id) if !found { // t这条消息将绑定到 // main.go -> app.OnAnyErrorCode -> NotFound -> shared/error.html -> .Message text. c.Ctx.Values().Set("message", "User couldn't be found!") } return u, found // 如果找到它将抛出/发出404 404 if found == false. } // PutBy更新用户. // Demo: // curl -i -X PUT -u admin:password -F "username=kataras" // -F "password=rawPasswordIsNotSafeIfOrNotHTTPs_You_Should_Use_A_client_side_lib_for_hash_as_well" // http://localhost:8080/users/1 func (c *UsersController) PutBy(id int64) (datamodels.User, error) { // username := c.Ctx.FormValue("username") // password := c.Ctx.FormValue("password") u := datamodels.User{} if err := c.Ctx.ReadForm(&u); err != nil { return u, err } return c.Service.Update(id, u) } // DeleteBy 删除 a user. // Demo: // curl -i -X DELETE -u admin:password http://localhost:8080/users/1 func (c *UsersController) DeleteBy(id int64) interface{} { wasDel := c.Service.DeleteByID(id) if wasDel { // 返回已删除的用户D return map[string]interface{}{"deleted": id} } // 在这里我们可以看到一个方法函数 // 可以返回这两种类型中的任何一种((map or int), // 我们不必将返回类型指定为特定类型. return iris.StatusBadRequest // same as 400. } ~~~ File: mvc/login/web/middleware/basicauth.go ~~~ // file: middleware/basicauth.go package middleware import "github.com/kataras/iris/middleware/basicauth" //BasicAuth中间件示例. var BasicAuth = basicauth.New(basicauth.Config{ Users: map[string]string{ "admin": "password", }, }) ~~~ File: mvc/login/web/public/css/site.css ~~~ /* Bordered form */ form { border: 3px solid #f1f1f1; } /* Full-width inputs */ input[type=text], input[type=password] { width: 100%; padding: 12px 20px; margin: 8px 0; display: inline-block; border: 1px solid #ccc; box-sizing: border-box; } /* Set a style for all buttons */ button { background-color: #4CAF50; color: white; padding: 14px 20px; margin: 8px 0; border: none; cursor: pointer; width: 100%; } /* Add a hover effect for buttons */ button:hover { opacity: 0.8; } /* Extra style for the cancel button (red) */ .cancelbtn { width: auto; padding: 10px 18px; background-color: #f44336; } /* Center the container */ /* Add padding to containers */ .container { padding: 16px; } /* The "Forgot password" text */ span.psw { float: right; padding-top: 16px; } /* Change styles for span and cancel button on extra small screens */ @media screen and (max-width: 300px) { span.psw { display: block; float: none; } .cancelbtn { width: 100%; } } ~~~ File: mvc/login/web/views/shared/error.html ~~~ <h1>Error.</h1> <h2>An error occurred while processing your request.</h2> <h3>{{.Message}}</h3> <footer> <h2>Sitemap</h2> <a href="http://localhost:8080/user/register">/user/register</a><br/> <a href="http://localhost:8080/user/login">/user/login</a><br/> <a href="http://localhost:8080/user/logout">/user/logout</a><br/> <a href="http://localhost:8080/user/me">/user/me</a><br/> <h3>requires authentication</h3><br/> <a href="http://localhost:8080/users">/users</a><br/> <a href="http://localhost:8080/users/1">/users/{id}</a><br/> </footer> ~~~ File: mvc/login/web/views/shared/layout.html ~~~ <html> <head> <title>{{.Title}}</title> <link rel="stylesheet" type="text/css" href="/public/css/site.css" /> </head> <body> {{ yield }} </body> </html> ~~~ File: mvc/login/web/views/user/login.html ~~~ <form action="/user/login" method="POST"> <div class="container"> <label><b>Username</b></label> <input type="text" placeholder="Enter Username" name="username" required> <label><b>Password</b></label> <input type="password" placeholder="Enter Password" name="password" required> <button type="submit">Login</button> </div> </form> ~~~ File: mvc/login/web/views/user/me.html ~~~ <p> Welcome back <strong>{{.User.Firstname}}</strong>! </p> ~~~ File: mvc/login/web/views/user/register.html ~~~ <form action="/user/register" method="POST"> <div class="container"> <label><b>Firstname</b></label> <input type="text" placeholder="Enter Firstname" name="firstname" required> <label><b>Username</b></label> <input type="text" placeholder="Enter Username" name="username" required> <label><b>Password</b></label> <input type="password" placeholder="Enter Password" name="password" required> <button type="submit">Register</button> </div> </form> ~~~