Обработав запрос GET, я хочу перенаправить пользователя на новый URL-адрес, обрабатываемый моим приложением, который отображает результаты.
В документации по джину я нашел два разных способа сделать это. Один из них — использовать функцию redirect()
gin, а другой — функцию маршрутизатора HandleContext()
. В цитируемой документации говорится, что redirect()
следует использовать для запроса POST или для запроса GET, но затем говорится, что HandleContext()
следует использовать для перенаправления «Маршрутизатор». Я не уверен, что полностью понимаю последствия использования того или другого.
Первый вариант использования, приведенный в приведенной выше документации:
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
})
Отличительной особенностью здесь является то, что перенаправление осуществляется на внешний URL-адрес, а не на само приложение. Второй вариант использования, который описывается как «перенаправление маршрутизатора», следующий:
r.GET("/test", func(c *gin.Context) {
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(200, gin.H{"hello": "world"})
})
Похоже, это необходимо, если URL-адрес для перенаправления — это путь, обрабатываемый моим приложением.
Но это URL-адрес, как и любой другой. Так почему бы просто не использовать c.redirect()
для перенаправления маршрутизатора?
Интуитивно это очевидно. Но я подозреваю, что это приводит к неожиданному поведению (или, по крайней мере, к поведению, которого я лично не ожидал)
Что плохого в использовании c.redirect()
для перенаправления маршрутизатора и в чем разница между ним и router.HandleContext()
?
Разница в том, что HandleContext() выполняет внутреннее перенаправление, которое клиент никогда не замечает, тогда как Redirect() фактически отправляет ответ Http-клиенту, уведомляя его о том, что ресурс изменился.
Я думаю, что лучше всего это можно проиллюстрировать, наблюдая за DevTools браузера.
HandleContext()
:
Как видите, здесь клиент даже не заметил, что запрос был внутренне перенаправлен на другой обработчик.
Redirect()
:
Здесь клиент был вынужден сделать два запроса, чтобы получить реальный ресурс. Также обратите внимание, что в строке URL-адреса теперь отображается URL-адрес перенаправленного запроса.
Очевидно, что выполнение двух запросов приводит к ухудшению производительности, поэтому для внутренних перенаправлений следует отдавать предпочтение router.HandleContext()
.
Если вы хотите проверить себя, вот код:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// Switch the uncommented block in order to see the effect of HandleContext.
/* r.GET("/test", func(c *gin.Context) {
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(200, gin.H{"hello": "world"})
}) */
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/test2")
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(200, gin.H{"hello": "world"})
})
r.Run()
}
Если вас также устраивает, что клиент заметил перенаправление, тогда ответ должен быть утвердительным.
Я обнаружил еще одну разницу между ctx.Redirect
и HandleContext
, которая может заинтересовать других.
В каком-то коде, который я писал, Redirect
выдал следующее предупреждение:
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 301 with 200
Код был следующим:
// if user has no simulations, redirect to the user dashboard
if user.CurrentSimulationID == 0 {
ctx.Redirect(http.StatusMovedPermanently, "/user/dashboard")
ctx.Abort()
}
в котором говорится, что когда пользователи пытаются отобразить страницу, в которой нет ничего полезного, они перенаправляются на страницу, которая позволяет им генерировать что-то полезное.
Если я правильно понял, Redirect
создаёт заголовок со статусом 301. Но страница, на которую я перенаправляюсь, хочет создать заголовок со статусом 200.
Следующий код не вызывает такого конфликта:
// if user has no simulations, redirect to the user dashboard
if u.CurrentSimulationID == 0 {
ctx.Request.URL.Path = `/user/dashboard`
Router.HandleContext(ctx)
ctx.Abort()
}
Вероятно, существует способ использовать Redirect
таким образом, чтобы не было предупреждения, например, путем модифицированного вызова Abort
. Или, может быть, другой вызов Redirect
, который не устанавливает статус.
Я осмотрелся, но не нашел работающего решения. Но в любом случае вариант Router.HandleContext
мне кажется лучше по причинам, указанным @frankenapps.
Суть вышесказанного в том, что между этими двумя вариантами, похоже, есть реальная практическая разница, кроме скорости.
Большое спасибо. Это проясняет. Только один вопрос: правильно ли я считаю, что никаких негативных последствий, кроме небольшого снижения производительности, при использовании редиректа нет? Я спрашиваю об этом, потому что я также пишу сервер, который обрабатывает запросы API, и, по крайней мере, в целях разработки удобно проверять, был ли запрос отправлен.