kratos V2 框架设计自定义返回结构以及跨域

在Web开发业务中,通常使用业务状态码来判断业务状态,不使用http状态码。在kratos V2中控制 http 的返回值,在 http 返回值外面返回自己熟悉的结构

在NewHTTPServer中进行使用(替换返回结构)

// 过滤,所包含的路由不会经过jwt权限验证
func NewSkipRoutersMatcher() selector.MatchFunc {
	//完整 todo /包名.服务名/方法名
	smn := "/intelligent_analysis.v1.Analysis" //包名.服务名
	skipRouters := make(map[string]struct{})
	skipRouters[smn+"/Login"] = struct{}{} //通过operation匹配规则,并不是http本身的路由  /包名.服务名/方法名
	skipRouters[smn+"/Platform"] = struct{}{}
	return func(ctx context.Context, operation string) bool {
		if _, ok := skipRouters[operation]; ok {
			return false
		}
		return true
	}
}


/=================================================================
// NewHTTPServer new a HTTP server.
func NewHTTPServer(c *conf.Server, jwtc *conf.JWT, cloud_platform *service.AnalysisService, logger log.Logger) *http.Server {
	var opts = []http.ServerOption{
		//自定义返回错误结构
		http.ErrorEncoder(errorEncoder), //替代默认的错误结构
        //自定义返回数据结构
		http.ResponseEncoder(responseEncoder),  //替代默认的返回数据结构
		http.Middleware(
			recovery.Recovery(),
			selector.Server(auth.JWTAuth(jwtc.Secret)).Match(NewSkipRoutersMatcher()).Build(),
			logging.Server(logger),
        //cors.MiddlewareCors(), //跨域中间件
		),       
		http.Filter(
		  //跨域  推荐
			handlers.CORS(
				handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}),
				handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS", "DELETE"}),
				handlers.AllowedOrigins([]string{"*"}),
			)),
	}
	if c.Http.Network != "" {
		opts = append(opts, http.Network(c.Http.Network))
	}
	if c.Http.Addr != "" {
		opts = append(opts, http.Address(c.Http.Addr))
	}
	if c.Http.Timeout != nil {
		opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
	}
	srv := http.NewServer(opts...)
	v1.RegisterAnalysisHTTPServer(srv, cloud_platform)
	return srv
}

返回结构的具体实现如下:

type ResponseError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (e *ResponseError) Error() string {
	return fmt.Sprintf("ResponseError: %d", e.Code)
}

func NewResponseError(code int, message string) *ResponseError {
	return &ResponseError{
		Code:    code,
		Message: message,
	}
}

// FromError try to convert an error to *ResponseError.
func FromError(err error) *ResponseError {
	if err == nil {
		return nil
	}
	if se := new(errors.Error); errors.As(err, &se) {
		return NewResponseError(int(se.Code), se.Message)
	}
	return &ResponseError{Code: 500}
}

//自定义返回error
func errorEncoder(w nethttp.ResponseWriter, r *nethttp.Request, err error) {
	se := errors.FromError(err)
	codec, _ := http.CodecForRequest(r, "Accept")
	body, err := codec.Marshal(se)
	if err != nil {
		w.WriteHeader(500)
		return
	}
	w.Header().Set("Content-Type", "application/"+codec.Name())
	//w.WriteHeader(se.Code) //写入状态码中,在此不需要
	_, _ = w.Write(body)
}

-----------------------------------
//自定义返回数据结构
func responseEncoder(w http.ResponseWriter, r *http.Request, data interface{}) error {
	type Response struct {
		Code    int         `json:"code"`
		Data    interface{} `json:"data"`
		Message string      `json:"message"`
	}
///************************************
    //Code与Message直接写固定值
	res := &Response{
		  Code:    200,
		  Data:    data,
		  Message: "成功",
	}
///**********************************/
	//codec := encoding.GetCodec("json")  //这两行的目的就是为了把要返回的数据进行序列化
	//msRes, err := codec.Marshal(res)    //也可采用上面函数errorEncoder中的方法或者下面的直接JSON序列化
	msRes, err := json.Marshal(res)
	if err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(msRes)
	return nil
}

跨域中间件

        前端需要将withCredentials设置为 false

