Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting 404 for CSS and JS files #10

Open
sandeepkangude opened this issue Feb 1, 2019 · 15 comments
Open

Getting 404 for CSS and JS files #10

sandeepkangude opened this issue Feb 1, 2019 · 15 comments

Comments

@sandeepkangude
Copy link

sandeepkangude commented Feb 1, 2019

I am new to go lang.
My go lang version: 1.11.3
I am using Gorilla mux for router.

Somehow I cannot able to integrate swaggo/swag in to my Go Web application. Please see the steps I have taken.

Step 1: Added comments to my API (main.go) source code (Refer: https://github.com/swaggo/http-swagger#canonical-example)

Step 2: Download swag and http-swagger
go get github.com/swaggo/swag/cmd/swag
go get -u github.com/swaggo/http-swagger

Step 3: Run swag (it will generate some files in a docs/ directory)
swag init

Add the following code in my main.go
import (
"github.com/swaggo/http-swagger"
_"mylocation/docs"
"net/http"
"github.com/gorilla/mux"
)

func main() {
router := mux.NewRouter()
router.PathPrefix("/documentation/").Handler(httpSwagger.WrapHandler)
http.ListenAndServe(":8000", router)
}

I can able to redirect to "/documentation" route but page is blank. And when I check network logs, following files are missing/ getting 404

  1. swagger-ui.css
  2. swagger-ui-bundle.js
  3. swagger-ui-standalone-preset.js

I don't know what exactly happening. Please help

@ubogdan
Copy link
Contributor

ubogdan commented Jul 25, 2019

I'm not able to reproduce your issue.
anyway you should try changing to
router.PathPrefix("/documentation").Handler(httpSwagger.WrapHandler)

If you got things working out, please , leave a comment about how you solved it and close the issue.

@chiptus
Copy link

chiptus commented Dec 30, 2020

Got the same issue, I will update if resolved

@chiptus
Copy link

chiptus commented Dec 30, 2020

my problem was that my code was stripping the prefix from r.URL.Path, and http-swagger got the same prefix from r.RequestURI (swagger.go:71), because it wasn't stripped. then in net/webdav.go:195 it looked for this prefix in r.URL.Path which didn't exist and failed. I fixed it by not stripping the prefix in my code

@ubogdan
Copy link
Contributor

ubogdan commented Dec 30, 2020

@chiptus router.PathPrefix("/documentation/") doesn't strip the prefix. It actually instructs the router when it has a request to "/documentation" path to call the httpSwagger handler.
And yes, maybe the WrapHandler may require some adjustments if you want to use a custom route.

@chiptus
Copy link

chiptus commented Dec 30, 2020

just added my solution for future knowledge. I'm actually using vanilla net/http without frameworks

@jvierauy
Copy link

jvierauy commented Feb 1, 2021

Is there any solution for this problem?
I had the same issue, and couldn't fix it the same way as @chiptus.

@marianososto
Copy link

any news on this issue ? It's happening the same to me.

@fbigand
Copy link

fbigand commented May 6, 2021

I have the exact same issue (using julienschmidt/http-router).
Minimal code to reproduce

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/julienschmidt/httprouter"
	httpSwagger "github.com/swaggo/http-swagger"
)

func main() {
	router := httprouter.New()

	router.ServeFiles("/api/doc/static/*filepath", http.Dir("path/to/swagger/files"))
	router.HandlerFunc(http.MethodGet, "/api/doc/index.html", swaggerHandler)

	fmt.Println("Server on port 8080")
	log.Fatal(http.ListenAndServe(":8080", router))
}

func swaggerHandler(w http.ResponseWriter, r *http.Request) {
	swaggerFileUrl := "http://localhost:8080/api/doc/static/swagger.json"
	handler := httpSwagger.Handler(httpSwagger.URL(swaggerFileUrl))
	handler.ServeHTTP(w, r)
}

@xScorp1oNx
Copy link

You can do it like this:

routes := httprouter.New()

routes.GET("/doc/:any", swaggerHandler)


func swaggerHandler(res http.ResponseWriter, req *http.Request, p httprouter.Params) {
     httpSwagger.WrapHandler(res, req)
}

