API: Refactor "404 Not Found" response handler #3931

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2024-01-16 20:56:43 +01:00
parent abfea6354c
commit 3946e2a16f
9 changed files with 34 additions and 30 deletions

View file

@ -6,6 +6,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/get"
"github.com/photoprism/photoprism/internal/i18n"
"github.com/photoprism/photoprism/pkg/clean"
)
@ -29,6 +31,26 @@ func Error(c *gin.Context, code int, err error, id i18n.Message, params ...inter
c.AbortWithStatusJSON(code, resp)
}
// AbortNotFound renders a "404 Not Found" error page or JSON response.
var AbortNotFound = func(c *gin.Context) {
conf := get.Config()
switch c.NegotiateFormat(gin.MIMEHTML, gin.MIMEJSON) {
case gin.MIMEJSON:
c.JSON(http.StatusNotFound, gin.H{"error": i18n.Msg(i18n.ErrNotFound)})
default:
values := gin.H{
"signUp": gin.H{"message": config.MsgSponsor, "url": config.SignUpURL},
"config": conf.ClientPublic(),
"error": i18n.Msg(i18n.ErrNotFound),
"code": http.StatusNotFound,
}
c.HTML(http.StatusNotFound, "404.gohtml", values)
}
c.Abort()
}
// AbortUnauthorized aborts with status code 401.
func AbortUnauthorized(c *gin.Context) {
Abort(c, http.StatusUnauthorized, i18n.ErrUnauthorized)
@ -39,11 +61,6 @@ func AbortForbidden(c *gin.Context) {
Abort(c, http.StatusForbidden, i18n.ErrForbidden)
}
// AbortNotFound aborts with status code 404.
func AbortNotFound(c *gin.Context) {
Abort(c, http.StatusNotFound, i18n.ErrNotFound)
}
// AbortEntityNotFound aborts with status code 404.
func AbortEntityNotFound(c *gin.Context) {
Abort(c, http.StatusNotFound, i18n.ErrEntityNotFound)

View file

@ -25,7 +25,7 @@ func CreateSession(router *gin.RouterGroup) {
// Prevent CDNs from caching this endpoint.
if header.IsCdn(c.Request) {
c.AbortWithStatus(http.StatusNotFound)
AbortNotFound(c)
return
}

View file

@ -28,7 +28,7 @@ func DeleteSession(router *gin.RouterGroup) {
// Prevent CDNs from caching this endpoint.
if header.IsCdn(c.Request) {
c.AbortWithStatus(http.StatusNotFound)
AbortNotFound(c)
return
}

View file

@ -24,7 +24,7 @@ func GetSession(router *gin.RouterGroup) {
// Prevent CDNs from caching this endpoint.
if header.IsCdn(c.Request) {
c.AbortWithStatus(http.StatusNotFound)
AbortNotFound(c)
return
}

View file

@ -30,7 +30,7 @@ func CreateOAuthToken(router *gin.RouterGroup) {
// Prevent CDNs from caching this endpoint.
if header.IsCdn(c.Request) {
c.AbortWithStatus(http.StatusNotFound)
AbortNotFound(c)
return
}
@ -139,7 +139,7 @@ func RevokeOAuthToken(router *gin.RouterGroup) {
// Prevent CDNs from caching this endpoint.
if header.IsCdn(c.Request) {
c.AbortWithStatus(http.StatusNotFound)
AbortNotFound(c)
return
}

View file

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/api"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/pkg/header"
)
@ -16,7 +17,7 @@ func registerPWARoutes(router *gin.Engine, conf *config.Config) {
pwa := func(c *gin.Context) {
// Prevent CDNs from caching this endpoint.
if header.IsCdn(c.Request) {
c.AbortWithStatus(http.StatusNotFound)
api.AbortNotFound(c)
return
}

View file

@ -6,8 +6,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/api"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/i18n"
)
// registerStaticRoutes adds routes for serving static content and templates.
@ -20,20 +20,7 @@ func registerStaticRoutes(router *gin.Engine, conf *config.Config) {
router.Any(conf.BaseUri("/"), login)
// Shows "Page Not found" error if no other handler is registered.
router.NoRoute(func(c *gin.Context) {
switch c.NegotiateFormat(gin.MIMEHTML, gin.MIMEJSON) {
case gin.MIMEJSON:
c.JSON(http.StatusNotFound, gin.H{"error": i18n.Msg(i18n.ErrNotFound)})
default:
values := gin.H{
"signUp": gin.H{"message": config.MsgSponsor, "url": config.SignUpURL},
"config": conf.ClientPublic(),
"error": i18n.Msg(i18n.ErrNotFound),
"code": http.StatusNotFound,
}
c.HTML(http.StatusNotFound, "404.gohtml", values)
}
})
router.NoRoute(api.AbortNotFound)
// Serves static favicon.
router.StaticFile(conf.BaseUri("/favicon.ico"), filepath.Join(conf.ImgPath(), "favicon.ico"))

View file

@ -1,10 +1,9 @@
package server
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/api"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/pkg/header"
)
@ -14,7 +13,7 @@ var Security = func(conf *config.Config) gin.HandlerFunc {
return func(c *gin.Context) {
// Abort if the request should not be served through a CDN.
if header.AbortCdnRequest(c.Request) {
c.AbortWithStatus(http.StatusNotFound)
api.AbortNotFound(c)
return
}

View file

@ -23,6 +23,6 @@ const (
// Requests that include a standard authorization header should be automatically excluded
// from public caches: https://datatracker.ietf.org/doc/html/rfc7234#section-3
var (
DefaultVaryHeaders = []string{XAuthToken, AcceptEncoding}
DefaultVaryHeaders = []string{AcceptEncoding, XAuthToken}
DefaultVary = strings.Join(DefaultVaryHeaders, ", ")
)