MVC login示例

目录结构

主目录login

  1. —— datamodels
  2. —— user.go
  3. —— datasource
  4. —— users.go
  5. —— repositories
  6. —— user_repository.go
  7. —— services
  8. —— user_service.go
  9. —— web
  10. —— controllers
  11. —— user_controller.go
  12. —— users_controller.go
  13. —— middleware
  14. —— basicauth.go
  15. —— public
  16. —— css
  17. —— site.css
  18. —— viewmodels
  19. —— viewModels.md
  20. —— views
  21. —— shared
  22. —— error.html
  23. —— layout.html
  24. —— user
  25. —— login.html
  26. —— me.html
  27. —— register.html
  28. —— main.go

代码示例

文件名称 /datamodels/user.goUser结构体定义

  1. package datamodels
  2. import (
  3. "time"
  4. "golang.org/x/crypto/bcrypt"
  5. )
  6. //User是我们的用户示例模型。
  7. //请注意标签(适用于我们的网络应用)
  8. //应该保存在其他文件中,例如“web/viewmodels/user.go”
  9. //可以通过嵌入datamodels.User或
  10. //定义完全新的字段
  11. //示例中,我们将使用此数据模型
  12. //作为我们应用程序中唯一的一个用户模型。
  13. type User struct {
  14. ID int64 `json:"id" form:"id"`
  15. Firstname string `json:"firstname" form:"firstname"`
  16. Username string `json:"username" form:"username"`
  17. HashedPassword []byte `json:"-" form:"-"`
  18. CreatedAt time.Time `json:"created_at" form:"created_at"`
  19. }
  20. // IsValid可以做一些非常简单的“低级”数据验证
  21. func (u User) IsValid() bool {
  22. return u.ID > 0
  23. }
  24. // GeneratePassword将根据我们为我们生成哈希密码
  25. //用户的输入
  26. func GeneratePassword(userPassword string) ([]byte, error) {
  27. return bcrypt.GenerateFromPassword([]byte(userPassword), bcrypt.DefaultCost)
  28. }
  29. // ValidatePassword将检查密码是否匹配
  30. func ValidatePassword(userPassword string, hashed []byte) (bool, error) {
  31. if err := bcrypt.CompareHashAndPassword(hashed, []byte(userPassword)); err != nil {
  32. return false, err
  33. }
  34. return true, nil
  35. }

文件名称 /datasource/user.go数据资源,相当于数据库

  1. //文件: datasource/users.go
  2. package datasource
  3. import (
  4. "errors"
  5. "../datamodels"
  6. )
  7. //引擎来自何处获取数据,在这种情况下是用户。
  8. type Engine uint32
  9. const (
  10. //内存代表简单的内存位置;
  11. // map[int64]datamodels.User随时可以使用,这是我们在这个例子中的来源。
  12. Memory Engine = iota
  13. // Bolt for boltdb source location.
  14. Bolt
  15. // MySQL for mysql-compatible source location.
  16. MySQL
  17. )
  18. //为了简单起见,Load Users从内存中返回所有用户(空map)。
  19. func LoadUsers(engine Engine) (map[int64]datamodels.User, error) {
  20. if engine != Memory {
  21. return nil, errors.New("for the shake of simplicity we're using a simple map as the data source")
  22. }
  23. return make(map[int64]datamodels.User), nil
  24. }

