package http_api

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"

    "github.com/julienschmidt/httprouter"
    "github.com/nsqio/nsq/internal/app"
)

type Decorator func(APIHandler) APIHandler

type APIHandler func(http.ResponseWriter, *http.Request, httprouter.Params) (interface{}, error)

type Err struct {
    Code int
    Text string
}

func (e Err) Error() string {
    return e.Text
}

func acceptVersion(req *http.Request) int {
    if req.Header.Get("accept") == "application/vnd.nsq; version=1.0" {
        return 1
    }

    return 0
}

func PlainText(f APIHandler) APIHandler {
    return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
        code := 200
        data, err := f(w, req, ps)
        if err != nil {
            code = err.(Err).Code
            data = err.Error()
        }
        switch d := data.(type) {
        case string:
            w.WriteHeader(code)
            io.WriteString(w, d)
        case []byte:
            w.WriteHeader(code)
            w.Write(d)
        default:
            panic(fmt.Sprintf("unknown response type %T", data))
        }
        return nil, nil
    }
}

func NegotiateVersion(f APIHandler) APIHandler {
    return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
        data, err := f(w, req, ps)
        if err != nil {
            if acceptVersion(req) == 1 {
                RespondV1(w, err.(Err).Code, err)
            } else {
                // this handler always returns 500 for backwards compatibility
                Respond(w, 500, err.Error(), nil)
            }
            return nil, nil
        }
        if acceptVersion(req) == 1 {
            RespondV1(w, 200, data)
        } else {
            Respond(w, 200, "OK", data)
        }
        return nil, nil
    }
}

func V1(f APIHandler) APIHandler {
    return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
        data, err := f(w, req, ps)
        if err != nil {
            RespondV1(w, err.(Err).Code, err)
            return nil, nil
        }
        RespondV1(w, 200, data)
        return nil, nil
    }
}

func Respond(w http.ResponseWriter, statusCode int, statusTxt string, data interface{}) {
    var response []byte
    var err error

    switch data.(type) {
    case string:
        response = []byte(data.(string))
    case []byte:
        response = data.([]byte)
    default:
        w.Header().Set("Content-Type", "application/json; charset=utf-8")
        response, err = json.Marshal(struct {
            StatusCode int         `json:"status_code"`
            StatusTxt  string      `json:"status_txt"`
            Data       interface{} `json:"data"`
        }{
            statusCode,
            statusTxt,
            data,
        })
        if err != nil {
            response = []byte(fmt.Sprintf(`{"status_code":500, "status_txt":"%s", "data":null}`, err))
        }
    }

    w.WriteHeader(statusCode)
    w.Write(response)
}

func RespondV1(w http.ResponseWriter, code int, data interface{}) {
    var response []byte
    var err error
    var isJSON bool

    if code == 200 {
        switch data.(type) {
        case string:
            response = []byte(data.(string))
        case []byte:
            response = data.([]byte)
        case nil:
            response = []byte{}
        default:
            isJSON = true
            response, err = json.Marshal(data)
            if err != nil {
                code = 500
                data = err
            }
        }
    }

    if code != 200 {
        isJSON = true
        response = []byte(fmt.Sprintf(`{"message":"%s"}`, data))
    }

    if isJSON {
        w.Header().Set("Content-Type", "application/json; charset=utf-8")
    }
    w.Header().Set("X-NSQ-Content-Type", "nsq; version=1.0")
    w.WriteHeader(code)
    w.Write(response)
}

func Decorate(f APIHandler, ds ...Decorator) httprouter.Handle {
    decorated := f
    for _, decorate := range ds {
        decorated = decorate(decorated)
    }
    return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
        decorated(w, req, ps)
    }
}

func Log(l app.Logger) Decorator {
    return func(f APIHandler) APIHandler {
        return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
            start := time.Now()
            response, err := f(w, req, ps)
            elapsed := time.Since(start)
            status := 200
            if e, ok := err.(Err); ok {
                status = e.Code
            }
            l.Output(2, fmt.Sprintf("%d %s %s (%s) %s",
                status, req.Method, req.URL.RequestURI(), req.RemoteAddr, elapsed))
            return response, err
        }
    }
}

func LogPanicHandler(l app.Logger) func(w http.ResponseWriter, req *http.Request, p interface{}) {
    return func(w http.ResponseWriter, req *http.Request, p interface{}) {
        l.Output(2, fmt.Sprintf("ERROR: panic in HTTP handler - %s", p))
        Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
            return nil, Err{500, "INTERNAL_ERROR"}
        }, Log(l), V1)(w, req, nil)
    }
}

func LogNotFoundHandler(l app.Logger) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
            return nil, Err{404, "NOT_FOUND"}
        }, Log(l), V1)(w, req, nil)
    })
}

func LogMethodNotAllowedHandler(l app.Logger) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
            return nil, Err{405, "METHOD_NOT_ALLOWED"}
        }, Log(l), V1)(w, req, nil)
    })
}