// MiddlewareCors 设置跨域请求头
func MiddlewareCors() middleware.Middleware {
	return func(handler middleware.Handler) middleware.Handler {
		return func(ctx context.Context, req interface{}) (interface{}, error) {
			if tr, ok := transport.FromServerContext(ctx); ok {
                //tr.ReplyHeader().Set("Access-Control-Allow-Origin", "*")
				//tr.ReplyHeader().Set("Access-Control-Allow-Credentials", "true")
				//tr.ReplyHeader().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")
				//tr.ReplyHeader().Set("Access-Control-Allow-Headers", "Content-Type,X-Requested-With,Authorization")
				if ht, ok := tr.(http.Transporter); ok {
					origin := ht.RequestHeader().Get("Origin")
					method := ht.Request().Method
					if method == nethttp.MethodOptions {
						ht.ReplyHeader().Set("Access-Control-Allow-Origin", origin) //origin也可以替换为 *
						ht.ReplyHeader().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")
						ht.ReplyHeader().Set("Access-Control-Allow-Credentials", "true")
						ht.ReplyHeader().Set("Access-Control-Allow-Headers", "Content-Type, X-Requested-With,Authorization")
					}					
				}
			}
			return handler(ctx, req)
		}
	}
}

//func NotFoundHandler(res nethttp.ResponseWriter, req *nethttp.Request) {
//	res.Header().Set("Access-Control-Allow-Origin", "*")
//	res.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,PATCH,DELETE")
//	res.Header().Set("Access-Control-Allow-Credentials", "true")
//	res.Header().Set("Access-Control-Allow-Headers", "Content-Type,"+
//		"X-Requested-With,Access-Control-Allow-Credentials,User-Agent,Content-Length,Authorization")
//
//	log.Info("NotFoundHandler")
//
//	errCode := errors.NotFound("page not found", "page not found")
//	buffer, _ := json.Marshal(errCode)
//	//res.WriteHeader(400)
//	_, _ = res.Write(buffer)
//}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!根据您的描述,您希望在现有的Kratos + Ent + Redis框架中实现登录功能,并生成动态验证码。以下是一个简单的实现示例: 首先,安装必要的依赖包: ```shell go get github.com/go-kratos/kratos go get github.com/go-kratos/ent go get github.com/go-redis/redis/v8 go get github.com/gin-gonic/gin ``` 然后,创建一个名为`main.go`的文件,并添加以下代码: ```go package main import ( "context" "fmt" "math/rand" "net/http" "strconv" "time" "github.com/go-kratos/ent/entc" "github.com/go-kratos/ent/entc/sql" "github.com/go-kratos/ent/examples/ent_user" "github.com/go-redis/redis/v8" "github.com/gin-gonic/gin" ) func main() { // 初始化数据库连接 client, err := sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/ent_demo") if err != nil { fmt.Printf("failed opening connection to mysql: %v", err) return } defer client.Close() // 初始化Redis连接 redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, }) // 初始化Kratos服务 router := gin.Default() // 登录接口 router.POST("/login", func(c *gin.Context) { // 获取用户输入的手机号和密码 phone := c.PostForm("phone") password := c.PostForm("password") // 从数据库中查询用户信息 user, err := client.User.Query().Where(ent_user.Phone(phone)).Only(context.Background()) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "用户不存在"}) return } // 验证密码是否正确 if user.Password != password { c.JSON(http.StatusBadRequest, gin.H{"error": "密码错误"}) return } // 生成动态验证码 code := generateCode() // 将验证码存储到Redis中,设置有效期为5分钟 err = redisClient.Set(context.Background(), phone, code, 5*time.Minute).Err() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "验证码生成失败"}) return } // 发送验证码到用户手机(这里省略具体的发送逻辑) c.JSON(http.StatusOK, gin.H{"message": "验证码已发送,请输入验证码进行登录"}) }) // 验证码登录接口 router.POST("/login/verify", func(c *gin.Context) { // 获取用户输入的手机号和验证码 phone := c.PostForm("phone") code := c.PostForm("code") // 从Redis中获取验证码 storedCode, err := redisClient.Get(context.Background(), phone).Result() if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "验证码已过期"}) return } // 验证验证码是否匹配 if code != storedCode { c.JSON(http.StatusBadRequest, gin.H{"error": "验证码错误"}) return } // TODO: 验证通过,进行登录操作 c.JSON(http.StatusOK, gin.H{"message": "登录成功"}) }) // 启动服务 router.Run(":8080") } // 生成6位随机数字验证码 func generateCode() string { rand.Seed(time.Now().UnixNano()) return strconv.Itoa(rand.Intn(900000) + 100000) } ``` 请确保您已正确配置数据库和Redis连接,并根据您的实际需求进行相应的修改。以上示例代码仅供参考,具体实现可能需要根据您的业务逻辑进行调整。祝您成功实现登录生成动态验证码功能!如果您有任何问题,请随时提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值