Golang HTTP处理函数

时间:2018-02-28 09:13:43

标签: go httphandler

我看到一些http处理程序函数声明是多种多样的。 我发现其中两个是标准函数,一个在处理程序中返回匿名函数。 例如:

使用标准方式:

func helloworld(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello World")
}

这是为http api声明处理程序的最直接方式。

另一种方法是在处理函数中使用anonym / closure函数:

func helloworld2() http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        fmt.Fprintln(w, "Hello World")
    })
}

有什么区别和好处?什么时候使用其中之一?什么是最佳做法?

3 个答案:

答案 0 :(得分:6)

模式

func Middleware(next http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // Do something
    next.ServeHTTP(w, r)
  })
}

经常用于构建像

这样的中间件链
http.Handle("/", middlewareOne(middlewareTwo(finalHandler)))

答案 1 :(得分:5)

通过返回闭包,返回匿名函数是处理需要其他参数的处理程序的唯一方法。例如:

func fooHandler(db *someDatabase) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // do something with `db` variable
    }
}

否则,这些方法之间通常没有实际差异。人们可以选择普遍使用匿名函数来保持一致性。

答案 2 :(得分:2)

关于结构返回匿名函数的最流行信息之一是Mat Ryer How I write HTTP services after eight years

的博客文章。

我肯定会在这里提供他的文章的一些引言:

...处理程序函数实际上并不处理请求,它们返回的函数可以处理请求。这为我们的处理程序可以在其中提供一个封闭环境:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutterlearningapp/colors.dart';

class HomeScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _HomeScreen();
  }
}

class _HomeScreen extends State<HomeScreen> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Material(
        child: Scaffold(
      appBar: AppBar(
        title: Text("Demo Scroll"),
      ),
      body: Container(
        height: double.infinity,
        width: double.infinity,
        color: Colors.white,
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("Shoring"),
              onPressed: (){
                setState(() {
                  fields.sort((a, b) => a.rating.compareTo(b.rating));

                });

              },


            ),
            Expanded(
              child:  ListView.builder
                (
                  itemCount: fields.length,
                  itemBuilder: (BuildContext ctxt, int index) {
                    return ListTile(
                      title: new Text("Rating #${fields[index].rating}"),
                      subtitle: new Text(fields[index].title),
                    );
                  }
              ) ,
            )


          ],

        ),
      ),
    ));
  }
}

class Fields {
  final String title;
  final int rating;

  Fields(this.title, this.rating);
}

List<Fields> fields = [
  new Fields(
    'Two',
    2,
  ),
  new Fields(
    'One',
    1,
  ),
  new Fields(
    'DEFAULT CATEGORY',
    5,
  ),
  new Fields(
    'Three',
    3,
  ),
  new Fields(
    'Four',
    4,
  ),
];

prepareThing仅被调用一次,因此您可以使用它一次执行 每个处理程序初始化,然后在处理程序中使用它。

也是

如果端点具有自己的请求和响应类型,通常它们仅对特定处理程序有用。如果是这样,您可以在函数中定义它们。

func (s *server) handleSomething() http.HandlerFunc {
    thing := prepareThing()
    return func(w http.ResponseWriter, r *http.Request) {
        // use thing        
    }
}

}

在实践中,编写RESTy API时,您有以资源命名的处理程序,例如您拥有/ maps资源和适当的处理程序结构mapsHandler,并在其中注入了相关性(存储库,包含一些业务逻辑的服务,记录器)。但是有时您还需要专门为每个句柄传递一个附加的依赖项,突然意识到处理程序具有严格的签名,因此您应该包装它。然后,您会遇到类似

func (s *server) handleSomething() http.HandlerFunc {

  // you have these handy structs always visible to your handler and eyes 
  // and invisible to code that don't use them

  type request struct {
    Name string
  }

  type response struct {
    Greeting string `json:"greeting"`
  }

  return func(w http.ResponseWriter, r *http.Request) {
    // decode into request struct
    // validate
    // call business-logic 
    // encode response from business-logic into response struct 

  }

使您的临时依赖对处理程序可见。

希望有帮助!