Как получить http.ResponseWriter и http.Request в контроллере Revel - PullRequest
0 голосов
/ 08 марта 2019

Я пытаюсь реализовать oauth-сервер и используемому пакету требуются полные типы http.ResponseWriter и http.Request.

c.Response не содержит все методы, которые использует http.ResponseWriter, а c.Request выдает ошибки несовместимого типа.

Как получить http.ResponseWriter и http.Request в контроллере Revel?

type client struct {
    ClientId string
    ClientSecret string
}
type App struct {
    *revel.Controller
}

func (c App) TokenRequest() {

    r := c.Request
    w := c.Response

    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        panic(err)
    }
    log.Println(string(body))

    var cli client
    err = json.Unmarshal(body, &cli)

    if err != nil {
        panic(err)
    }
    log.Println(cli.ClientId)

    err = OauthSrv.HandleTokenRequest(w, r)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

1 Ответ

0 голосов
/ 08 марта 2019

Предупреждение

Мне вообще не нравятся фреймворки, такие как Revel in Go, по причинам, которые я надеюсь продемонстрировать на этой странице.Моей первой рекомендацией было бы внимательно изучить, что вы на самом деле получаете от Revel, что заслуживает использования такого тяжелого уровня абстракции, и если это действительно так ценно, вы можете задавать вопросы в другом направлении, например, какможно заставить OauthSrv работать в настроенной экосистеме Revel.

Использование контроллера Revel в качестве ResponseWriter

Чтобы что-то было http.ResponseWriter, ему просто нужно иметь эти методы.

Заголовок

Вам необходим метод с именем Header(), который возвращает http.Header, который вы можете построить из любого map[string][]string.Revel предоставляет аналогичную функциональность, но через несколько уровней абстракции.Вам нужно будет их распутать:

  1. c.Response - это *Response, поэтому в нем есть поле с именем Out, содержащее OutResponse.
  2. An OutResponseимеет метод Header(), но он не возвращает http.Header.Вместо этого он возвращает *RevelHeader.
  3. A *RevelHeader имеет метод GetAll(key string) []string, который очень похож на API, уже предоставленный встроенным типом map, но не совсемтот же самый.Таким образом, вам нужно будет копировать возвращаемые значения в новую карту каждый раз, когда вызывается Header(), чтобы полностью удовлетворить требования сигнатуры функции.
  4. Кроме того, GetAll() требует, чтобы вы знали имя ключаВы заинтересованы в этом, и *RevelHeader сам по себе не позволяет найти доступные ключи. На данный момент мы можем полагаться на тот факт, что текущая реализация имеет только одно поле, а ServerHeader, что предоставляет , обеспечивает метод GetKeys() []string.

Собрав все это вместе, мы можем построить наш Header метод:

func (rrw RevelResponseWrapper) Header() http.Header {
  revelHeader := rrw.Response.Out.Header()
  keys := revelHeader.Server.GetKeys()
  headerMap := make(map[string][]string)

  for _, key := range keys {
    headerMap[key] = revelHeader.GetAll(key)
  }
  return http.Header(headerMap)
}

Write и WriteHeader

Вы бы использовали аналогичные анти-паттерны, чтобы выставить rrw.Write([]byte) (int, error) чтобы он звонил на c.Response.Out.Write(data []byte) (int, error) и rrw.WriteHeader(int) error, чтобы он звонил c.Response.WriteHeader(int, string).В зависимости от того, что считается подходящим для платформы, либо паникуйте по ошибкам, либо молча терпите неудачу, так как их API не ожидает, что WriteHeader ошибки будут обработаны.

Получение http.Request от Revel

К сожалению, тип http.Request является структурой, поэтому вы не можете просто смоделировать его.По сути, у вас есть два варианта: восстановить его с помощью пакета net/http из всех свойств, к которым у вас есть доступ, или надеяться, что *revel.Request, который у вас есть, тайно http.Request под капотом.В последнем случае вы можете использовать утверждение типа:

revelReq, ok := c.Request.In.(*revel.GoRequest)
if !ok {
  // handle this somehow
}
r := revelReq.Original
...