文件名称 /repositories/user_repository.go对数据筛选,必要数据的仓库

  1. package repositories
  2. import (
  3. "errors"
  4. "sync"
  5. "../datamodels"
  6. )
  7. // Query表示访问者和操作查询。
  8. type Query func(datamodels.User) bool
  9. // UserRepository处理用户实体/模型的基本操作。
  10. //它是一个可测试的接口,即内存用户存储库或 连接到sql数据库。
  11. type UserRepository interface {
  12. Exec(query Query, action Query, limit int, mode int) (ok bool)
  13. Select(query Query) (user datamodels.User, found bool)
  14. SelectMany(query Query, limit int) (results []datamodels.User)
  15. InsertOrUpdate(user datamodels.User) (updatedUser datamodels.User, err error)
  16. Delete(query Query, limit int) (deleted bool)
  17. }
  18. // NewUserRepository返回一个新的基于用户内存的存储库,
  19. //我们示例中唯一的存储库类型。
  20. func NewUserRepository(source map[int64]datamodels.User) UserRepository {
  21. return &userMemoryRepository{source: source}
  22. }
  23. //userMemoryRepository是一个“UserRepository”
  24. //使用内存数据源(map)管理用户。
  25. type userMemoryRepository struct {
  26. source map[int64]datamodels.User
  27. mu sync.RWMutex
  28. }
  29. const (
  30. // ReadOnlyMode将RLock(read) 数据。
  31. ReadOnlyMode = iota
  32. // ReadWriteMode将锁定(read/write)数据。
  33. ReadWriteMode
  34. )
  35. func (r *userMemoryRepository) Exec(query Query, action Query, actionLimit int, mode int) (ok bool) {
  36. loops := 0
  37. if mode == ReadOnlyMode {
  38. r.mu.RLock()
  39. defer r.mu.RUnlock()
  40. } else {
  41. r.mu.Lock()
  42. defer r.mu.Unlock()
  43. }
  44. for _, user := range r.source {
  45. ok = query(user)
  46. if ok {
  47. if action(user) {
  48. loops++
  49. if actionLimit >= loops {
  50. break // break
  51. }
  52. }
  53. }
  54. }
  55. return
  56. }
  57. //Select接收查询方法
  58. //为内部的每个用户模型触发查找我们想象中的数据源
  59. //当该函数返回true时,它会停止迭代。
  60. //它实际上是一个简单但非常游泳的原型函数
  61. //自从我第一次想到它以来,我一直在使用它,
  62. //希望你会发现它也很有用。
  63. func (r *userMemoryRepository) Select(query Query) (user datamodels.User, found bool) {
  64. found = r.Exec(query, func(m datamodels.User) bool {
  65. user = m
  66. return true
  67. }, 1, ReadOnlyMode)
  68. //设置一个空的datamodels.User,如果根本找不到的话
  69. if !found {
  70. user = datamodels.User{}
  71. }
  72. return
  73. }
  74. // SelectMany与Select相同但返回一个或多个datamodels.User作为切片。
  75. //如果limit <= 0则返回所有内容。
  76. func (r *userMemoryRepository) SelectMany(query Query, limit int) (results []datamodels.User) {
  77. r.Exec(query, func(m datamodels.User) bool {
  78. results = append(results, m)
  79. return true
  80. }, limit, ReadOnlyMode)
  81. return
  82. }
  83. // InsertOrUpdate将用户添加或更新到(内存)存储。
  84. //返回新用户,如果有则返回错误。
  85. func (r *userMemoryRepository) InsertOrUpdate(user datamodels.User) (datamodels.User, error) {
  86. id := user.ID
  87. if id == 0 {
  88. var lastID int64
  89. //找到最大的ID,以便不重复 在制作应用中,
  90. //您可以使用第三方库以生成UUID作为字符串。
  91. r.mu.RLock()
  92. for _, item := range r.source {
  93. if item.ID > lastID {
  94. lastID = item.ID
  95. }
  96. }
  97. r.mu.RUnlock()
  98. id = lastID + 1
  99. user.ID = id
  100. r.mu.Lock()
  101. r.source[id] = user
  102. r.mu.Unlock()
  103. return user, nil
  104. }
  105. //基于user.ID更新操作,
  106. //这里我们将允许更新海报和流派,如果不是空的话。
  107. //或者我们可以做替换:
  108. // r.source [id] =user
  109. //的代码;
  110. current, exists := r.Select(func(m datamodels.User) bool {
  111. return m.ID == id
  112. })
  113. if !exists { // ID不是真实的,返回错误。
  114. return datamodels.User{}, errors.New("failed to update a nonexistent user")
  115. }
  116. //和r.source [id] =user 进行纯替换
  117. if user.Username != "" {
  118. current.Username = user.Username
  119. }
  120. if user.Firstname != "" {
  121. current.Firstname = user.Firstname
  122. }
  123. r.mu.Lock()
  124. r.source[id] = current
  125. r.mu.Unlock()
  126. return user, nil
  127. }
  128. func (r *userMemoryRepository) Delete(query Query, limit int) bool {
  129. return r.Exec(query, func(m datamodels.User) bool {
  130. delete(r.source, m.ID)
  131. return true
  132. }, limit, ReadWriteMode)
  133. }

