使用结构体建模服务器端点时遇到问题 - Golang求助

使用结构体建模服务器端点时遇到问题 - Golang求助 你认为这是一种合理的服务器端点建模方式吗?基本上,我有一个通用的端点,但希望根据负载进行特化。这对我来说似乎有点冗长,尤其是在访问结构体字段时。

查看示例代码

package main

import (
	"fmt"
)

type Endpoint struct {
	NeedsAuthentication bool
	Uri                 string
}
type RefreshEndpoint struct {
	Endpoint
	Payload RefreshPayload
}
type RefreshPayload struct {
	RefreshToken string
}

func main() {
	refreshEndpoint := RefreshEndpoint{Endpoint: Endpoint{NeedsAuthentication: true, Uri: "/foo"}, Payload: RefreshPayload{RefreshToken: "xxx"}}
	fmt.Printf("The uri is: %s, the access token is: %s", refreshEndpoint.Endpoint.Uri, refreshEndpoint.Payload.RefreshToken)
}

解决 refreshEndpoint.Endpoint.Uri 这种奇怪的间接访问的一种方法是在 RefreshEndpoint 上定义一个名为 Uri 的方法:

package main

import (
	"fmt"
)

type Endpoint struct {
	NeedsAuthentication bool
	Uri                 string
}
type RefreshEndpoint struct {
	Endpoint
	Payload RefreshPayload
}
type RefreshPayload struct {
	RefreshToken string
}
func (e RefreshEndpoint) Uri() string {
	return e.Endpoint.Uri
}

func main() {
	refreshEndpoint := RefreshEndpoint{Endpoint: Endpoint{NeedsAuthentication: true, Uri: "/foo"}, Payload: RefreshPayload{RefreshToken: "xxx"}}
	fmt.Printf("The uri is: %s, the access token is: %s", refreshEndpoint.Uri(), refreshEndpoint.Payload.RefreshToken)
}

这样我们就可以像 refreshEndpoint.Uri() 这样访问 URI。

但问题依然存在。如何优雅地建模一个通用的 Endpoint,使其能够根据端点类型接收不同类型的负载?


更多关于使用结构体建模服务器端点时遇到问题 - Golang求助的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

我发现,由于 RefreshEndpoint 嵌入了 Uri,我可以直接访问它。我之前没有意识到这一点。

更多关于使用结构体建模服务器端点时遇到问题 - Golang求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个合理的建模方式,但可以通过接口和泛型来改进。以下是两种更优雅的解决方案:

方案一:使用接口和类型断言

package main

import (
	"fmt"
)

type Endpoint struct {
	NeedsAuthentication bool
	Uri                 string
	Payload             interface{}
}

type RefreshPayload struct {
	RefreshToken string
}

type LoginPayload struct {
	Username string
	Password string
}

func (e Endpoint) GetRefreshPayload() (RefreshPayload, bool) {
	payload, ok := e.Payload.(RefreshPayload)
	return payload, ok
}

func (e Endpoint) GetLoginPayload() (LoginPayload, bool) {
	payload, ok := e.Payload.(LoginPayload)
	return payload, ok
}

func main() {
	refreshEndpoint := Endpoint{
		NeedsAuthentication: false,
		Uri:                 "/refresh",
		Payload: RefreshPayload{RefreshToken: "xxx"},
	}
	
	loginEndpoint := Endpoint{
		NeedsAuthentication: false,
		Uri:                 "/login",
		Payload: LoginPayload{Username: "user", Password: "pass"},
	}
	
	if payload, ok := refreshEndpoint.GetRefreshPayload(); ok {
		fmt.Printf("URI: %s, RefreshToken: %s\n", refreshEndpoint.Uri, payload.RefreshToken)
	}
	
	if payload, ok := loginEndpoint.GetLoginPayload(); ok {
		fmt.Printf("URI: %s, Username: %s\n", loginEndpoint.Uri, payload.Username)
	}
}

方案二:使用Go 1.18+泛型(推荐)

package main

import (
	"fmt"
)

type Endpoint[T any] struct {
	NeedsAuthentication bool
	Uri                 string
	Payload             T
}

type RefreshPayload struct {
	RefreshToken string
}

