Golang WebHook using chi router - go

I am creating a service in Go with chi router and I need to implement a WebHook for one of my endpoints. I am using this library to create my webHook. However I am having problems when I try to receive it and decode it. In their sample code they used Gin framework and I have to use chi. My receivedSignature comes out as nil and I get an error: interface conversion: interface {} is nil, not string .
Could anyone please help me with a work around on this?
Here is my send function:
data := []int{1, 2, 3, 4}
// String sent in the GoHook that helps identify actions to take with data
resource := "int-list-example"
// Secret string that should be common to sender and receiver
// in order to validate the GoHook signature
saltSecret := "0014716e-392c-4120-609e-555e295faff5"
hook := &gohooks.GoHook{}
hook.Create(data, resource, saltSecret)
// Will return *http.Response and error
resp, err := hook.Send("http://localhost:3001/")
if err != nil {
fmt.Println("error: ", err)
}
fmt.Println("resp: ", resp)
And here is my receiving logic:
func main(){
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Post("/", receiveWebHook)
http.ListenAndServe(":3001", r)
}
type MyWebhook struct {
Resource string `json:"resource"`
Data []int `json:"data"`
}
func receiveWebHook(w http.ResponseWriter, r *http.Request){
var request MyWebhook
err := json.NewDecoder(r.Body).Decode(&request)
if err !=nil{
fmt.Println("err: ", err)
}
// Shared secret with sender
saltSecret := "0014716e-392c-4120-609e-555e295faff5"
receivedSignature := r.Context().Value(gohooks.DefaultSignatureHeader).(string)
// Verify validity of GoHook
isValid := gohooks.IsGoHookValid(request, receivedSignature, saltSecret)
// Decide what to do if GoHook is valid or not.
if !isValid {
fmt.Println("Not valid, receivedSignature: ", receivedSignature)
}else{
fmt.Println("Valid, receivedSignature: ", receivedSignature)
}
w.WriteHeader(200)
}

The signature will be in a header. So you should also read it from the requests' header.
sig := r.Header.Get(gohooks.DefaultSignatureHeader)

Related

Golang bufio from websocket breaking after first read

I am trying to stream JSON text from a websocket. However after an initial read I noticed that the stream seems to break/disconnect. This is from a Pleroma server (think: Mastodon). I am using the default Golang websocket library.
package main
import (
"bufio"
"fmt"
"log"
"golang.org/x/net/websocket"
)
func main() {
origin := "https://poa.st/"
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
log.Fatal(err)
}
s := bufio.NewScanner(ws)
for s.Scan() {
line := s.Text()
fmt.Println(line)
}
}
After the initial JSON text response, the for-loop breaks. I would expect it to send a new message every few seconds.
What might be causing this? I am willing to switch to the Gorilla websocket library if I can use it with bufio.
Thanks!
Although x/net/websocket connection has a Read method with the same signature as the Read method in io.Reader, the connection does not work like an io.Reader. The connection will not work as you expect when wrapped with a bufio.Scanner.
The poa.st endpoint sends a stream of messages where each message is a JSON document. Use the following code to read the messages using the Gorilla package:
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
defer ws.Close()
for {
_, p, err := ws.ReadMessage()
if err != nil {
log.Fatal(err)
}
// p is a []byte containing the JSON document.
fmt.Printf("%s\n", p)
}
The Gorilla package has a helper method for decoding JSON messages. Here's an example of how to use that method.
url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal(err)
}
defer ws.Close()
for {
// The JSON documents are objects containing two fields,
// the event type and the payload. The payload is a JSON
// document itself.
var e struct {
Event string
Payload string
}
err := ws.ReadJSON(&e)
if err != nil {
log.Fatal(err)
}
// TODO: decode e.Payload based on e.Event
}

Creating nested JSON from API call response