文件名称 /services/user_service.go业务逻辑代码

  1. package services
  2. import (
  3. "errors"
  4. "../datamodels"
  5. "../repositories"
  6. )
  7. //UserService处理用户数据模型的CRUID操作,
  8. //它取决于用户存储库的操作。
  9. //这是将数据源与更高级别的组件分离。
  10. //因此,不同的存储库类型可以使用相同的逻辑,而无需任何更改。
  11. //它是一个接口,它在任何地方都被用作接口
  12. //因为我们可能需要在将来更改或尝试实验性的不同域逻辑。
  13. type UserService interface {
  14. GetAll() []datamodels.User
  15. GetByID(id int64) (datamodels.User, bool)
  16. GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool)
  17. DeleteByID(id int64) bool
  18. Update(id int64, user datamodels.User) (datamodels.User, error)
  19. UpdatePassword(id int64, newPassword string) (datamodels.User, error)
  20. UpdateUsername(id int64, newUsername string) (datamodels.User, error)
  21. Create(userPassword string, user datamodels.User) (datamodels.User, error)
  22. }
  23. // NewUserService返回默认用户服务
  24. func NewUserService(repo repositories.UserRepository) UserService {
  25. return &userService{
  26. repo: repo,
  27. }
  28. }
  29. type userService struct {
  30. repo repositories.UserRepository
  31. }
  32. // GetAll返回所有用户。
  33. func (s *userService) GetAll() []datamodels.User {
  34. return s.repo.SelectMany(func(_ datamodels.User) bool {
  35. return true
  36. }, -1)
  37. }
  38. // GetByID根据其id返回用户。
  39. func (s *userService) GetByID(id int64) (datamodels.User, bool) {
  40. return s.repo.Select(func(m datamodels.User) bool {
  41. return m.ID == id
  42. })
  43. }
  44. //获取yUsernameAndPassword根据用户名和密码返回用户,
  45. //用于身份验证。
  46. func (s *userService) GetByUsernameAndPassword(username, userPassword string) (datamodels.User, bool) {
  47. if username == "" || userPassword == "" {
  48. return datamodels.User{}, false
  49. }
  50. return s.repo.Select(func(m datamodels.User) bool {
  51. if m.Username == username {
  52. hashed := m.HashedPassword
  53. if ok, _ := datamodels.ValidatePassword(userPassword, hashed); ok {
  54. return true
  55. }
  56. }
  57. return false
  58. })
  59. }
  60. //更新现有用户的每个字段的更新,
  61. //通过公共API使用是不安全的
  62. //但是我们将在web controllers/user_controller.go#PutBy上使用它
  63. //为了向您展示它是如何工作的。
  64. func (s *userService) Update(id int64, user datamodels.User) (datamodels.User, error) {
  65. user.ID = id
  66. return s.repo.InsertOrUpdate(user)
  67. }
  68. // UpdatePassword更新用户的密码。
  69. func (s *userService) UpdatePassword(id int64, newPassword string) (datamodels.User, error) {
  70. //更新用户并将其返回。
  71. hashed, err := datamodels.GeneratePassword(newPassword)
  72. if err != nil {
  73. return datamodels.User{}, err
  74. }
  75. return s.Update(id, datamodels.User{
  76. HashedPassword: hashed,
  77. })
  78. }
  79. // UpdateUsername更新用户的用户名
  80. func (s *userService) UpdateUsername(id int64, newUsername string) (datamodels.User, error) {
  81. return s.Update(id, datamodels.User{
  82. Username: newUsername,
  83. })
  84. }
  85. //创建插入新用户,
  86. // userPassword是客户端类型的密码
  87. //它将在插入我们的存储库之前进行哈希处理
  88. func (s *userService) Create(userPassword string, user datamodels.User) (datamodels.User, error) {
  89. if user.ID > 0 || userPassword == "" || user.Firstname == "" || user.Username == "" {
  90. return datamodels.User{}, errors.New("unable to create this user")
  91. }
  92. hashed, err := datamodels.GeneratePassword(userPassword)
  93. if err != nil {
  94. return datamodels.User{}, err
  95. }
  96. user.HashedPassword = hashed
  97. return s.repo.InsertOrUpdate(user)
  98. }
  99. // DeleteByID按其id删除用户。
  100. //如果删除则返回true,否则返回false。
  101. func (s *userService) DeleteByID(id int64) bool {
  102. return s.repo.Delete(func(m datamodels.User) bool {
  103. return m.ID == id
  104. }, 1)
  105. }

