How do you override a function created in another module in Golang?
Module A
In one module I have the function NewPersonApiService, the full code is laid out as below:
package openapi
import (
"context"
"errors"
"net/http"
)
// PersonApiService is a service that implements the logic for the PersonApiServicer
// This service should implement the business logic for every endpoint for the PersonApi API.
// Include any external packages or services that will be required by this service.
type PersonApiService struct {
}
// NewPersonApiService creates a default api service
func NewPersonApiService() PersonApiServicer {
return &PersonApiService{}
}
// ShowPerson - Detail
func (s *PersonApiService) ShowPerson(ctx context.Context) (ImplResponse, error) {
// TODO - update ShowPerson with the required logic for this service method.
// Add api_person_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation.
//TODO: Uncomment the next line to return response Response(200, Person{}) or use other options such as http.Ok ...
//return Response(200, Person{}), nil
//TODO: Uncomment the next line to return response Response(0, Error{}) or use other options such as http.Ok ...
//return Response(0, Error{}), nil
return Response(http.StatusNotImplemented, nil), errors.New("ShowPerson method not implemented")
}
Module B
In a separate module I want to override this NewPersonApiService.
I can call this function in the other module by doing the following:
package main
import (
"log"
"net/http"
openapi "build/code/spec/src"
)
func main() {
log.Printf("Server started")
PersonApiService := openapi.NewPersonApiService()
PersonApiController := openapi.NewPersonApiController(PersonApiService)
router := openapi.NewRouter(PersonApiController)
log.Fatal(http.ListenAndServe(":8080", router))
}
But if I try to override the function I get compilation error, unresolved type for openapi, below is what I was attempting to do:
package main
import (
"context"
"log"
"net/http"
openapi "build/code/spec/src"
)
func main() {
log.Printf("Server started")
PersonApiService := openapi.NewPersonApiService()
PersonApiController := openapi.NewPersonApiController(PersonApiService)
router := openapi.NewRouter(PersonApiController)
log.Fatal(http.ListenAndServe(":8080", router))
}
func (s openapi.PersonApiService) ShowPerson(ctx context.Context) (openapi.ImplResponse, error) {
return openapi.Response(200, openapi.Person{}), nil
}
Below is an image of the compilation error
Additional Info:
I believe Module B is properly referencing Module A.
Module A's go.mod file reads as follows:
module build/code/spec
go 1.13
require github.com/go-chi/chi/v5 v5.0.3
Module B's go.mod file reads as follows:
module bakkt.com/boilerplate
go 1.19
replace build/code/spec => ./../build/generated/
require build/code/spec v0.0.0-00010101000000-000000000000
require github.com/go-chi/chi/v5 v5.0.3 // indirect
The solution was to implement the ShowPerson method in another module, you would need to create a new type that implements the PersonApiServicer interface and provides its own implementation of the ShowPerson method.
Running this in Module B worked and allowed me to change the response of the API call defined in Module A.
package main
import (
"context"
"log"
"net/http"
openapi "build/code/spec/src"
)
type MyPersonApiService struct{}
func NewMyPersonApiService() openapi.PersonApiServicer {
return &MyPersonApiService{}
}
func (s *MyPersonApiService) ShowPerson(ctx context.Context) (openapi.ImplResponse, error) {
// TODO: Add your own implementation of the ShowPerson method here.
// For example, you could retrieve a person's details and return them as follows:
person := openapi.Person{Id: 23, Name: "Vark Thins", Age: 20}
return openapi.Response(http.StatusOK, person), nil
}
func main() {
log.Printf("Server started")
PersonApiService := NewMyPersonApiService()
PersonApiController := openapi.NewPersonApiController(PersonApiService)
router := openapi.NewRouter(PersonApiController)
log.Fatal(http.ListenAndServe(":8080", router))
}
Related
I am trying to use an external (non anonymous) function in the routing of my Gin based web server as shown below:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/hi/", Hi)
router.Run(":8080")
}
func (c *gin.Context) Hi() {
c.String(http.StatusOK, "Hello")
}
But I get 2 errors:
./main.go:13:23: undefined: Hi
./main.go:18:6: cannot define new methods on non-local type gin.Context
I am wondering how I can use anonymous functions in my endpoint handlers with gin gonic? All the documentation I've found so far uses anonymous functions.
Thanks!
You can only define a new method for a type in the same package declaring that type. That is, you cannot add a new method to gin.Context.
You should do:
func Hi(c *gin.Context) {
...
package main
import "github.com/gin-gonic/gin"
func main() {
router := gin.Default()
router.GET("/hi", hi)
var n Node
router.GET("/hello", n.hello)
router.GET("/extra", func(ctx *gin.Context) {
n.extra(ctx, "surprise~")
})
router.Run(":8080")
}
func hi(c *gin.Context) {
c.String(200, "hi")
}
type Node struct{}
func (n Node) hello(c *gin.Context) {
c.String(200, "world")
}
func (n Node) extra(c *gin.Context, data interface{}) {
c.String(200, "%v", data)
}
I am new in Golang and need some help.
As you can see in the code below I am tring to create REST API in Golang. I use mux (Gorilla Mux) and pq (PostgreSQL driver) as third party libraries. Don't want to use ORM.
Inside application.go file I have InitializeRoutes function with a list of all aviable routes. GetFactors function process one of these routes. I am tring to define GetFactors function logic in other file called factors.go. Inside factors.go file I want to use Application struct which was defined in application.go. How to make it correctly? Right now as you can see they are in different packages. For thats why factors.go file don't see Application struct.
Project structure:
main.go
application.go
controllers
factors.go
main.go:
package main
func main() {
application := Application{}
application.Initialization()
application.Run("localhost:8000")
}
application.go:
package main
import (
"database/sql"
"github.com/gorilla/mux"
"log"
"net/http"
"rest-api/configurations"
)
type Application struct {
Router *mux.Router
Database *sql.DB
}
func (application *Application) Initialization() {
var err error
application.Database, err = configurations.DatabaseConnection()
if err != nil {
log.Fatal(err)
}
application.Router = mux.NewRouter()
application.Router.StrictSlash(true)
application.InitializeRoutes()
}
func (application *Application) Run(address string) {
log.Fatal(http.ListenAndServe(address, application.Router))
}
func (application *Application) InitializeRoutes() {
application.Router.HandleFunc("/api/factors", application.GetFactors).Methods("GET")
// other code
}
controllers/factors.go:
package controllers
import (
"net/http"
)
func (application *Application) GetFactors(rw http.ResponseWriter, request *http.Request) {
// code
}
Well, finally I decided to redesign the project structure.
main.go
routes
routes.go
controllers
factors.go
models
factors.go
main.go:
import (
"your_project_name/routes"
)
func main() {
// code
router := mux.NewRouter()
routes.Use(router)
// code
}
routes/routes.go:
package routes
import (
"github.com/gorilla/mux"
"your_application_name/controllers"
)
func Use(router *mux.Router) {
router.HandleFunc("/api/factors", controllers.GetFactors).Methods("GET")
}
controllers/factors.go:
package controllers
var GetFactors = func(res http.ResponseWriter, req *http.Request) {
// code
}
I'm trying to access a controller from main.go but I'm getting the following error:
./main.go:34:28: cannot refer to unexported name controllers.getUserDetails
./main.go:34:28: undefined: controllers.getUserDetails
here's a snippet of my main.go, I've removed some extra code
package main
import (
"net/http"
"os"
"log"
"github.com/urfave/negroni"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"Go-Social/controllers"
)
func main() {
router := mux.NewRouter()
UserRouter := router.PathPrefix("/api/user").Subrouter()
UserRouter.HandleFunc("", controllers.getUserDetails).Methods("GET")
env := os.Getenv("GO_ENV")
if "" == env {
env = "Development"
}
// appending middlewares
server := negroni.Classic()
// router handler with negroni
server.UseHandler(router)
// starting server
server.Run(":" + os.Getenv(env + "_PORT"))
}
my controller.go file
package controllers
import (
"net/http"
"fmt"
)
func getUserDetails(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
message := "Hello World"
w.Write([]byte(message))
}
Please Help I'm new to Go. Thanks in advance.
to use a function from another package, you need to export it (GetUserDetails)
as said here
An identifier may be exported to permit access to it from another package
func GetUserDetails(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
message := "Hello World"
w.Write([]byte(message))
}
Since the getUserDetails function is in another package it cannot be accessed. Only functions starting with capital letter can be accessed. That's how encapsulation works in Go.
func GetUserDetails(w http.ResponseWriter, r *http.Request) {
fmt.Println("here")
message := "Hello World"
w.Write([]byte(message))
}
So in your main:
UserRouter.HandleFunc("", controllers.GetUserDetails).Methods("GET")
Language like Java, enCAPSulation in class-based OOP is achieved through private and public class variables / methods.
In Go, encapsulation is achieved on a package level.
In other words, in Go, starting with capital letter for any package object (type, variable or function) will allow you to access it from another package.
I'm having some issues with implementing a slight MVC design with gorilla/mux.
The layout of the modules is as follows:
main.go
-- controllers
---- base.controller.go
---- example.controller.go
-- models
---- base.model.go
---- example.controller.go
All the files in controllers is in the controllers package, same with models and then the main.go is the main package.
Currently I'm just trying to get the Base Controller to be able to be shared with the main package which is working, although it's throwing some errors when trying to implement routes. The build is not throwing any errors, but the routes are not available. If I implement the Walk function in the Gorilla/Mux documentation to print out all the registered routes for the mux.Router then it gives me this error:
&{%!!(MISSING)s(*mux.Router=&{ [0xc4200901b0] map[] true
false false false}) %!!(MISSING)s(http.HandlerFunc=0xc8df0)
[%!!(MISSING)s(*mux.routeRegexp=&{/ false false true false
0xc420095360 / [] []})] %!!(MISSING)s(*mux.routeRegexpGroup=&{
0xc420016240 []}) %!!(MISSING)s(bool=true) %!!(MISSING)s(bool=false)
%!!(MISSING)s(bool=false) %!!(MISSING)s(bool=false)
%!!(MISSING)s(mux.BuildVarsFunc=)}
The reasoning for the global var V1Router *mux.Router is firstly to access it in the main package and also to create subrouters in the other controllers.
I am fairly new to Go, but I'm trying my best to learn the best practices! Any help would be greatly appreciated!
Example code below:
base.controllers.go
package controllers
import (
"fmt"
"bytes"
"net/http"
"github.com/gorilla/mux"
)
var V1Router *mux.Router
func init () {
V1Router = mux.NewRouter()
V1Router.StrictSlash(true)
V1Router.HandleFunc("/", BaseHandler)
}
// Base route to access the API Documentation.
func BaseHandler (w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, Gophers!")
}
main.go
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
In response to the comments, I tried this solution with the same result:
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
r := mux.NewRouter()
r.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Gorilla mux.Router is supposed to be used to create mapping between a set of predefined rules (e.g. host, path, protocol, scheme, etc...) and it's handler (http.Handler or http.HandlerFunc). Gorilla mux can be used to replace standard server mux. If you combine gorilla/mux with built in http server mux as your original question, i.e.
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
what actually happen when a client access /v1 is controllers.V1Router will be called with request path /v1 passed to V1Router1. In the controllers.V1Router, you defined that / will be handled by BaseHandler. However, since incoming request path is /v1, it won't match to your routing table. If you want to define sub routing, you can do as follows (this is what I mean in first comment):
func main () {
r := mux.NewRouter()
v1 := r.PathPrefix("/v1").Subrouter()
controllers.RegisterHandlers(v1)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Then in the controllers (base.controllers.go) define
//Register handlers and it's sub router
func RegisterHandlers(r *mux.Router) {
//base handler, i.e. /v1
r.StrictSlash(true)
r.HandleFunc("/", BaseHandler)
//example sub-router, i.e. /v1/example
ex := r.PathPrefix("/example").Subrouter()
ex.HandleFunc("/", ExampleHandler)
//other handlers...
}
I have a main function, where I initiate a variable, a client. For example:
func main() {
myClient := my.MustNewClient("localhost")
}
Now I want to pass this client to another package, but for some reason I cannot figure out how to do this. My package looks like this:
package rest
import (
"net/http"
"github.com/Sirupsen/logrus"
)
type AssetHandler struct {
mc my.Client
}
func (f AssetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
logrus.Info("bla")
// here I want to use the client
mc.SomeFunctionIntheClient()
}
So my question is, how do I use the client (out of main) in my package?
In the package rest you have to add a constructor function like:
func NewAssetHandler(mc my.Client) AssetHandler {
return AssetHandler{mc}
}
Then you have to instantiate the handler from your main function.
Otherwise you would have to create a separate package where you store global variables. The main package itself can not be used for this because it can't be accessed from somewhere else.