How can I create a nested JSON response from a response I received from an API?
For example, I got a resp (*http.Response) from an API which I expect to be a list of objects ([{},{},{},...])
I want to create a response like so
{
total: 1234,
addresses: [{},{},{}]
}
I'm not quite sure how to deal with this. I got close as my code below returns a similar structure but my addresses section returns an escaped string like so
"[{\"access\":\"INTERNAL\",\"address\":\"1P9SnpTait5bS\"}]"
func (h *Handler) getAddresses(w http.ResponseWriter, r *http.Request) {
type Message struct {
Total int `json:total`
Addresses string `json:addresses`
}
resp, _ := h.Search(address, page, pageOffset) // *http.Response
body, _ := ioutil.ReadAll(resp.Body)
res := Message{
Total: total,
Addresses: string(body),
}
m, _ := json.Marshal(res)
w.Write(m)
}
You can use json.RawMessage if all you need is to pass the json along.
func (h *Handler) getAddresses(w http.ResponseWriter, r *http.Request) {
type Message struct {
Total int `json:"total"`
Addresses json.RawMessage `json:"addresses"`
}
resp, err := h.Search(address, page, pageOffset) // *http.Response
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
res := Message{
Total: total,
Addresses: json.RawMessage(body),
}
if err := json.NewEncoder(w).Encode(res); err != nil {
log.Println("failed to respond with json:", err)
}
}
According to your desired response format, Addresses should be defined as type []map[string]interface rather than of type string.
type Message struct {
Total int `json:total`
Addresses []map[string]interface{} `json:addresses`
}
Then you would json unmarshal the response bytes into a temporary variable of type []map[string]interface{} before assigning the temporary variable to Message.Address, and finally json marshal Message as you were doing already.
Your question currently doesn't demonstrate where total is coming from. I assume you know how to deal with total.

json.Marshal for http post request with echo

I have two golang servers running on localhost.
They are using different ports.
I want to create a post request on one that sends a JSON object to the other one.
I am using the echo framework (if this matters)
The error I am getting is when I try to marshal the object for the post object:
2-valued json.Marshal(data) (value of type ([]byte, error)) where single value is expected
server 1:
type SendEmail struct {
SenderName string `json:"senderName,omitempty" bson:"senderName,omitempty" validate:"required,min=3,max=128"`
SenderEmail string `json:"senderEmail" bson:"senderEmail" validate:"required,min=10,max=128"`
Subject string `json:"subject" bson:"subject" validate:"required,min=10,max=128"`
RecipientName string `json:"recipientName" bson:"recipientName" validate:"required,min=3,max=128"`
RecipientEmail string `json:"recipientEmail" bson:"recipientEmail" validate:"required,min=10,max=128"`
PlainTextContent string `json:"plainTextContent" bson:"plainTextContent" validate:"required,min=10,max=512"`
}
func resetPassword(c echo.Context) error {
email := c.Param("email")
if email == "" {
return c.String(http.StatusNotFound, "You have not supplied a valid email")
}
data := SendEmail{
RecipientEmail: email,
RecipientName: email,
SenderEmail: “test#test”,
SenderName: “name”,
Subject: "Reset Password",
PlainTextContent: "Here is your code to reset your password, if you did not request this email then please ignore.",
}
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", json.Marshal(data))
if err != nil {
fmt.Println(err)
}
defer req.Body.Close()
return c.JSON(http.StatusOK, email)
}
server 2:
e.GET("/", defaultRoute)
func defaultRoute(c echo.Context) (err error) {
u := SendEmail{}
if err = c.Bind(u); err != nil {
return
}
return c.JSON(http.StatusOK, u)
}
It's always nice to meet a Gopher. A few things you might want to know, Go supports multi-value returns in that a function can return more than one value.
byteInfo, err := json.Marshal(data) // has two values returned
// check if there was an error returned first
if err != nil{
// handle your error here
}
Now the line below in your code
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", json.Marshal(data))
Will become this
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", bytes.NewBuffer(byteInfo))
And you can continue with the rest of your code. Happy Coding!
json.Marshal returns []byte and error which means you're passing 4 values to http.NewRequest.
You should call json.Marshal first and then use the result for http.NewRequest.
body, err := json.Marshal(data)
if err != nil {
// deal with error
}
req, err := http.NewRequest("POST", "127.0.0.1:8081/", body)

Go-Gin read request body many times