文件名称 /web/controllers/user_controller.go

  1. // 文件: controllers/user_controller.go
  2. package controllers
  3. import (
  4. "../../datamodels"
  5. "../../services"
  6. "github.com/kataras/iris"
  7. "github.com/kataras/iris/mvc"
  8. "github.com/kataras/iris/sessions"
  9. )
  10. // UserController是我们的/用户控制器。
  11. // UserController负责处理以下请求:
  12. // GET /user/register
  13. // POST /user/register
  14. // GET /user/login
  15. // POST /user/login
  16. // GET /user/me
  17. //所有HTTP方法 /user/logout
  18. type UserController struct {
  19. //每个请求都由Iris自动绑定上下文,
  20. //记住,每次传入请求时,iris每次都会创建一个新的UserController,
  21. //所以所有字段都是默认的请求范围,只能设置依赖注入
  22. //自定义字段,如服务,对所有请求都是相同的(静态绑定)
  23. //和依赖于当前上下文的会话(动态绑定)。
  24. Ctx iris.Context
  25. //我们的UserService,它是一个接口
  26. //从主应用程序绑定。
  27. Service services.UserService
  28. //Session,使用来自main.go的依赖注入绑定
  29. Session *sessions.Session
  30. }
  31. const userIDKey = "UserID"
  32. func (c *UserController) getCurrentUserID() int64 {
  33. userID := c.Session.GetInt64Default(userIDKey, 0)
  34. return userID
  35. }
  36. func (c *UserController) isLoggedIn() bool {
  37. return c.getCurrentUserID() > 0
  38. }
  39. func (c *UserController) logout() {
  40. c.Session.Destroy()
  41. }
  42. var registerStaticView = mvc.View{
  43. Name: "user/register.html",
  44. Data: iris.Map{"Title": "User Registration"},
  45. }
  46. // GetRegister 处理 GET: http://localhost:8080/user/register.
  47. func (c *UserController) GetRegister() mvc.Result {
  48. if c.isLoggedIn() {
  49. c.logout()
  50. }
  51. return registerStaticView
  52. }
  53. // PostRegister 处理 POST: http://localhost:8080/user/register.
  54. func (c *UserController) PostRegister() mvc.Result {
  55. //从表单中获取名字,用户名和密码
  56. var (
  57. firstname = c.Ctx.FormValue("firstname")
  58. username = c.Ctx.FormValue("username")
  59. password = c.Ctx.FormValue("password")
  60. )
  61. //创建新用户,密码将由服务进行哈希处理
  62. u, err := c.Service.Create(password, datamodels.User{
  63. Username: username,
  64. Firstname: firstname,
  65. })
  66. //将用户的id设置为此会话,即使err!= nil,
  67. //零id无关紧要因为.getCurrentUserID()检查它。
  68. //如果错误!= nil那么它将被显示,见下面的mvc.Response.Err:err
  69. c.Session.Set(userIDKey, u.ID)
  70. return mvc.Response{
  71. //如果不是nil,则会显示此错误
  72. Err: err,
  73. //从定向 /user/me.
  74. Path: "/user/me",
  75. //当从POST重定向到GET请求时,您应该使用此HTTP状态代码,
  76. //但是如果你有一些(复杂的)选择
  77. //在线搜索甚至是HTTP RFC。
  78. //状态“查看其他”RFC 7231,但虹膜可以自动修复它
  79. //但很高兴知道你可以设置自定义代码;
  80. //代码:303,
  81. }
  82. }
  83. var loginStaticView = mvc.View{
  84. Name: "user/login.html",
  85. Data: iris.Map{"Title": "User Login"},
  86. }
  87. // GetLogin handles GET: http://localhost:8080/user/login.
  88. func (c *UserController) GetLogin() mvc.Result {
  89. if c.isLoggedIn() {
  90. // if it's already logged in then destroy the previous session.
  91. c.logout()
  92. }
  93. return loginStaticView
  94. }
  95. // PostLogin handles
  96. // PostLogin处理POST: http://localhost:8080/user/register.
  97. func (c *UserController) PostLogin() mvc.Result {
  98. var (
  99. username = c.Ctx.FormValue("username")
  100. password = c.Ctx.FormValue("password")
  101. )
  102. u, found := c.Service.GetByUsernameAndPassword(username, password)
  103. if !found {
  104. return mvc.Response{
  105. Path: "/user/register",
  106. }
  107. }
  108. c.Session.Set(userIDKey, u.ID)
  109. return mvc.Response{
  110. Path: "/user/me",
  111. }
  112. }
  113. // GetMe 处理P GET: http://localhost:8080/user/me.
  114. func (c *UserController) GetMe() mvc.Result {
  115. if !c.isLoggedIn() {
  116. //如果没有登录,则将用户重定向到登录页面。
  117. return mvc.Response{Path: "/user/login"}
  118. }
  119. u, found := c.Service.GetByID(c.getCurrentUserID())
  120. if !found {
  121. //如果session存在但由于某种原因用户不存在于“数据库”中
  122. //然后注销并重新执行该函数,它会将客户端重定向到
  123. // /user/login页面。
  124. c.logout()
  125. return c.GetMe()
  126. }
  127. return mvc.View{
  128. Name: "user/me.html",
  129. Data: iris.Map{
  130. "Title": "Profile of " + u.Username,
  131. "User": u,
  132. },
  133. }
  134. }
  135. // AnyLogout处理 All/AnyHTTP 方法:http://localhost:8080/user/logout
  136. func (c *UserController) AnyLogout() {
  137. if c.isLoggedIn() {
  138. c.logout()
  139. }
  140. c.Ctx.Redirect("/user/login")
  141. }

