Применить структуру к контексту *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()? Обновил мой вопрос новой функцией внизу.