I am trying to restore the context with it's data after performing validation on it's data.I need the data to keep moving as need it later on in the next function.
I am new to golang and the below code is as far I could go. any help and a better approach is much appreciated.
thanks in advance.
the validation middleware
func SignupValidator(c *gin.Context) {
// Read the Body content
// var bodyBytes []byte
// if c.Request.Body != nil {
// bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
// }
var user entity.User
if err := c.ShouldBindJSON(&user); err != nil {
validate := validator.New()
if err := validate.Struct(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
c.Abort()
return
}
// c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
}
// Read the Body content
var bodyBytes []byte
if c.Request.Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request.Body)
}
fmt.Println(string(bodyBytes)) // this empty
c.Next()
}
route
auth.POST("login", gin.Logger(), validations.SignupValidator, func(ctx *gin.Context) {
ctx.JSON(200, videoController.Save(ctx))
})
You can try this.
ByteBody, _ := ioutil.ReadAll(c.Request.Body)
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(ByteBody))
You can then use ByteBody however you want without side-effects on c.Request.Body
Here is an example of reading body twice with ShouldBindBodyWith, check it:
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
type ParamsOne struct {
Username string `json:"username"`
}
type ParamsTwo struct {
Username string `json:"username"`
}
func main() {
r := gin.New()
r.POST("/", func(c *gin.Context) {
var f ParamsOne
// Read ones
if err := c.ShouldBindBodyWith(&f, binding.JSON); err != nil {
log.Printf("%+v", err)
}
log.Printf("%+v", f)
var ff ParamsTwo
if err := c.ShouldBindBodyWith(&ff, binding.JSON); err != nil {
log.Printf("%+v", err)
}
log.Printf("%+v", ff)
c.IndentedJSON(http.StatusOK, f)
})
r.Run(":4000")
}
Output:
$example: ./example
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] POST / --> main.main.func1 (1 handlers)
[GIN-debug] Listening and serving HTTP on :4000
2020/07/05 10:47:03 {Username:somename}
2020/07/05 10:47:03 {Username:somename}
As #Philidor has shown ShouldBindBodyWith should do the trick, in my case I decided to go with something similar to #spehlivan, because of two reasons:
ShouldBindBodyWith requires that the following binds are also ShouldBindBodyWith, it means I need to change all my previous code, which uses c.Bind
You need to explicitly tell to ShouldBindBodyWith the binding type you are trying to do, JSON, Form, ProtoBuf, etc, other binds like c.Bind detects it automatically.
This is what it looks like:
var input models.SomeInput
bodyCopy := new(bytes.Buffer)
// Read the whole body
_, err := io.Copy(bodyCopy, c.Request.Body)
if err != nil {
log.Println(err)
c.JSON(http.StatusBadRequest, gin.H{"error": "Error reading API token"})
c.Abort()
return
}
bodyData := bodyCopy.Bytes()
// Replace the body with a reader that reads from the buffer
c.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyData))
err = c.Bind(&input)
// Some code here...
// Replace the body with a reader that reads from the buffer
c.Request.Body = ioutil.NopCloser(bytes.NewReader(bodyData))
Pay attention I replaced the c.Request.Body twice, for the bind in that code snippet and then at the end, for the next bind, in another place of my code (this snippet is from a middleware, the next bind is called from the controller).
In my case I needed to do this because the API Token is sent in the request body, which I don't recommend, it should be sent in the request header.

How to make function work with different input types?

I have this simple generic Request struct to make get requests in my app:
package api
import (
"net/http"
"time"
"log"
"app/errors"
)
type Request struct {
Url string
}
func (request *Request) Run(responseObject *AppStatusInfo) *errors.Error {
req, requestErr := http.NewRequest(http.MethodGet, request.Url, nil)
req.Header.Set("Content-Type", "application/json")
timeout := time.Duration(5 * time.Second)
client := &http.Client{
Timeout: timeout,
}
resp, requestErr := client.Do(req)
if requestErr != nil {
return &errors.UnknownError
}
decodeError := DecodeJsonRequestBody(resp, &responseObject)
if (decodeError != nil) {
return &errors.UnknownError
}
defer resp.Body.Close()
return nil
}
Here responseObject has pointer of type AppStatusInfo which is a struct with some fields.
I run it like this to get app status information and put it inside appStatusInfo object:
var appStatusInfo AppStatusInfo
req := Request{
Url:config.Config.ApiUrl,
}
req.Run(&appStatusInfo)
So, this code runs fine.
But, when I want to generalize Request to accept other types of responses, like UserProducts, I don't know how to do it without replacing responseObject *AppStatusInfo with responseObject interface{}, then casting it with responseObject.(UserProducts) which I think can be improved.
So, as soon as there are no generics, how do I make Request.Run() accept different types and return respective objects?
Assuming that DecodeJsonRequestBody passes the second argument to json.Unmarshal or json.Decoder.Decode, then write it like this. I show the changed lines only:
func (request *Request) Run(responseObject interface{}) *errors.Error {
...
resp, requestErr := client.Do(req)
if requestErr != nil {
return &errors.UnknownError
}
defer resp.Body.Close() // defer close before doing anything else
...
decodeError := DecodeJsonRequestBody(resp, responseObject) // don't take address of responseObject
...
}
You can call it like this:
var up UserProducts
err = r.Run(&up)
var asi AppStatusInfo
err = r.Run(&asi)
Type assertions and type conversions are not required.

Resources