文件名称 /web/controllers/users_controller.go

  1. package controllers
  2. import (
  3. "../../datamodels"
  4. "../../services"
  5. "github.com/kataras/iris"
  6. )
  7. // UsersController是我们 /users API控制器。
  8. // GET /users | get all
  9. // GET /users/{id:long} | 获取通过 id
  10. // PUT /users/{id:long} | 修改通过 id
  11. // DELETE /users/{id:long} | 删除通过 id
  12. //需要基本身份验证。
  13. type UsersController struct {
  14. //可选:Iris在每个请求中自动绑定上下文,
  15. //记住,每次传入请求时,iris每次都会创建一个新的UserController,
  16. //所以所有字段都是默认的请求范围,只能设置依赖注入
  17. //自定义字段,如Service,对所有请求都是相同的(静态绑定)。
  18. Ctx iris.Context
  19. //我们的UserService,它是一个接口
  20. //从主应用程序绑定。
  21. Service services.UserService
  22. }
  23. //获取用户的返回列表。
  24. // 示例:
  25. // curl -i -u admin:password http://localhost:8080/users
  26. // func (c *UsersController) Get() (results []viewmodels.User) {
  27. // data := c.Service.GetAll()
  28. //
  29. // for _, user := range data {
  30. // results = append(results, viewmodels.User{user})
  31. // }
  32. // return
  33. // }
  34. //否则只返回数据模型
  35. func (c *UsersController) Get() (results []datamodels.User) {
  36. return c.Service.GetAll()
  37. }
  38. // GetBy 返回指定一个id用户
  39. // 示例:
  40. // curl -i -u admin:password http://localhost:8080/users/1
  41. func (c *UsersController) GetBy(id int64) (user datamodels.User, found bool) {
  42. u, found := c.Service.GetByID(id)
  43. if !found {
  44. //此信息将被绑定到
  45. // main.go -> app.OnAnyErrorCode -> NotFound -> shared/error.html -> .Message text.
  46. c.Ctx.Values().Set("message", "User couldn't be found!")
  47. }
  48. return u, found // it will throw/emit 404 if found == false.
  49. }
  50. // PutBy 修改指定用户.
  51. // 示例:
  52. // curl -i -X PUT -u admin:password -F "username=kataras"
  53. // -F "password=rawPasswordIsNotSafeIfOrNotHTTPs_You_Should_Use_A_client_side_lib_for_hash_as_well"
  54. // http://localhost:8080/users/1
  55. func (c *UsersController) PutBy(id int64) (datamodels.User, error) {
  56. // username := c.Ctx.FormValue("username")
  57. // password := c.Ctx.FormValue("password")
  58. u := datamodels.User{}
  59. if err := c.Ctx.ReadForm(&u); err != nil {
  60. return u, err
  61. }
  62. return c.Service.Update(id, u)
  63. }
  64. // DeleteBy 删除指定用户
  65. // Demo:
  66. // curl -i -X DELETE -u admin:password http://localhost:8080/users/1
  67. func (c *UsersController) DeleteBy(id int64) interface{} {
  68. wasDel := c.Service.DeleteByID(id)
  69. if wasDel {
  70. //返回已删除用户的ID
  71. return map[string]interface{}{"deleted": id}
  72. }
  73. //在这里我们可以看到一个方法函数
  74. //可以返回这两种类型中的任何一种(map或int),
  75. //我们不必将返回类型指定为特定类型。
  76. return iris.StatusBadRequest //等同于 400.
  77. }

文件名称 /web/middleware/basicauth.go

  1. // 文件: middleware/basicauth.go
  2. package middleware
  3. import "github.com/kataras/iris/middleware/basicauth"
  4. // BasicAuth中间件示例。
  5. var BasicAuth = basicauth.New(basicauth.Config{
  6. Users: map[string]string{
  7. "admin": "password",
  8. },
  9. })