type LoginPayload struct {
	Username string
	Password string
}

// 泛型处理器示例
func HandleEndpoint[T any](endpoint Endpoint[T]) {
	fmt.Printf("处理端点: %s\n", endpoint.Uri)
	fmt.Printf("需要认证: %v\n", endpoint.NeedsAuthentication)
	
	// 可以直接访问特定类型的Payload
	_ = endpoint.Payload
}

// 类型特定的处理
func HandleRefresh(endpoint Endpoint[RefreshPayload]) {
	fmt.Printf("刷新令牌: %s\n", endpoint.Payload.RefreshToken)
}

func HandleLogin(endpoint Endpoint[LoginPayload]) {
	fmt.Printf("用户名: %s\n", endpoint.Payload.Username)
}

func main() {
	refreshEndpoint := Endpoint[RefreshPayload]{
		NeedsAuthentication: false,
		Uri:                 "/refresh",
		Payload: RefreshPayload{RefreshToken: "xxx"},
	}
	
	loginEndpoint := Endpoint[LoginPayload]{
		NeedsAuthentication: false,
		Uri:                 "/login",
		Payload: LoginPayload{Username: "user", Password: "pass"},
	}
	
	// 直接访问,无需嵌套
	fmt.Printf("刷新端点URI: %s, 令牌: %s\n", 
		refreshEndpoint.Uri, 
		refreshEndpoint.Payload.RefreshToken)
	
	fmt.Printf("登录端点URI: %s, 用户: %s\n", 
		loginEndpoint.Uri, 
		loginEndpoint.Payload.Username)
	
	// 使用泛型处理器
	HandleEndpoint(refreshEndpoint)
	HandleEndpoint(loginEndpoint)
	
	// 使用类型特定处理器
	HandleRefresh(refreshEndpoint)
	HandleLogin(loginEndpoint)
}

方案三:组合方法提升

package main

import (
	"fmt"
)

type Endpoint struct {
	NeedsAuthentication bool
	Uri                 string
}

type RefreshEndpoint struct {
	Endpoint
	Payload RefreshPayload
}

type LoginEndpoint struct {
	Endpoint
	Payload LoginPayload
}

type RefreshPayload struct {
	RefreshToken string
}

type LoginPayload struct {
	Username string
	Password string
}

// 为所有嵌入Endpoint的结构体提供统一访问
func (e Endpoint) GetURI() string {
	return e.Uri
}

func (e Endpoint) NeedsAuth() bool {
	return e.NeedsAuthentication
}

// 类型特定的方法
func (e RefreshEndpoint) GetRefreshToken() string {
	return e.Payload.RefreshToken
}

func (e LoginEndpoint) GetCredentials() (string, string) {
	return e.Payload.Username, e.Payload.Password
}

func main() {
	refreshEndpoint := RefreshEndpoint{
		Endpoint: Endpoint{NeedsAuthentication: false, Uri: "/refresh"},
		Payload:  RefreshPayload{RefreshToken: "xxx"},
	}
	
	loginEndpoint := LoginEndpoint{
		Endpoint: Endpoint{NeedsAuthentication: false, Uri: "/login"},
		Payload:  LoginPayload{Username: "user", Password: "pass"},
	}
	
	// 简洁的访问方式
	fmt.Printf("刷新端点: %s, 令牌: %s\n", 
		refreshEndpoint.GetURI(), 
		refreshEndpoint.GetRefreshToken())
	
	fmt.Printf("登录端点: %s, 用户: %s\n", 
		loginEndpoint.GetURI(), 
		loginEndpoint.Payload.Username)
	
	// 统一处理不同端点的示例
	endpoints := []interface{}{refreshEndpoint, loginEndpoint}
	for _, ep := range endpoints {
		switch e := ep.(type) {
		case RefreshEndpoint:
			fmt.Printf("处理刷新端点: %s\n", e.GetRefreshToken())
		case LoginEndpoint:
			user, _ := e.GetCredentials()
			fmt.Printf("处理登录端点: %s\n", user)
		}
	}
}

泛型方案最简洁,它提供了类型安全且无需类型断言。如果使用Go 1.18+,推荐方案二;如果使用旧版本,方案三的组合方法提升是不错的选择。

回到顶部