Network IO: An example with gofiber

There are several way to catch and update your data in your app. I will cover some basic example with go fiber with embbed wasm and assets. You can off course set the assets on the filesystem.

XMLHTTPRequest

We build a page with one button design with bulma.

On click we use a POST request on /app/data with form data set with username=test

func OnClickButton() {

	endpoint, _ := url.Parse("app/data")

	if xhr, err := xmlhttprequest.New(); err == nil {

		xhr.Open("POST", endpoint.String())
		f, _ := formdata.New()

		f.Append("username", "test")

		xhr.SetOnload(func(i interface{}) {

			if text, err := xhr.ResponseText(); err == nil {
				fmt.Printf("Resultat: %s\n", text)
			}

			if header, err := xhr.GetResponseHeader("Content-Type"); err == nil {
				fmt.Printf("Resultat: %s\n", header)
			}

		})
		xhr.Send(f)

	}

}

func main() {
	hogosuru.Init()

	// we get the current document
	if doc, err := document.New(); hogosuru.AssertErr(err) {

		//we got the head
		if head, err := doc.Head(); hogosuru.AssertErr(err) {

			if link, err := htmllinkelement.New(doc); hogosuru.AssertErr(err) {

				link.SetRel("stylesheet")
				link.SetHref("https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css")
				head.AppendChild(link.Node)

			}

		}

		//we get the current body
		if body := doc.Body_(); hogosuru.AssertErr(err) {

			if div, err := htmldivelement.New(doc); hogosuru.AssertErr(err) {

				if list, err := div.ClassList(); hogosuru.AssertErr(err) {
					list.Add("buttons")
				}

				if buttonprimary, err := htmlbuttonelement.New(doc); hogosuru.AssertErr(err) {

					buttonprimary.SetTextContent("Primary")
					//we get the class list attribute
					if list, err := buttonprimary.ClassList(); hogosuru.AssertErr(err) {
						list.Add("button")
						list.Add("is-primary")
					}

					buttonprimary.OnClick(func(e event.Event) {
						OnClickButton()
					})

					div.Append(buttonprimary.Element)
				}

				body.Append(div.Element)

			}
		}
	}

	ch := make(chan struct{})
	<-ch

}

We can test with our example with wasmbrowsertest:

WASM_HEADLESS=off GOOS=js GOARCH=wasm go run ioxmlhttprequest/front/main.go 

This work! if we click on button ,the httprequest is executed but return 404.

It's normal there is no WebServer available. Now if can build or wasm and loaded it on a true webserver like GoFiber πŸ‘

We build our wasm and output to the assets directory of server with command:

WASM_HEADLESS=off GOOS=js GOARCH=wasm  go build -o ioxmlhttprequest/server/assets/app.wasm ioxmlhttprequest/front/main.go

We copy the "glue wasm_exec.js" correspondant to your compiler


cp $(go env GOROOT)/misc/wasm/wasm_exec.js ioxmlhttprequest/server/assets/

We can now write the backend parts. We listen a HTTP servers on port 8080, serve the index.hml , app.wasm and wasm_exec.js

We handle the "/app/data" routing on POST. We log the username value of post.

//go:embed assets/*

var Dist embed.FS

func ServerAssets(app *fiber.App) {

	app.Get("app.wasm", func(c *fiber.Ctx) error {
		wasm, _ := Dist.ReadFile("assets/app.wasm")
		c.Set(fiber.HeaderContentType, "application/wasm")
		return c.Send(wasm)

	})

	app.Use("/", filesystem.New(filesystem.Config{
		Root:       http.FS(Dist),
		PathPrefix: "assets",
	}))
}

func SetData(c *fiber.Ctx) error {
	if username := c.FormValue("username"); username != "" {

		fmt.Printf("Receive POST with %s\n", username)
	}
	return c.Send([]byte("OK"))

}

func Routing(app *fiber.App) {
	approute := app.Group("/app")

	approute.Post("/data", SetData)
}