Do not forget import doc files:

import (
     _ "example.project/docs"
)

@srPuebla
Copy link

srPuebla commented Oct 7, 2021

Hi,

I am getting the same issue but using echo-swagger

        echo.GET("/swagger/*", echoSwagger.WrapHandler)
 	echo.GET("/test/", h.Status, enforcer.Enforce)
	echo.Start(":8080")

After more than 48 hours working, suddenly the app starts to drop 404 loading css, js that you mentioned.

Failed to load resource: the server responded with a status of 404 (Not Found)
swagger-ui-bundle.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
swagger-ui-standalone-preset.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
index.html:75 Uncaught ReferenceError: SwaggerUIBundle is not defined
    at window.onload (index.html:75)
swagger-ui.css:1 Failed to load resource: the server responded with a status of 404 (Not Found)

If i restart app, it starts to work... but i dont know why it happens.

@bradleygore
Copy link

Hey @ubogdan I am able to reproduce this. With the most recent swag updates allowing to generate swag docs only for targeted --tags, we split up our docs into separate directories and separate http endpoints. The wiring looks like this:

import (
        // generated swag docs per tag targeted
	swagBridge "pos-api/internal/api/swaggerdocs/bridge"
	swagHttp "pos-api/internal/api/swaggerdocs/http"
	swagPos "pos-api/internal/api/swaggerdocs/pos"
	swagProvisioner "pos-api/internal/api/swaggerdocs/provisioner"
	swagSolo "pos-api/internal/api/swaggerdocs/solo"
	swagSupport "pos-api/internal/api/swaggerdocs/support"
	swagTools "pos-api/internal/api/swaggerdocs/tools"

	// http-swagger for handler func
	httpSwagger "github.com/swaggo/http-swagger"

	// ... other imports
)

var (
	// config options for display: https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/#display
	swagUiConfig = httpSwagger.UIConfig(map[string]string{
		"displayOperationId":    "true",
		"defaultModelRendering": `"model"`,
		"filter":          "true",
		"showExtensions":  "true",
		"syntaxHighlight": `{"active":"true","theme":"obsidian"}`,
		"tagSorter":       `"alpha"`,
	})
	ginHandlers = map[string]gin.HandlerFunc{}
)

func setupSwaggerRoutes(r *gin.RouterGroup) {
	r.GET("/swagger/bridge/*any", func(ctx *gin.Context) { swagSpecHandler(swagBridge.SwaggerInfobridge, ctx) })
	r.GET("/swagger/http/*any", func(ctx *gin.Context) { swagSpecHandler(swagHttp.SwaggerInfohttp, ctx) })
	r.GET("/swagger/pos/*any", func(ctx *gin.Context) { swagSpecHandler(swagPos.SwaggerInfopos, ctx) })
	r.GET("/swagger/provisioner/*any", func(ctx *gin.Context) { swagSpecHandler(swagProvisioner.SwaggerInfoprovisioner, ctx) })
	r.GET("/swagger/solo/*any", func(ctx *gin.Context) { swagSpecHandler(swagSolo.SwaggerInfosolo, ctx) })
	r.GET("/swagger/support/*any", func(ctx *gin.Context) { swagSpecHandler(swagSupport.SwaggerInfosupport, ctx) })
	r.GET("/swagger/tools/*any", func(ctx *gin.Context) { swagSpecHandler(swagTools.SwaggerInfotools, ctx) })
}

func swagSpecHandler(spec *swag.Spec, ctx *gin.Context) {
	instName := spec.InstanceName() //custom instances like /bridge, /pos, etc...
	if _, exists := ginHandlers[instName]; !exists {
		cfg := middleware.HTTPRetrieveAPIConfig(ctx)
		spec.Host = cfg.Host
		ginHandlers[instName] = gin.WrapH(httpSwagger.Handler(
			// note: have to have _both_ the InstanceName and the URL to the `doc.json` setup properly.
			// See also: https://github.com/swaggo/http-swagger/blob/6ee8ad96a7f258e1531d470d9e220ee7beee7a16/swagger.go#L178
			httpSwagger.InstanceName(instName),
			httpSwagger.URL(fmt.Sprintf("/api/swagger/%s/doc.json", instName)),
			httpSwagger.DocExpansion("none"),
			swagUiConfig,
		))
	}
	ginHandlers[instName](ctx)
}