文件名称 /web/public/css/site.css

  1. /* Bordered form */
  2. form {
  3. border: 3px solid #f1f1f1;
  4. }
  5. /* Full-width inputs */
  6. input[type=text], input[type=password] {
  7. width: 100%;
  8. padding: 12px 20px;
  9. margin: 8px 0;
  10. display: inline-block;
  11. border: 1px solid #ccc;
  12. box-sizing: border-box;
  13. }
  14. /* Set a style for all buttons */
  15. button {
  16. background-color: #4CAF50;
  17. color: white;
  18. padding: 14px 20px;
  19. margin: 8px 0;
  20. border: none;
  21. cursor: pointer;
  22. width: 100%;
  23. }
  24. /* Add a hover effect for buttons */
  25. button:hover {
  26. opacity: 0.8;
  27. }
  28. /* Extra style for the cancel button (red) */
  29. .cancelbtn {
  30. width: auto;
  31. padding: 10px 18px;
  32. background-color: #f44336;
  33. }
  34. /* Center the container */
  35. /* Add padding to containers */
  36. .container {
  37. padding: 16px;
  38. }
  39. /* The "Forgot password" text */
  40. span.psw {
  41. float: right;
  42. padding-top: 16px;
  43. }
  44. /* Change styles for span and cancel button on extra small screens */
  45. @media screen and (max-width: 300px) {
  46. span.psw {
  47. display: block;
  48. float: none;
  49. }
  50. .cancelbtn {
  51. width: 100%;
  52. }
  53. }

文件名称 /web/viewmodels/viewModels.md

  1. 应该有视图模型,客户端将能够看到的结构例:
  2. import (
  3. "github.com/kataras/iris/_examples/mvc/login/datamodels"
  4. "github.com/kataras/iris/context"
  5. )
  6. type User struct {
  7. datamodels.User
  8. }
  9. func (m User) IsValid() bool {
  10. /*做一些检查,如果有效则返回true ... */
  11. return m.ID > 0
  12. }
  13. Iris能够将任何自定义数据结构转换为HTTP响应调度程序,
  14. 从理论上讲,如果真的有必要,可以使用以下内容;
  15. //Dispatch实现`kataras/iris/mvc#Result`接口。
  16. //将`User` 作为受控的http响应发送。
  17. //如果其ID为零或更小,则返回404未找到错误
  18. //否则返回其json表示,
  19. //(就像控制器的函数默认为自定义类型一样)。
  20. //不要过度,应用程序的逻辑不应该在这里。可以在这里添加简单的检查验证
  21. //这只是一个展示
  22. //想象一下设计更大的应用程序时此功能將很有帮助。
  23. //调用控制器方法返回值的函数
  24. //是`User` 的类型。
  25. func (m User) Dispatch(ctx context.Context) {
  26. if !m.IsValid() {
  27. ctx.NotFound()
  28. return
  29. }
  30. ctx.JSON(m, context.JSON{Indent: " "})
  31. }
  32. 但是,我们将使用“datamodels”作为唯一的一个模型包,因为
  33. User结构不包含任何敏感数据,客户端可以查看其所有字段我们内部不需要任何额外的功能或验证。

文件名称 /web/views/shared/error.html

  1. <h1>错误</h1>
  2. <h2>处理您的请求时发生错误。</h2>
  3. <h3>{{.Message}}</h3>
  4. <footer>
  5. <h2>Sitemap</h2>
  6. <a href="http://localhost:8080/user/register">/user/register</a><br/>
  7. <a href="http://localhost:8080/user/login">/user/login</a><br/>
  8. <a href="http://localhost:8080/user/logout">/user/logout</a><br/>
  9. <a href="http://localhost:8080/user/me">/user/me</a><br/>
  10. <h3>requires authentication</h3><br/>
  11. <a href="http://localhost:8080/users">/users</a><br/>
  12. <a href="http://localhost:8080/users/1">/users/{id}</a><br/>
  13. </footer>

文件名称 shared/layout.html


<html>
<head>
    <title>{{.Title}}</title>
    <link rel="stylesheet" type="text/css" href="/public/css/site.css" />
</head>
<body>
    {{ yield }}
</body>
</html>

文件名称 /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>

文件名称 /web/views/user/me.html


<p>
    Welcome back <strong>{{.User.Firstname}}</strong>!
</p>

文件名称 /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>

目录结构