func main() {

	app := fiber.New(fiber.Config{
		ServerHeader: "hogosuruserver",
	})

	Routing(app)
	ServerAssets(app)
	app.Listen(":8080")
}

Its ok we just need test with:

go run ioxmlhttprequest/server/main.go 

And go to:

http://localhost:8080/

πŸŽ‰We have our front page with a button, when we click on button a post is done and we can that value is send and receive by our backend!


 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 
 β”‚                   Fiber v2.29.0                   β”‚ 
 β”‚               http://127.0.0.1:8080               β”‚ 
 β”‚       (bound on host 0.0.0.0 and port 8080)       β”‚ 
 β”‚                                                   β”‚ 
 β”‚ Handlers ............. 4  Processes ........... 1 β”‚ 
 β”‚ Prefork ....... Disabled  PID ............. 36099 β”‚ 
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 

Receive POST with test

Fetch

The Fetch method is more complicated to use than xmlhttprequest but allow you control more things. A fetch is Promise, so you can use it in the same way and chained it.

The same behaviour will be result to:

func OnClickButton() {

	dataPost := url.Values{}

	var headers map[string]interface{} = map[string]interface{}{"Content-Type": "application/x-www-form-urlencoded"}
	var fetchOpts map[string]interface{} = map[string]interface{}{"method": "POST", "headers": headers, "body": dataPost.Encode()}

	//Start promise and wait result
	if f, err := fetch.New("app/data", fetchOpts); hogosuru.AssertErr(err) {
		f.Then(func(r response.Response) *promise.Promise {

			return nil
		}, func(e error) {

			fmt.Printf("An error occured: %s\n", e.Error())
		})

	}

}

func main() {
	hogosuru.Init()

	// we get the current document
	if doc, err := document.New(); hogosuru.AssertErr(err) {

		//we got the head
		if head, err := doc.Head(); hogosuru.AssertErr(err) {

			if link, err := htmllinkelement.New(doc); hogosuru.AssertErr(err) {

				link.SetRel("stylesheet")
				link.SetHref("https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css")
				head.AppendChild(link.Node)

			}

		}

		//we get the current body
		if body := doc.Body_(); hogosuru.AssertErr(err) {

			if div, err := htmldivelement.New(doc); hogosuru.AssertErr(err) {

				if list, err := div.ClassList(); hogosuru.AssertErr(err) {
					list.Add("buttons")
				}

				if buttonprimary, err := htmlbuttonelement.New(doc); hogosuru.AssertErr(err) {

					buttonprimary.SetTextContent("Primary")
					//we get the class list attribute
					if list, err := buttonprimary.ClassList(); hogosuru.AssertErr(err) {
						list.Add("button")
						list.Add("is-primary")
					}

					buttonprimary.OnClick(func(e event.Event) {
						OnClickButton()
					})

					div.Append(buttonprimary.Element)
				}

				body.Append(div.Element)

			}
		}
	}

	ch := make(chan struct{})
	<-ch

}

We build our wasm and output to the assets directory of server with command:

WASM_HEADLESS=off GOOS=js GOARCH=wasm  go build -o iofetch/server/assets/app.wasm iofetch/front/main.go

We copy the "glue wasm_exec.js" correspondant to your compiler


cp $(go env GOROOT)/misc/wasm/wasm_exec.js ioxmlhttprequest/server/assets/

Its ok we just need test with:

go run iofetch/server/main.go 

And go to:

http://localhost:8080/

Click on button:


 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 
 β”‚                   Fiber v2.29.0                   β”‚ 
 β”‚               http://127.0.0.1:8080               β”‚ 
 β”‚       (bound on host 0.0.0.0 and port 8080)       β”‚ 
 β”‚                                                   β”‚ 
 β”‚ Handlers ............. 4  Processes ........... 1 β”‚ 
 β”‚ Prefork ....... Disabled  PID ............. 41014 β”‚ 
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 

Receive POST with testfetch

Websockets

My wesocket-tester is a good example how to manipulate websockets:

https://github.com/realPy/websocket-tester

Last updated