I'm using gqlgen for my service and apollo client and graphql-ws for my front end and I'm trying to use a subscription and it perfectly works in my playground but when i try to connect the client to it I receive this error:
WebSocket connection to 'ws://localhost:8080/' failed:
and in my container log I receive:
unable to upgrade *http.response to websocket websocket: request origin not allowed by Upgrader.CheckOrigin:
http: superfluous response.WriteHeader call from github.com/99designs/gqlgen/graphql/handler/transport.SendError (error.go:15)
here is my golang code:
if err := godotenv.Load(); err != nil {
log.Fatal("Error loading environment variables file")
}
port := helpers.Env("PORT")
if port == "" {
port = defaultPort
}
router := chi.NewRouter()
router.Use(cors.New(cors.Options{
AllowedOrigins: strings.Split(helpers.Env("ALLOWED_ORIGINS"), ","),
AllowCredentials: true,
Debug: helpers.Env("DEBUG") == "true",
AllowedHeaders: []string{"*"},
}).Handler)
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers.Resolver{}}))
srv.AddTransport(transport.POST{})
upgrader := &transport.Websocket{
Upgrader: websocket.Upgrader{
HandshakeTimeout: 1 * time.Minute,
CheckOrigin: func(r *http.Request) bool {
return true
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
},
KeepAlivePingInterval: 10 * time.Second,
}
srv.AddTransport(upgrader)
srv.Use(extension.Introspection{})
if helpers.Env("MODE") == "PRODUCTION" {
cache, err := apq.NewCache(24 * time.Hour)
if err != nil {
log.Fatalf("cannot create APQ redis cache: %v", err)
}
srv.Use(extension.AutomaticPersistedQuery{Cache: cache})
}
go initWorkers()
go runAsynqmon()
router.Use(getHeadersMiddleware())
router.Handle("/", srv)
if helpers.Env("MODE") == "DEVELOPMENT" {
router.Handle("/playground", playground.Handler("GraphQL playground", "/"))
log.Printf("connect to http://localhost:%s/playground for GraphQL playground", port)
}
log.Fatal(http.ListenAndServe(":"+port, router))
and here is my client code:
import { setContext } from '#apollo/client/link/context'
import { onError } from '#apollo/client/link/error'
import { GraphQLWsLink } from '#apollo/client/link/subscriptions'
import { getMainDefinition } from '#apollo/client/utilities'
import { createUploadLink } from 'apollo-upload-client'
import { createClient } from 'graphql-ws'
import { logout } from '../helpers/logout'
import { getTokenFromStorage } from '../helpers/userData'
import { lang } from '../localization'
const authLink = setContext((_, { headers }) => {
const token = getTokenFromStorage()
return {
headers: {
authorization: token ? `Bearer ${token}` : undefined,
'Accept-Language': lang,
...headers
}
}
})
const httpLink = createUploadLink({
uri: process.env.REACT_APP_GRAPH_BFF || 'http://localhost:8080'
})
const wsLink = new GraphQLWsLink(
createClient({
url: 'ws://localhost:8080/'
})
)
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
const logoutLink = onError(({ response }) => {
if (
response?.errors &&
response.errors.length > 0 &&
response.errors.some((errorItem) =>
errorItem.message.toLowerCase().includes('unauthenticated')
)
) {
logout()
}
})
const chainList = [logoutLink, authLink, splitLink]
const linkChain = from(chainList)
const apolloClient = new ApolloClient({
cache: new InMemoryCache({
addTypename: false
}),
link: linkChain
})
export default apolloClient
I thought CheckOrigin in upgrader would fix this but it didn't work
any idea how to fix this?
that was a awesome question. that means you are a talented programmer... and response is here:
you need to change this line:
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers.Resolver{}}))
because NewDefaultServer use same origin that means your Origin and Host MUST be same and that means your code :
CheckOrigin: func(r *http.Request) bool {
return true
},
did not work.
you must change NewDefaultServer to New, so your code change to it:
srv := handler.New(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers.Resolver{}}))
and Your WebSocket works fine.
be careful about this change because NewDefaultServer has some config inside it for Update or get or some other things. (see package itself)
and you must write inside it handy in your code.
Related
Description
I have an API which I have created and I have some endpoints protected. The problem I am facing now on the client making a request is that the first request comes through with the Authorization header provided but a second request is blocked because Authorization is not present.
I can confirm that Authorization is present and worked perfectly when I was running Typescript till I recreated the endpoints in Go with Gin.
How to reproduce
Call estimate endpoint from client (iOS app) response succeceds
Make a second call from Client (iOS app) response failed because it is not taking the Authorization header which contains token
package main
import (
"github.com/gin-gonic/gin"
)
type App struct {
Router *gin.Engine
Gateman *gateman.Gateman
Database *mongo.Client
}
func (a *App) StartApp() {
err := godotenv.Load()
if err != nil {
fmt.Printf("Could not load .env \n")
}
a.Database = database.DB
a.Router = gin.New()
a.Gateman = middleware.Gateman()
a.Router.Use(gin.Recovery())
a.Router.Use(middleware.DefaultHelmet())
a.Router.Use(middleware.GinContextToContextMiddleware())
a.Router.Use(middleware.RequestID(nil))
a.Router.Use(middleware.ErrorHandler())
a.Router.Use(middleware.Logger("package-service"))
connection, err := amqp091.Dial(os.Getenv("AMQP_URL"))
if err != nil {
log.Fatal(fmt.Printf("Error on dial %v\n", err.Error()))
}
routes.Routes(a.Router, database.GetDatabase(a.Database), a.Gateman, connection)
}
func (a *App) Run(addr string) {
logs := log.New(os.Stdout, "package-service", log.LstdFlags)
server := &http.Server{
Addr: addr,
Handler: a.Router,
ErrorLog: logs,
IdleTimeout: 120 * time.Second, // max time for connections using TCP Keep-Alive
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
go func() {
if err := server.ListenAndServe(); err != nil {
logs.Fatal(err)
}
}()
// trap sigterm or interrupt and gracefully shutdown the server
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
signal.Notify(c, os.Kill)
sig := <-c
logs.Println("Recieved terminate, graceful shutdown", sig)
tc, _ := context.WithTimeout(context.Background(), 30*time.Second)
server.Shutdown(tc)
}
func Routes(r *gin.Engine, db *mongo.Database, g *gateman.Gateman, conn *amqp091.Connection) {
atHandler := pc.NewPackagesController(ps.NewPackagesService(pr.NewPackagesRepository(db)), g, events.NewEventEmitter(conn))
r.Use(CORS())
v1 := r.Group("/api/v1/package")
{
v1.POST("/query", GraphqlHandler(db, directives.NewDirectivesManager(g)))
v1.GET("/", PlaygroundHandler(db))
v1.POST("/", g.Guard([]string{"user"}, nil), atHandler.Create)
v1.POST("/estimate", g.Guard([]string{"user"}, nil), atHandler.Estimate)
v1.PUT("/:packageID", g.Guard([]string{"user", "admin"}, nil), atHandler.Update)
v1.PUT("/:packageID/assign", g.Guard([]string{"admin"}, nil), atHandler.Assign)
v1.POST("/:packageID/cancel", g.Guard([]string{"user", "admin"}, nil), atHandler.CancelRequest)
v1.POST("/:packageID/complete", g.Guard([]string{"admin"}, nil), atHandler.Complete)
v1.POST("/:packageID/reject", g.Guard([]string{"admin"}, nil), atHandler.RejectRequest)
v1.GET("/healthz", atHandler.GetHealth)
}
r.GET("/", atHandler.GetUP)
}
func CORS() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
func main() {
start := App{}
start.StartApp()
start.Run(":3009")
}
Expectations
All endpoints that are Guarded simply checks the header for Authorization and if provided in the request, it should be successful
Actual result
First request succeed /estimate
Second request / POST request fails to accept Authorization header
Also irrespective of what the first post request is, the second post request just never accept the Authorization header
Also need to mention that I do not have this issue with postman. Both request run independently but using another client for the request, gives this problem
Environment
go version: 1.19
gin version (or commit ref): v1.8.1
operating system: Mac and iOS mobile
Here is my client code
func request<T>(with builder: BaseRequest) -> AnyPublisher<T, APIError> where T: Codable {
request(with: builder, customDecoder: JSONDecoder())
}
func request<T>(with builder: BaseRequest, customDecoder: JSONDecoder) -> AnyPublisher<T, APIError> where T: Codable {
let encoding: ParametersEncoder = [.get, .delete].contains(builder.method) ? URLParameretersEncoder() : JSONParametersEncoder()
customDecoder.keyDecodingStrategy = .convertFromSnakeCase
var url: URL {
var components = URLComponents()
components.scheme = "https"
components.host = builder.baseUrl
components.path = "/api/v1" + builder.path
guard let url = components.url else {
preconditionFailure("Invalid URL components: \(components)")
}
return url
}
var urlRequest = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 46.0)
urlRequest.httpMethod = builder.method.rawValue
builder.headers.forEach { key, value in
urlRequest.setValue(value, forHTTPHeaderField: key)
}
if let token = tokenManager.token {
urlRequest.setValue("Bearer " + token, forHTTPHeaderField: "Authorization")
// urlRequest.setValue("ABC", forHTTPHeaderField: "testing123")
}
if let parameters = builder.parameters {
guard let encoded = try? encoding.encode(parameters: parameters, in: urlRequest) else {
fatalError()
}
urlRequest = encoded
}
self.log(request: urlRequest)
return URLSession.shared
.dataTaskPublisher(for: urlRequest)
.receive(on: DispatchQueue.main)
.mapError {_ -> APIError in
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
return .unknown
}
.flatMap { data, response -> AnyPublisher<T, APIError> in
guard let response = response as? HTTPURLResponse else {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
return Fail(error: APIError.invalidResponse).eraseToAnyPublisher()
}
self.log(response: response, data: data, error: nil)
if (200...299).contains(response.statusCode) {
return Just(data)
.decode(type: T.self, decoder: customDecoder)
// .map {
// print($0)
// return $0
// } //added
.mapError {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
return .decodingError(underlyingError: $0)
}
.eraseToAnyPublisher()
} else {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
if response.statusCode == 401 {
// Send notification to remove corrdinator and return to root
// rxNotificationCenter.post(.invalidToken)
}
guard let errorResponse = try? customDecoder.decode(BaseResponse.self, from: data) else {
return Fail(error: APIError.decodingError(underlyingError: NSError("Can't decode error"))).eraseToAnyPublisher()
}
return Fail(error: APIError.server(response: errorResponse))
.eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}
protocol BaseRequest {
var baseUrl: String { get }
var path: String { get }
var headers: HTTPHeaders { get }
var method: HTTPRequestMethod { get }
var parameters: HTTPParameters { get }
}
public typealias HTTPHeaders = [String: String]
public typealias HTTPParameters = [String: Any]?
Another point, calling a single endpoint multiple time, works fine, calling a different one is where the header is rejected
I have this scraper library, I would like to change my user agent if the first user agent returns error, but this code doesnt work, if first user agent doesnt work, I have send the 2nd attempt but this will never finish since onHTML is not triggered:
package scraper
import (
"net/http"
"github.com/davecgh/go-spew/spew"
"github.com/gocolly/colly"
)
const (
fbUserAgent = "ua 1"
userAgent = "ua 2"
)
type ScrapeResult struct {
Title string
Description string
SiteName string
URL string
Images []string
}
func Scrape2(url string) (*ScrapeResult, error) {
var (
res *ScrapeResult
scrapeErr error
done = make(chan bool, 1)
c = colly.NewCollector()
)
c.OnError(func(r *colly.Response, err error) {
if ua := r.Request.Headers.Get("User-Agent"); ua == fbUserAgent {
c.Request(
"GET",
url,
nil,
nil,
http.Header{
"User-Agent": []string{userAgent},
"Accept": []string{"*/*"},
},
)
} else {
scrapeErr = err
done <- true
}
})
c.OnHTML("html", func(e *colly.HTMLElement) {
spew.Dump("ON HTML")
res = &ScrapeResult{URL: url}
res.Title = FindTitle(e)
res.Description = FindDescription(e)
res.SiteName = FindSiteName(e)
res.Images = FindImages(e)
done <- true
})
c.Request(
"GET",
url,
nil,
nil,
http.Header{
"User-Agent": []string{fbUserAgent},
"Accept": []string{"*/*"}, // * / *
"Accept-Language": []string{"en-GB,en-US;q=0.9,en;q=0.8"},
"Accept-Encoding": []string{"gzip, deflate, br"},
"Connection": []string{"keep-alive"},
"sec-ch-ua": []string{` Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90`},
},
)
<- done
return res, scrapeErr
}
func FindTitle(e *colly.HTMLElement) string {
if content := e.ChildAttr(`meta[property="og:title"]`, "content"); len(content) > 0 {
return content
}
return ""
}
func FindDescription(e *colly.HTMLElement) string {
if content := e.ChildAttr(`meta[property="og:description"]`, "content"); len(content) > 0 {
return content
}
return ""
}
func FindSiteName(e *colly.HTMLElement) string {
if content := e.ChildAttr(`meta[property="og:site_name"]`, "content"); len(content) > 0 {
return content
}
return ""
}
func FindImages(e *colly.HTMLElement) []string {
images := make([]string, 0)
if content := e.ChildAttr(`meta[property="og:image"]`, "content"); len(content) > 0 {
images = append(images, content)
}
return images
}
How can I make colly request for the 2nd time and trigger the onHTML? thank you
You can set the property collector.CheckHead = true
What this does is ensures that you do a GetHEAD operation first to check connection issues and if it fails - there is a retry.
you will need /v2 of gocolly to have this feature included.
https://github.com/gocolly/colly/blob/master/colly.go#L110
I built a backend with Golang's Gin framework and the JWT middleware for it. This is the official example from the readme, which I used:
main.go
package main
import (
"log"
"net/http"
"os"
"time"
"github.com/appleboy/gin-jwt/v2"
"github.com/gin-gonic/gin"
)
type login struct {
Username string `form:"username" json:"username" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
var identityKey = "id"
func helloHandler(c *gin.Context) {
claims := jwt.ExtractClaims(c)
user, _ := c.Get(identityKey)
c.JSON(200, gin.H{
"userID": claims[identityKey],
"userName": user.(*User).UserName,
"text": "Hello World.",
})
}
// User demo
type User struct {
UserName string
FirstName string
LastName string
}
func main() {
port := os.Getenv("PORT")
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
if port == "" {
port = "8000"
}
// the jwt middleware
authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
Realm: "test zone",
Key: []byte("secret key"),
Timeout: time.Hour,
MaxRefresh: time.Hour,
IdentityKey: identityKey,
PayloadFunc: func(data interface{}) jwt.MapClaims {
if v, ok := data.(*User); ok {
return jwt.MapClaims{
identityKey: v.UserName,
}
}
return jwt.MapClaims{}
},
IdentityHandler: func(c *gin.Context) interface{} {
claims := jwt.ExtractClaims(c)
return &User{
UserName: claims[identityKey].(string),
}
},
Authenticator: func(c *gin.Context) (interface{}, error) {
var loginVals login
if err := c.ShouldBind(&loginVals); err != nil {
return "", jwt.ErrMissingLoginValues
}
userID := loginVals.Username
password := loginVals.Password
if (userID == "admin" && password == "admin") || (userID == "test" && password == "test") {
return &User{
UserName: userID,
LastName: "Bo-Yi",
FirstName: "Wu",
}, nil
}
return nil, jwt.ErrFailedAuthentication
},
Authorizator: func(data interface{}, c *gin.Context) bool {
if v, ok := data.(*User); ok && v.UserName == "admin" {
return true
}
return false
},
Unauthorized: func(c *gin.Context, code int, message string) {
c.JSON(code, gin.H{
"code": code,
"message": message,
})
},
// TokenLookup is a string in the form of "<source>:<name>" that is used
// to extract token from the request.
// Optional. Default value "header:Authorization".
// Possible values:
// - "header:<name>"
// - "query:<name>"
// - "cookie:<name>"
// - "param:<name>"
TokenLookup: "header: Authorization, query: token, cookie: jwt",
// TokenLookup: "query:token",
// TokenLookup: "cookie:token",
// TokenHeadName is a string in the header. Default value is "Bearer"
TokenHeadName: "Bearer",
// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
TimeFunc: time.Now,
})
if err != nil {
log.Fatal("JWT Error:" + err.Error())
}
r.POST("/login", authMiddleware.LoginHandler)
r.NoRoute(authMiddleware.MiddlewareFunc(), func(c *gin.Context) {
claims := jwt.ExtractClaims(c)
log.Printf("NoRoute claims: %#v\n", claims)
c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"})
})
auth := r.Group("/auth")
// Refresh time can be longer than token timeout
auth.GET("/refresh_token", authMiddleware.RefreshHandler)
auth.Use(authMiddleware.MiddlewareFunc())
{
auth.GET("/hello", helloHandler)
}
if err := http.ListenAndServe(":"+port, r); err != nil {
log.Fatal(err)
}
}
My auth service in Angular 8 looks like this:
auth.service
headers = new HttpHeaders({ "Content-Type": "application/json" });
login(username: string, password: string): Promise<any> {
const url: string = `${this.BASE_URL}` + "/login";
const request = this.http
.post(
url,
JSON.stringify({ username: username, password: password }),
{ headers: this.headers }
)
.toPromise();
return request;
}
But this gives me an error message in Chrome:
Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/login' from origin 'http://localhost:4200' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
In the console Gin returns status code 204 though.
I thought this was a CORS issue, so I implemented Gin's CORS middleware:
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:8000"},
AllowMethods: []string{"PUT", "PATCH"},
AllowHeaders: []string{"Origin"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
AllowOriginFunc: func(origin string) bool {
return origin == "https://github.com"
},
MaxAge: 12 * time.Hour,
}))
Unfortunately it still didn't work. Also not if I added the POST method to the allowed methods:
AllowMethods: []string{"PUT", "PATCH", "POST"}
My last try was to use proxy, as described in the Angular documentation:
proxy.conf.json
{
"/api": {
"target": "http://localhost:8000",
"secure": false
}
}
angular.json
"options": {
"browserTarget": "your-application-name:build",
"proxyConfig": "src/proxy.conf.json"
}
I re-started with ng serve, but the error still appears (I changed the urls to /api/login in the auth.service and main.go according to the configuration in the proxy file).
What am I doing wrong here?
Change AllowHeaders: []string{"Origin"} to AllowHeaders: []string{"content-type"};
I need some help trying to integrate aws-sdk-go with localstack to access SQS service.
I tried something like:
result, err := q.Client.SendMessage(&sqs.SendMessageInput{
MessageAttributes: map[string]*sqs.MessageAttributeValue{
"JobName": &sqs.MessageAttributeValue{
DataType: aws.String("String"),
StringValue: aws.String(jobName),
},
},
MessageBody: aws.String(messageBody),
QueueUrl: &q.URL,
})
if err != nil {
return "", err
}
With an initialization like:
type Queue struct {
Client sqsiface.SQSAPI
URL string
}
var q Queue
func init() {
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))
q = Queue{
Client: sqs.New(sess),
URL: viper.GetString("queue.sqs.url"),
}
}
How can I configure the SDK to access localstack's SQS?
I'm trying to create a custom Buffalo (gobuffalo) middleware that accepts config which is working. The issue is I lose the ability to skip the middleware function with this error:
actions/app.go:63:22: cannot use myMiddlewareFunc (type func(myConfig) buffalo.MiddlewareFunc) as type buffalo.MiddlewareFunc in argument to app.Middleware.Skip
The code I've got thus far is:
package actions
import (
"github.com/gobuffalo/buffalo"
"github.com/gobuffalo/buffalo/middleware"
"github.com/gobuffalo/envy"
"github.com/gobuffalo/buffalo/middleware/csrf"
"github.com/gobuffalo/buffalo/middleware/i18n"
"github.com/gobuffalo/packr"
)
// ENV is used to help switch settings based on where the
// application is being run. Default is "development".
var ENV = envy.Get("GO_ENV", "development")
var app *buffalo.App
var T *i18n.Translator
type myConfig struct {
value string
}
// App is where all routes and middleware for buffalo
// should be defined. This is the nerve center of your
// application.
func App() *buffalo.App {
if app == nil {
app = buffalo.New(buffalo.Options{
Env: ENV,
SessionName: "_myapp_session",
})
// Automatically redirect to SSL
app.Use(ssl.ForceSSL(secure.Options{
SSLRedirect: ENV == "production",
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
}))
if ENV == "development" {
app.Use(middleware.ParameterLogger)
}
// Protect against CSRF attacks. https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
// Remove to disable this.
app.Use(csrf.New)
// Wraps each request in a transaction.
// c.Value("tx").(*pop.PopTransaction)
// Remove to disable this.
app.Use(middleware.PopTransaction(models.DB))
// Setup and use translations:
var err error
if T, err = i18n.New(packr.NewBox("../locales"), "en-US"); err != nil {
app.Stop(err)
}
app.Use(T.Middleware())
app.Use(myMiddlewareFunc(myConfig{
value: "test value",
}))
app.Middleware.Skip(myMiddlewareFunc, TestHandler)
app.GET("/", HomeHandler)
app.GET("/test", TestHandler)
app.ServeFiles("/assets", assetsBox)
}
return app
}
// TestHandler is a test handler
func TestHandler(c buffalo.Context) error {
return c.Render(200, r.String("Test1234"))
}
func myMiddlewareFunc(config myConfig) buffalo.MiddlewareFunc {
return func(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {
c.Logger().Info("Test ", config.value)
return next(c)
}
}
}
So #ttomalak's method of creating a method with a receiver worked as well as the two methods below:
mw := myMiddlewareFunc(myConfig{
value: "test value",
})
app.Use(mw)
app.Middleware.Skip(mw, TestHandler)
and
config := myConfig{
value: "test value",
}
app.Use(myMiddlewareFunc(config))
app.Middleware.Skip(myMiddlewareFunc(config), TestHandler)
All ways require calling the middleware method. The method shown here can't be used:
a.Middleware.Skip(Authorization, HomeHandler, ...)
https://github.com/gobuffalo/buffalo/blob/master/middleware.go#L77
I'm new to go but my sense of it is that the methods aren't conforming to the MiddlewareFunc interface so calling them and getting the return type buffalo.MiddlewareFunc is what's allowing it to work.
You need to staisfy required func but your require additional parameters which can't be passed. You probably can change how you approach this by making your myConfig as reciever of myMiddlewareFunc, by this I mean:
type myConfig struct {
value string
}
func (config *myConfig) myMiddlewareFunc() buffalo.MiddlewareFunc {
return func(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {
c.Logger().Info("Test ", config.value)
return next(c)
}
}
}
func App() *buffalo.App {
if app == nil {
app = buffalo.New(buffalo.Options{
Env: ENV,
SessionName: "_myapp_session",
})
// Automatically redirect to SSL
app.Use(ssl.ForceSSL(secure.Options{
SSLRedirect: ENV == "production",
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
}))
if ENV == "development" {
app.Use(middleware.ParameterLogger)
}
// Protect against CSRF attacks. https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
// Remove to disable this.
app.Use(csrf.New)
// Wraps each request in a transaction.
// c.Value("tx").(*pop.PopTransaction)
// Remove to disable this.
app.Use(middleware.PopTransaction(models.DB))
// Setup and use translations:
var err error
if T, err = i18n.New(packr.NewBox("../locales"), "en-US"); err != nil {
app.Stop(err)
}
app.Use(T.Middleware())
c := &myConfig{
value: "test value",
}
app.Use(c.myMiddlewareFunc())
app.Middleware.Skip(c.myMiddlewareFunc(), TestHandler)
app.GET("/", HomeHandler)
app.GET("/test", TestHandler)
app.ServeFiles("/assets", assetsBox)
}
return app
}