api_response.go的更多相关文章

  1. NSQ之粗读浅谈

    回顾: 以前一直是C++开发(客户端),最近听同事讲go语言不错,随后便决定先从go语法开始投向go的怀抱.由于历史原因学习go语法时,用了半天的时间看完了菜鸟教程上相关资料,后来又看了易百教程上的一 ...

  2. go语言nsq源码解读八 http.go、http_server.go

    这篇讲另两个文件http.go.http_server.go,这两个文件和第六讲go语言nsq源码解读六 tcp.go.tcp_server.go里的两个文件是相对应的.那两个文件用于处理tcp请求, ...

  3. 02: djangorestframework使用

    1.1 djangorestframework登录.认证和权限 1.认证与权限相关模块 # -*- coding: utf-8 -*- from django.utils import six fro ...

  4. 用PHP编写一个APP的API

    第一部分,通信接口的实现 标签(空格分隔): PHP 手机后台 api 通信接口 Andy PHP开发手机API时,一般返回XML或JSON数据类型的数据,除了要返回从源数据(程序本身需要的数据)外还 ...

  5. 在python中配置tornado服务

    import tornado.httpserver import tornado.options import tornado.web from tornado.options import defi ...

  6. api响应类

    接口开发响应类封装 class response{ /* * 封通信接口数据 * @param integer $code 状态码 * @param string $message 状态信息 * @p ...

  7. 如何使用k3OS和Argo进行自动化边缘部署?

    本文转自边缘计算k3s社区 前 言 随着Kubernetes生态系统的发展,新的技术正在被开发出来,以实现更广泛的应用和用例.边缘计算的发展推动了对其中一些技术的需求,以实现将Kubernetes部署 ...

  8. Python+Pytest+Allure+Git+Jenkins接口自动化框架

    Python+Pytest+Allure+Git+Jenkins接口自动化框架 一.接口基础 接口测试是对系统和组件之间的接口进行测试,主要是效验数据的交换,传递和控制管理过程,以及相互逻辑依赖关系. ...

  9. 登录、认证、token处理、前台cookie存储token

    免费课程相关表设计 models的设计 from django.contrib.contenttypes.fields import GenericRelation class Course(mode ...

随机推荐

  1. IIS服务器如何抗住高并发的客户端访问

    今天被问到一个问题,如果你在阿里云上部署了一个IIS服务器,此时如果有成千上万的客户端来访问,你将如何设计?我东扯扯西谈谈,说加个线程池来处理,在加个请求队列.当时觉得说的没有问题,现在想想,服务器自 ...

  2. 一种WPF在后台线程更新UI界面的简便方法

    WPF框架规定只有UI线程(主线程)可以更新界面,所有其他后台线程无法直接更新界面.幸好,WPF提供的SynchronizationContext类以及C#的Lambda表达式提供了一种方便的解决方法 ...

  3. 重定向和servlet生命周期

    重定向(1)什么是重定向服务器通知浏览器向一个新的地址发送请求.注:可以发送一个302状态码和一个Location消息头.(该消息头包含了一个地址,称之为重定向地址),浏览器收到之后,会立即向重定向地 ...

  4. Jhipster 学习(一)jhipster构建项目

    如何安装jhipster 第一步:下载jdk  自己安装的1.8版本  (安装.环境变量配置略) 第二步:1.下载Eclipse (luna版 eclipse-4.4.1) 第三步:下载maven ( ...

  5. 如何在asp.net mvc 中使用Autofac 控制反转(Ioc)

    前言 最近看了一些关于Ioc方面的开源项目,里面的类跳来转去,看的迷迷糊糊的.项目里根本不需要这么“复杂的”设计,只需简单完成Ico,达到解耦的目的,并且能高效的完成项目.于是参考autofac的官网 ...

  6. OpenStack初识

    一.它可以用来做什么? 想认识一个事物,必须先弄明白它是什么,能干什么.首先说一下,openstack是一个搭建云平台的一个解决方案,说他不是个软件,但是我觉得说是一个软件,能够让初学者更容易接受和理 ...

  7. JS(原生js和jq方式)获取元素属性(自定义属性),删除属性(自定义属性)

    JS(原生js和jq方式)获取元素属性(自定义属性),删除属性(自定义属性) 以下内容: 一.获取元素的属性 二.设置元素的属性 三.删除元素的属性 一.获取元素的属性 1-原生JS 获取属性 .ge ...

  8. 初探Apache Beam

    文章作者:luxianghao 文章来源:http://www.cnblogs.com/luxianghao/p/9010748.html  转载请注明,谢谢合作. 免责声明:文章内容仅代表个人观点, ...

  9. django-团队简介的网页

    团队简介的网页,是使用Django完成的 关于Django的教程网址:http://www.runoob.com/django/django-tutorial.html 小组作业成果如下:

  10. 《T-SQL查询》读书笔记Part 3.索引的基本知识

    索引优化是查询优化中最重要的一部分,索引是一种用于排序和搜索的结构,在查找数据时索引可以减少对I/O的需要:当计划中的某些元素需要或是可以利用经过排序的数据时,也会减少对排序的需要.某些方面的优化可以 ...