Значение контекста *http.Request возвращает ноль

Применить структуру к контексту *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))
}
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
1
0
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Результат функции Apply() не используется в оболочке adapt_api, и поскольку WithContext returns a shallow copy of r with its context changed to ctx исходный запрос не изменяется.

h.ServeHTTP(w, a.Apply()) должен выполнить работу.

Спасибо. Ваше решение работает. Но разве нельзя переопределить *http.Request непосредственно из функции a.Apply()? Обновил мой вопрос новой функцией внизу.

rick001 13.08.2024 14:22

Это именно то, что здесь делает ваш обновленный Apply(), потому что этот метод имеет приемник указателя. Поэтому нет необходимости возвращать a.r = a.r.WithContext(context.WithValue(a.r.Context(), CTX_API, a)).

xflr6 13.08.2024 14:29

Хм.. В главном http.Handler вроде работает (последний выполненный), а во втором adapt_auth() не работает. У меня до сих пор паника от этого

rick001 13.08.2024 14:30

- и если я использую свое собственное предложение в обновленном вопросе, я все равно получаю указатель nil. Это работает, только если я использую h.ServeHTTP(w, a.Apply()), а потом все равно не работает adapt_auth()

rick001 13.08.2024 14:33

Я должен был спросить об этом в первую очередь, но зачем вам передавать обертку Request в контексте http.Request? Имхо, это странный способ действовать.

xflr6 13.08.2024 15:06

Вы имеете в виду, почему я хочу добавить *api.Request в контекст *http.Request? Я делаю это, потому что он обрабатывает все ответы JSON, как ошибки, так и успехи. Я не знаю, как еще я могу передать его каждому http.Handler, потому что он мне нужен для ответов об ошибках JSON и т. д. (все виды ответов)

rick001 13.08.2024 15:13

Возможно, вам не нужно его проходить. Способ продолжить — использовать контекст запроса только для передачи информации о промежуточном программном обеспечении вашим обработчикам (например, идентификатор пользователя, утверждения и т. д. с помощью функции WithContext или Clone) и создать api.Request непосредственно в обработчике для обработки ответов JSON.

xflr6 13.08.2024 15:28

Хорошо. Тогда как вы обрабатываете соединение с БД? Каждый HTTP-запрос использует одну и ту же транзакцию БД на протяжении всего запроса (если вы хотите выполнить откат). Как вы можете передать это каждому http.Handler?

rick001 13.08.2024 15:34

Другие вопросы по теме