The intent was to only create each handler once, for efficiency. However, the swaggerFiles package seems to have a singleton in place. The swag-http handler tries to set the Prefix of that handler in a sync.Once func:

handler := swaggerFiles.Handler
once.Do(func() {
	handler.Prefix = matches[1]
})

However, if we try to have separate swag doc routes, this singleton poses a problem. As I switch through the routes in the browser, the first run of each endpoint always works. However, once the sync.Once for each http-swagger Handler has run, there is no more setting of the swaggerFiles.Prefix - so whatever the last-loaded Prefix is seems to be the one used.

What I'm going to have to do is create the handler anew for each request, which seems terribly inefficient. I don't see another way around this though.

Maybe I'm missing something or there is a better way to structure this?

@umurpza
Copy link

umurpza commented Feb 22, 2023

Hi,

I am getting the same issue but using echo-swagger

        echo.GET("/swagger/*", echoSwagger.WrapHandler)
 	echo.GET("/test/", h.Status, enforcer.Enforce)
	echo.Start(":8080")

After more than 48 hours working, suddenly the app starts to drop 404 loading css, js that you mentioned.

Failed to load resource: the server responded with a status of 404 (Not Found)
swagger-ui-bundle.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
swagger-ui-standalone-preset.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
index.html:75 Uncaught ReferenceError: SwaggerUIBundle is not defined
    at window.onload (index.html:75)
swagger-ui.css:1 Failed to load resource: the server responded with a status of 404 (Not Found)

If i restart app, it starts to work... but i dont know why it happens.

Did you find a solution for this? I'm using Gin and having the same issue you described. After a certain amount of time, I get 404's on swagger files. Much less than 48 hours at times.

Errors:
Failed to load resource: the server responded with a status of 404 (Not Found)
swagger-ui-standalone-preset.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
swagger-ui-bundle.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
favicon-32x32.png:1 Failed to load resource: the server responded with a status of 404 (Not Found)
favicon-16x16.png:1 Failed to load resource: the server responded with a status of 404 (Not Found)
index.html:75 Uncaught ReferenceError: SwaggerUIBundle is not defined
at window.onload (index.html:75:14)
swagger-ui.css:1 Failed to load resource: the server responded with a status of 404 (Not Found)

@bradleygore
Copy link

@umurpza this is the solution I came up with, and it has been working fine. It's not ideal because I have to make a new handler internally for each request because of how httpSwagger.Handler works, but it at least got me going.

import (
	"fmt"
	swagGroupA "path/to/swaggerdocs/a" // built targeting tag a
	swagGroupB "path/to/swaggerdocs/b" // built targeting tag b
	swagGroupC "path/to/swaggerdocs/c" // built targeting tag c
	httpSwagger "github.com/swaggo/http-swagger"
	"github.com/swaggo/swag"
	"github.com/gin-gonic/gin"
)

var (
	// config options for display: https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/#display
	swagUiConfig = httpSwagger.UIConfig(map[string]string{
		// this map goes into the client-side as raw json, but apparently not using json.Marshal,
		// so "true" -> true, `"model"` -> "model", etc...
		"displayOperationId":    "true",
		"defaultModelRendering": `"model"`,
		"filter":          "true",
		"showExtensions":  "true",
		"syntaxHighlight": `{"active":"true","theme":"obsidian"}`,
		"tagSorter":       `"alpha"`,
	})
)

