4、登录界面的实现

a、实现登录界面响应:

ru.GET("/login", views.Html.Login)


func (*HTMLApi) Login(c *gin.Context) {
   c.HTML(200, "login.html", config.Cfg.Viewer)
}

b、登录数据接口实现:

登录接口的实现需要提交表单,将用户名和密码提交,需要涉及到post请求,前端提交参数的绑定必须使用shoudBind()进行绑定,否则前端提交数据无法获取。

ru.POST("/api/v1/login", api.API.Login)


func (*Api) Login(c *gin.Context) {
   //接收用户名和密码
   //返回数据

   //必须使用shouldBind获取参数  postform 不行
   userMima := models.Mima{}
   err := c.ShouldBind(&userMima)
   if err != nil {
      log.Println("从页面获取的用户参数失败")
      return
   }
   username := userMima.UserName
   passwd := userMima.Passwd

   loginRes, err := service.Login(username, passwd)
   //fmt.Println(loginRes.Usf.Uid, loginRes.Usf.UserName)
   if err != nil {
      fmt.Println("我到了这")
      var res models.Result
      res.Code = -999
      res.Error = err.Error()
      //resultJson, err := json.Marshal(res)
      //c.Set("Content-Type", "application/json")
      //if err != nil {
      // log.Println("json 序列化失败")
      // return
      //}
      c.JSON(200, gin.H{
         "code":  res.Code,
         "data":  "",
         "error": res.Error,
      })
      return
   }
   var result models.Result
   result.Code = 200
   result.Error = ""
   result.Data = loginRes
   //_, err = json.Marshal(result)
   //if err != nil {
   // log.Println("json 序列化失败")
   // return
   //}
   c.JSON(200, gin.H{
      "code":     result.Code,
      "data":     result.Data,
      "error":    result.Error,
      "userName": loginRes.UserInfo.UserName,
   })
}
service.Login(username, passwd)
func Login(username, passwd string) (*models.LoginRes, error) {
   passwd = utils.Md5Crypt(passwd, "mszlu")
   //fmt.Println("********************************")
   //fmt.Println(username, passwd)
   user := dao.GetUser(username, passwd)
   if user == nil {
      log.Println("登录时用户查询失败")
      return nil, errors.New("账号密码不真切")
   }
   uid := user.Uid
   //生成token
   token, err := utils.Award(&uid)
   if err != nil {
      log.Println("生成token失败")
   }
   var userInfo models.UserInfo
   userInfo.Uid = user.Uid
   userInfo.UserName = user.UserName
   userInfo.Avatar = user.Avatar

   var lr = &models.LoginRes{
      Token:    token,
      UserInfo: userInfo,
   }

   return lr, nil
}

注意:返回的是json类型的,这个返回什么类型应该按照前端要求,在js文件中的index.js中,查看登录逻辑,在ajax语言包括的部分有限制:

$.ajax({
  url: "/api/v1/login",
  data: JSON.stringify({ username: name, passwd: MD5Passwd }),
  contentType: "application/json",
  type: "POST",
  success: function (res) {
      if (res.code !== 200) {
        return tipEle.show().text(res.error);
      }
      var data = res.data || {};
      localStorage.setItem(TOKEN_KEY, data.token);
      localStorage.setItem(USER_KEY, JSON.stringify(data.userInfo));
      location.href = "/";
  },

返回json类型,需要字段code、data,所以我们的返回函数会设置字段data,和code字段在结构体中,必须一一对应。

5、搜索功能实现

//搜索
ru.GET("/api/v1/post/search", api.API.Search)

同样查看前端js文件,需要返回json数据,并且包括字段data和code状态码,其中data数据是可现实的文章内容,

$.ajax({
  url: "/api/v1/post/search?val=" + val,
  contentType: "application/json",
  success: function (res) {
    if (res.code !== 200) return alert(res.error);
    var data = res.data || [];
    searchList = [];
    if (data.length === 0) return drop.html("");
    for (var i = 0, len = data.length; i < len; i++) {
      var item = data[i];
      searchList.push(
        "<a href='/p/" + item.pid + ".html'>" + item.title + "<a/>"
      );
      drop.show().html(searchList.join(""));
    }
  },
});

并且可转到文章id的详细内容

func (*Api) Search(c *gin.Context) {
   search := c.Query("val")
   //获取表单

   //每页显示的数量
   posts, err := dao.GetPostPageBySearch(search)
   if len(posts) == 0 {
      fmt.Println("不对")
   }

   if err != nil {
      fmt.Println("********************")
      c.JSON(200, gin.H{
         "code": 501,
         "err":  err,
         "data": "",
      })
      return
   }
   var searchRes []SearchRes
   for _, v := range posts {
      searchRes = append(searchRes, SearchRes{v.Pid, v.Title})
   }
   c.JSON(200, gin.H{
      "code":  200,
      "error": "",
      "data":  searchRes,
   })
}
dao.GetPostPageBySearch(search)
func GetPostPageBySearch(search string) ([]models.Post, error) {

   row, err := DB.Query("select * from  blog_post where title  like ?", "%"+search+"%")
   var posts []models.Post
   if err != nil {
      return nil, err
   }

   for row.Next() {
      var post models.Post
      err = row.Scan(&post.Pid,
         &post.Title,
         &post.Content,
         &post.Markdown,
         &post.CategoryId,
         &post.UserId,
         &post.ViewCount,
         &post.Type,
         &post.Slug,
         &post.CreateAt,
         &post.UpdateAt,
      )
      if err != nil {
         return nil, err
      }
      posts = append(posts, post)
   }
   return posts, nil
}

6、写blog文章

a、获取写文章页面
 

ru.GET("/writing", views.Html.Writing)


func (HTMLApi) Writing(c *gin.Context) {

   wr := service.Writing()
   c.HTML(http.StatusOK, "writing.html", wr)

}


type WritingRes struct {
   Title     string
   CdnURL    string
   Categorys []Category
}

写文章时写页面的展示回复结构体,对应前端writing.html

func Writing() (wr models.WritingRes) {
   wr.Title = config.Cfg.Viewer.Title
   wr.CdnURL = config.Cfg.System.CdnURL
   category, err := dao.GetAllCategory()
   if err != nil {
      log.Println(err)
      return
   }
   wr.Categorys = category
   return
}

b、提交写文章

//提交写的文章
ru.POST("/api/v1/post", api.API.SavePost)
func (*Api) SavePost(c *gin.Context) {
   //获取用户id判断是否登录
   token := c.Request.Header.Get("Authorization")

   _, claim, err := utils.ParseToken(token)
   if err != nil {
      c.HTML(200, "writing.html", err)
      return
   }
   uid := claim.Uid
   var pv SaveP

   c.ShouldBind(&pv)

   // post save

   cid, _ := strconv.Atoi(pv.CategoryId)

   typeId, _ := strconv.Atoi(pv.Type)
   post := &models.Post{
      Pid:        -1,
      Title:      pv.Title,
      Slug:       pv.Slug,
      Content:    pv.Content,
      Markdown:   pv.Markdown,
      CategoryId: cid,
      UserId:     uid,
      ViewCount:  0,
      Type:       typeId,
      CreateAt:   time.Now(),
      UpdateAt:   time.Now(),
   }
   service.SavePost(post)

   c.JSON(200, gin.H{
      "code":  200,
      "error": "这里没",
      "data":  post,
   })
}
其中上述的登录验证使用的jwt

绑定的结构体为:

type SaveP struct {
   CategoryId string `json:"categoryId"`
   Content    string `json:"content"`
   Markdown   string `json:"markdown"`
   Slug       string `json:"slug"`
   Title      string `json:"title"`
   Type       string `json:"type"`
}
// 生成Token
func Award(uid *int) (string, error) {
   // 过期时间 默认7天
   expireTime := time.Now().Add(7 * 24 * time.Hour)
   claims := &Claims{
      Uid: *uid,
      StandardClaims: gojwt.StandardClaims{
         ExpiresAt: expireTime.Unix(),
         IssuedAt:  time.Now().Unix(),
      },
   }
   // 生成token
   token := gojwt.NewWithClaims(gojwt.SigningMethodHS256, claims)
   tokenStr, err := token.SignedString(jwtKey)
   if err != nil {
      return "", err
   }
   return tokenStr, nil
}

// 解析token
func ParseToken(tokenStr string) (*gojwt.Token, *Claims, error) {
   claims := &Claims{}
   token, err := gojwt.ParseWithClaims(tokenStr, claims, func(t *gojwt.Token) (interface{}, error) {
      return jwtKey, nil
   })
   if err != nil {
      return nil, nil, err
   }
   return token, claims, err
}

我们在登录的时候使用jwt包生产登录的token,在验证登录的时候选择获取用户的token去验证token := c.Request.Header.Get("Authorization"),

验证成功返回前端的json需要的数据,并且保存文章信息到数据库:

func SavePost(post *models.Post) {
   dao.SavePost(post)
}



func SavePost(post *models.Post) {
   ret, err := DB.Exec("insert into blog_post (title,content,markdown,category_id,user_id,view_count,type,slug,create_at,update_at) "+
      "values (?,?,?,?,?,?,?,?,?,?)",
      post.Title,
      post.Content,
      post.Markdown,
      post.CategoryId,
      post.UserId,
      post.ViewCount,
      post.Type,
      post.Slug,
      post.CreateAt,
      post.UpdateAt,
   )
   if err != nil {
      log.Println(err)

   }
   //获取自动更新的id
   pid, _ := ret.LastInsertId()
   post.Pid = int(pid)

}