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)
}