Применить структуру к контексту *http.Request
возвращает nil
Я обертываю несколько http.Handler
(например, промежуточное программное обеспечение), чтобы добавить функции и данные в контекст, но, похоже, это не работает должным образом.
Каждый раз, когда я вызываю a := api.Applied(r)
в другой области или обернутом слое http.Handler
, это возвращается
*runtime.TypeAssertionError преобразование интерфейса: интерфейс {} равен нулю, а не *api.Request
пакет API
package api
const (
CTX_API ctx_key = ""
)
type (
Request struct {
w http.ResponseWriter
r *http.Request
}
ctx_key string
)
func NewRequest(w http.ResponseWriter, r *http.Request) *Request{
return &Request{
w: w,
r: r,
}
}
// Apply api to request context
func (a *Request) Apply() *http.Request {
return a.r.WithContext(context.WithValue(a.r.Context(), CTX_API, a))
}
// Fetch applied api from request context
func Applied(r *http.Request) *Request {
return r.Context().Value(CTX_API).(*Request)
}
пакет основной
// This is the first wrapper and receives the HTTP request first
func adapt_api() serv.Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
defer serv.Recover(w)
a := api.NewRequest(w, r)
defer a.Recover()
/* Here the *api.Request is applied to the context */
a.Apply()
h.ServeHTTP(w, r)
})
}
}
// This is the second wrapper and receives the HTTP request after the first
func adapt_auth() serv.Adapter {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
if !serv.Auth_basic(r, AUTH_USER, AUTH_PASS) {
/* PANIC: *runtime.TypeAssertionError interface conversion: interface {} is nil, not *api.Request */
a := api.Applied(r)
a.Error(http.StatusUnauthorized, fmt.Errorf(http.StatusText(http.StatusUnauthorized)))
return
}
h.ServeHTTP(w, r)
})
}
}
func main(){
h := serv.NewHTTP("domain.com", "127.0.0.1", 8000)
h.Subhost("lib.").
Route_exact(serv.POST, "/csv/r", 60, serv.Adapt(
func(w http.ResponseWriter, r *http.Request){
/* PANIC: *runtime.TypeAssertionError interface conversion: interface {} is nil, not *api.Request */
a := api.Applied(r)
// some code
},
adapt_api(),
adapt_auth(),
))
h.Run()
}
Как было предложено в ответе. Если я изменю adapt_api()
(первый обработчик) и добавлю h.ServeHTTP(w, a.Apply())
, это будет работать в основном http.Handler
(третий обработчик), но я все равно получу указатель nil
в adapt_auth()
(второй обработчик)
Если я переопределяю *http.Request
непосредственно в a.Apply()
, это должно работать. Но здесь я все еще получаю nil
указатели повсюду.
func (a *Request) Apply(){
a.r = a.r.WithContext(context.WithValue(a.r.Context(), CTX_API, a))
}
Результат функции Apply()
не используется в оболочке adapt_api
, и поскольку
WithContext returns a shallow copy of r with its context changed to ctx
исходный запрос не изменяется.
h.ServeHTTP(w, a.Apply())
должен выполнить работу.
Это именно то, что здесь делает ваш обновленный Apply()
, потому что этот метод имеет приемник указателя. Поэтому нет необходимости возвращать a.r = a.r.WithContext(context.WithValue(a.r.Context(), CTX_API, a))
.
Хм.. В главном http.Handler
вроде работает (последний выполненный), а во втором adapt_auth()
не работает. У меня до сих пор паника от этого
- и если я использую свое собственное предложение в обновленном вопросе, я все равно получаю указатель nil
. Это работает, только если я использую h.ServeHTTP(w, a.Apply())
, а потом все равно не работает adapt_auth()
Я должен был спросить об этом в первую очередь, но зачем вам передавать обертку Request
в контексте http.Request
? Имхо, это странный способ действовать.
Вы имеете в виду, почему я хочу добавить *api.Request
в контекст *http.Request
? Я делаю это, потому что он обрабатывает все ответы JSON, как ошибки, так и успехи. Я не знаю, как еще я могу передать его каждому http.Handler
, потому что он мне нужен для ответов об ошибках JSON и т. д. (все виды ответов)
Возможно, вам не нужно его проходить. Способ продолжить — использовать контекст запроса только для передачи информации о промежуточном программном обеспечении вашим обработчикам (например, идентификатор пользователя, утверждения и т. д. с помощью функции WithContext
или Clone
) и создать api.Request непосредственно в обработчике для обработки ответов JSON.
Хорошо. Тогда как вы обрабатываете соединение с БД? Каждый HTTP-запрос использует одну и ту же транзакцию БД на протяжении всего запроса (если вы хотите выполнить откат). Как вы можете передать это каждому http.Handler
?
Спасибо. Ваше решение работает. Но разве нельзя переопределить
*http.Request
непосредственно из функцииa.Apply()
? Обновил мой вопрос новой функцией внизу.