如何在Go中编写通用处理程序?

huangapple go评论81阅读模式
英文:

How to write a generic handler in Go?

问题

我需要为一个REST API创建一个HTTP处理程序。

这个REST API有许多不同的对象,这些对象存储在一个数据库中(在我的情况下是MongoDB)。

目前,我需要为每个对象的每个操作编写一个处理程序。

我想找到一种像使用泛型一样的方法,可以编写一个通用的处理程序,可以处理特定的操作,但适用于任何类型的对象(因为在大多数情况下,它基本上只是CRUD操作)。

我该如何做到这一点?

以下是我想要转换为通用处理程序的示例:

func IngredientIndex(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    db := r.Context().Value("Database").(*mgo.Database)
    ingredients := []data.Ingredient{}
    err := db.C("ingredients").Find(bson.M{}).All(&ingredients)
    if err != nil {
        panic(err)
    }
    json.NewEncoder(w).Encode(ingredients)
}

func IngredientGet(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    vars := mux.Vars(r)
    logger := r.Context().Value("Logger").(zap.Logger)
    db := r.Context().Value("Database").(*mgo.Database)
    ingredient := data.Ingredient{}
    err := db.C("ingredients").Find(bson.M{"_id": bson.ObjectIdHex(vars["id"])}).One(&ingredient)
    if err != nil {
        w.WriteHeader(404)
        logger.Info("Fail to find entity", zap.Error(err))
    } else {
        json.NewEncoder(w).Encode(ingredient)
    }
}

基本上,我需要一个像这样的处理程序(这是我尝试但不起作用的示例):

func ObjectIndex(collection string, container interface{}) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json; charset=UTF-8")
        db := r.Context().Value("Database").(*mgo.Database)
        objects := container
        err := db.C(collection).Find(bson.M{}).All(&objects)
        if err != nil {
            panic(err)
        }
        json.NewEncoder(w).Encode(objects)
    }
}

我该如何做到这一点?

英文:

I need to create a HTTP Handler for a REST API.

This REST API have many different objects that are stored in a database (MongoDB in my case).

Currently, I need to write one handler per action per object.

I would like to find a way like it's possible with Generics to write a generic handler that could handle a specific action but for any kind of object (As basically it's just CRUD in most of the case)

How can I do this ?

Here is examples of Handlers I would like to transform into a generic one :

func IngredientIndex(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	db := r.Context().Value("Database").(*mgo.Database)
	ingredients := []data.Ingredient{}
	err := db.C("ingredients").Find(bson.M{}).All(&ingredients)
	if err != nil {
		panic(err)
	}
	json.NewEncoder(w).Encode(ingredients)
}

func IngredientGet(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	vars := mux.Vars(r)
	logger := r.Context().Value("Logger").(zap.Logger)
	db := r.Context().Value("Database").(*mgo.Database)
	ingredient := data.Ingredient{}
	err := db.C("ingredients").Find(bson.M{"_id": bson.ObjectIdHex(vars["id"])}).One(&ingredient)
	if err != nil {
		w.WriteHeader(404)
		logger.Info("Fail to find entity", zap.Error(err))
	} else {
		json.NewEncoder(w).Encode(ingredient)
	}
}

Basically I would need a handler like this (This is my tentative that doesn't work) :

func ObjectIndex(collection string, container interface{}) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		db := r.Context().Value("Database").(*mgo.Database)
		objects := container
		err := db.C(collection).Find(bson.M{}).All(&objects)
		if err != nil {
			panic(err)
		}
		json.NewEncoder(w).Encode(objects)
	}

How can I do that ?

答案1

得分: 3

使用reflect包在每次调用时创建一个新的容器:

func ObjectIndex(collection string, container interface{}) func(http.ResponseWriter, *http.Request) {
  t := reflect.TypeOf(container)
  return func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    db := r.Context().Value("Database").(*mgo.Database)
    objects := reflect.New(t).Interface()
    err := db.C(collection).Find(bson.M{}).All(objects)
    if err != nil {
        panic(err)
    }
    json.NewEncoder(w).Encode(objects)
  }
}

像这样调用它:

h := ObjectIndex("ingredients", data.Ingredient{})

假设data.Ingredient是一个切片类型。

英文:

Use the reflect package to create a new container on each invocation:

func ObjectIndex(collection string, container interface{}) func(http.ResponseWriter, *http.Request) {
  t := reflect.TypeOf(container)
  return func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    db := r.Context().Value("Database").(*mgo.Database)
    objects := reflect.New(t).Interface()
    err := db.C(collection).Find(bson.M{}).All(objects)
    if err != nil {
        panic(err)
    }
    json.NewEncoder(w).Encode(objects)
}

Call it like this:

h := ObjectIndex("ingredients", data.Ingredient{})

assuming that data.Indgredient is a slice type.

huangapple
  • 本文由 发表于 2016年12月16日 22:46:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/41186856.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定