用户模型已经构建完了,可以愉快的写接口开发了,来编写第一个接口,登录/注册接口 首先定义接口routers/routers.go
...
apiv1.POST("auth",v1.AuthStore) //登录/注册接口
...
然后实现接口定义的方法,这时候还只是一个空壳routers/v1/user.go
package v1
import (
"api/pkg/e"
"api/pkg/util"
"github.com/gin-gonic/gin"
)
//登录,注册
func UserStore(c *gin.Context) {
mobile := c.PostForm("mobile") //取出请求参数手机号mobile
vCode := c.PostForm("code") //取出请求参数验证码code
util.ResponseWithJson(e.SUCCESS,gin.H{
"Mobile":mobile,
"Code":vCode,
},c)
}
有一点需要注意,有些参数是需要做验证的,所以这里使用一个三方验证的依赖
终端执行
go get -u github.com/astaxie/beego/validation
这个是beego框架下的一个表单验证依赖。
根据需求,先根据手机号查询数据库中是否已有这个账号,有的话就返回信息,没有就创建后返回信息。编写这些数据库操作方法models/user.go
...
//根据手机号查找用户
func FindUserByMobile(mobile string) (*User, error) {
var user User
err := db.Where("mobile = ?",mobile).First(&user).Error
return &user,err
}
//创建用户
func CreateUser(mobile string) (*User, error) {
user := User{
Mobile: mobile,
}
err := db.Create(&user).Error
return &user, err
}
完善接口方法routers/v1/user.go
package v1
import (
"api/models"
"api/pkg/e"
"api/pkg/util"
"github.com/astaxie/beego/validation"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
//登录,注册接口
func UserStore(c *gin.Context) {
mobile := c.PostForm("mobile") //取出请求参数手机号mobile
vCode := c.PostForm("code") //取出请求参数验证码code
//对请求对参数进行验证
validate := validation.Validation{}
validate.Required(mobile,"Mobile").Message("手机号有误") //因为测试环境,所以使用了Required方法,正式下使用Mobile方法,做手机号校验。
validate.Length(vCode,4,"Code").Message("验证码格式不正确")
//校验错误,有错误返回json
if isOk := checkValidation(&validate, c); isOk == false { //校验不通过
return
}
//数据库根据手机号查询用户信息
user,err := models.FindUserByMobile(mobile)
if gorm.IsRecordNotFoundError(err) { //如果数据库没有查询到没有用户信息,代表要注册,新创建用户信息
user,err = models.CreateUser(mobile)
if err != nil {
util.ResponseWithJson(e.ERROR, "数据库操作错误", c)
return
}
}else {
if err !=nil {
util.ResponseWithJson(e.ERROR, "数据库操作错误", c)
return
}
}
util.ResponseWithJson(e.SUCCESS,gin.H{
"User":user,
},c)
}
/*检查请求参数是否有错误,如果有的话返回false*/
func checkValidation(vali *validation.Validation,c *gin.Context)bool {
if vali.HasErrors() { //请求的参数有误
var errs []string //创建一个保存错误信息的数组
for _,err := range vali.Errors{ //遍历错误信息数组,把错误信息添加到数组当中
errs = append(errs,err.Message)
}
util.ResponseWithJson(e.INVALID_PARAMS,errs,c) //返回客户端错误信息
return false
}
return true
}
重新run一下程序,我们使用postman来分别测试一下无参数情况、只传手机号情况、传完整参数 http://127.0.0.1/api/v1/auth 然后查看数据库查看是否有数据
上述的流程是没有添加短信验证这一环节的,因为手中没有企业账号,所以这一步是省略了的。推荐两个验证码服务平台阿里云短信服务Go文档和MOB免费短信平台
JWT 进行身份校验
终端执行下载JWT
go get -u github.com/dgrijalva/jwt-Go
对jwt的使用做封装,pkg/util/jwt.go
package util
import (
"api/pkg/setting"
"github.com/dgrijalva/jwt-go"
"time"
)
var jwtSecret = []byte(setting.AppSetting.JwtSecret) //jwt密钥
/*在jwt中添加的自定义用户信息,有用户的ID和手机号,也可以加一些其它信息*/
type Claims struct {
ID uint
//Mobile string //这里没有使用手机号。因为token字符串可以被解析。。。。
jwt.StandardClaims
}
/*生成Token*/
func GeterateToken(id uint,mobile string) (string,error) {
nowTime := time.Now() //当前时间
expireTime := nowTime.Add(setting.AppSetting.JwtExpireTime * time.Hour) //过期时间,为了测试这里是3小时后过期
//设置自定义荷载
claims := Claims{
id,
//mobile,
jwt.StandardClaims{
ExpiresAt:expireTime.Unix(),
Issuer : "blog",
},
}
tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
token, err := tokenClaims.SignedString(jwtSecret) //该方法内部生成签名字符串,再用于获取完整、已签名的token
return token, err
}
/*校验和解析token*/
func ParseToken(token string) (*Claims, error) {
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if tokenClaims != nil {
if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
return claims, nil
}
}
return nil, err
}
在登录/注册接口中, 返给客户端生成的token routers/v1/user.go
...
//登录,注册接口
func UserStore(c *gin.Context) {
...
//生成token
token,err := util.GeterateToken(user.ID,user.Mobile)
if err != nil {
util.ResponseWithJson(e.ERROR, "创建token失败", c)
return
}
util.ResponseWithJson(e.SUCCESS,gin.H{
"User":user,
"Token":token,
},c)
}
...
这样就返还给客户端token了,客户端获取token后保存,在需要使用token的接口中,在请求header中加入token即可。