func setupSwaggerRoutes(r *gin.RouterGroup) {
	r.GET("/swagger/a/*any", func(ctx *gin.Context) { swagSpecHandler(swagGroupA.SwaggerInfobridge, ctx) })
	r.GET("/swagger/b/*any", func(ctx *gin.Context) { swagSpecHandler(swagGroupB.SwaggerInfohttp, ctx) })
	r.GET("/swagger/c/*any", func(ctx *gin.Context) { swagSpecHandler(swagGroupC.SwaggerInfopos, ctx) })
}
func swagSpecHandler(spec *swag.Spec, ctx *gin.Context) {
	instName := spec.InstanceName() //custom instances like /a, /b, etc...

	// Cannot cache the handlers due to an issue I worked through while debugging the http-swagger code.
	// They have a singleton in play under the hood, and their Handler only writes to that singleton in a `sync.Once`.
	// See https://github.com/swaggo/http-swagger/issues/10#issuecomment-1358163317
	// Once it is fixed, we can go back to caching the handler
	spec.Host = "https://some-domain/api"
	gin.WrapH(httpSwagger.Handler(
		// note: have to have _both_ the InstanceName and the URL to the `doc.json` setup properly.
		// See also: https://github.com/swaggo/http-swagger/blob/6ee8ad96a7f258e1531d470d9e220ee7beee7a16/swagger.go#L178
		httpSwagger.InstanceName(instName),
		httpSwagger.URL(fmt.Sprintf("/api/swagger/%s/doc.json", instName)),
		httpSwagger.DocExpansion("none"),
		swagUiConfig,
	))(ctx)
}

@umurpza
Copy link

umurpza commented Feb 22, 2023

@umurpza this is the solution I came up with, and it has been working fine. It's not ideal because I have to make a new handler internally for each request because of how httpSwagger.Handler works, but it at least got me going.

import (
	"fmt"
	swagGroupA "path/to/swaggerdocs/a" // built targeting tag a
	swagGroupB "path/to/swaggerdocs/b" // built targeting tag b
	swagGroupC "path/to/swaggerdocs/c" // built targeting tag c
	httpSwagger "github.com/swaggo/http-swagger"
	"github.com/swaggo/swag"
	"github.com/gin-gonic/gin"
)

var (
	// config options for display: https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/#display
	swagUiConfig = httpSwagger.UIConfig(map[string]string{
		// this map goes into the client-side as raw json, but apparently not using json.Marshal,
		// so "true" -> true, `"model"` -> "model", etc...
		"displayOperationId":    "true",
		"defaultModelRendering": `"model"`,
		"filter":          "true",
		"showExtensions":  "true",
		"syntaxHighlight": `{"active":"true","theme":"obsidian"}`,
		"tagSorter":       `"alpha"`,
	})
)

func setupSwaggerRoutes(r *gin.RouterGroup) {
	r.GET("/swagger/a/*any", func(ctx *gin.Context) { swagSpecHandler(swagGroupA.SwaggerInfobridge, ctx) })
	r.GET("/swagger/b/*any", func(ctx *gin.Context) { swagSpecHandler(swagGroupB.SwaggerInfohttp, ctx) })
	r.GET("/swagger/c/*any", func(ctx *gin.Context) { swagSpecHandler(swagGroupC.SwaggerInfopos, ctx) })
}
func swagSpecHandler(spec *swag.Spec, ctx *gin.Context) {
	instName := spec.InstanceName() //custom instances like /a, /b, etc...

	// Cannot cache the handlers due to an issue I worked through while debugging the http-swagger code.
	// They have a singleton in play under the hood, and their Handler only writes to that singleton in a `sync.Once`.
	// See https://github.com/swaggo/http-swagger/issues/10#issuecomment-1358163317
	// Once it is fixed, we can go back to caching the handler
	spec.Host = "https://some-domain/api"
	gin.WrapH(httpSwagger.Handler(
		// note: have to have _both_ the InstanceName and the URL to the `doc.json` setup properly.
		// See also: https://github.com/swaggo/http-swagger/blob/6ee8ad96a7f258e1531d470d9e220ee7beee7a16/swagger.go#L178
		httpSwagger.InstanceName(instName),
		httpSwagger.URL(fmt.Sprintf("/api/swagger/%s/doc.json", instName)),
		httpSwagger.DocExpansion("none"),
		swagUiConfig,
	))(ctx)
}

This seems to be working for me. Thanks!

@alexandredantas
Copy link

Hey everyone! I had the same issue but looks like the version 2.0.1 fixes it removing the singleton (dce10a8#diff-44aa764656674984f26bf27a7bcd56f8c03ea4f6b1ee8b02c48bd7e6c6c8206fL172)

I upgraded and now it's under test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests