Skip to content

Instantly share code, notes, and snippets.

@visopsys
Created March 19, 2023 09:09
Show Gist options
  • Select an option

  • Save visopsys/bc6bf95df74e38382e27de6a08d8e5ff to your computer and use it in GitHub Desktop.

Select an option

Save visopsys/bc6bf95df74e38382e27de6a08d8e5ff to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"time"
)
func postHelloMessage() {
body := []byte(`{
"first_name": "John"
}`)
req, err := http.NewRequest(http.MethodPost, "http://localhost:3333/PostHelloMessage", bytes.NewBuffer(body))
if err != nil {
fmt.Printf("client: could not create request: %s\n", err)
os.Exit(1)
}
req.Header.Set("Content-Type", "application/json")
client := http.Client{
Timeout: 30 * time.Second,
}
res, err := client.Do(req)
if err != nil {
fmt.Printf("client: error making http request: %s\n", err)
os.Exit(1)
}
defer res.Body.Close()
bz, err := ioutil.ReadAll(res.Body)
fmt.Println("Response = ", string(bz))
}
func Redirect() {
req, err := http.NewRequest(http.MethodPost, "http://localhost:3333/Redirect", nil)
if err != nil {
fmt.Printf("client: could not create request: %s\n", err)
os.Exit(1)
}
client := http.Client{
Timeout: 30 * time.Second,
}
res, err := client.Do(req)
if err != nil {
fmt.Printf("client: error making http request: %s\n", err)
os.Exit(1)
}
defer res.Body.Close()
bz, err := ioutil.ReadAll(res.Body)
fmt.Println("Response = ", string(bz))
}
func GetMessage() {
req, err := http.NewRequest(http.MethodPost, "http://localhost:3333/GetMesasge?name=aaaa,bbbb", nil)
if err != nil {
fmt.Printf("client: could not create request: %s\n", err)
os.Exit(1)
}
client := http.Client{
Timeout: 30 * time.Second,
}
res, err := client.Do(req)
if err != nil {
fmt.Printf("client: error making http request: %s\n", err)
os.Exit(1)
}
defer res.Body.Close()
bz, err := ioutil.ReadAll(res.Body)
fmt.Println("Response = ", string(bz))
}
func main() {
// postHelloMessage()
// Redirect()
GetMessage()
}
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"reflect"
)
type T struct{}
type PostRequest struct {
Firstname string `json:"first_name"`
}
type PostResponse struct {
HelloMessage string `json:"hello_message"`
}
type GetResponse struct {
Message string `json:"message"`
}
type Api struct {
}
func (a *Api) PostHelloMessage(extras map[string]any, e PostRequest) (PostResponse, error) {
return PostResponse{
HelloMessage: "hello " + e.Firstname,
}, nil
}
func (a *Api) Redirect(extras map[string]any, params map[string][]string) {
var writer http.ResponseWriter
var request *http.Request
writer = extras["writer"].(http.ResponseWriter)
request = extras["request"].(*http.Request)
http.Redirect(writer, request, "https://example.com", http.StatusSeeOther)
}
func (a *Api) GetMesasge(extras map[string]any, params map[string][]string) (GetResponse, error) {
names := params["name"]
var name string
if len(names) > 0 {
name = names[0]
}
return GetResponse{
Message: "Some message from server to " + name,
}, nil
}
func InvokePostFuncCall(fn interface{}, extras map[string]any, jsonData []byte) []reflect.Value {
vfn := reflect.ValueOf(fn)
// Get first arg of the function
secondArg := reflect.TypeOf(fn).In(1)
// Get the PtrTo to the first function parameter
structPtr := reflect.New(secondArg)
// Convert to Interface
// Note that I can't assert this to .(myStruct) type
instance := structPtr.Interface()
// Unmarshal the JSON
err := json.Unmarshal(jsonData, instance)
if err != nil {
// TODO: return error here.
}
// Call the function
return vfn.Call([]reflect.Value{reflect.ValueOf(extras), structPtr.Elem()})
}
func InvokeGet(fn interface{}, extras map[string]any, params map[string][]string) []reflect.Value {
vfn := reflect.ValueOf(fn)
return vfn.Call([]reflect.Value{reflect.ValueOf(extras), reflect.ValueOf(params)})
}
func TestInvokeFunc() {
// Unmarshal to reflected struct
api := new(Api)
values := InvokePostFuncCall(api.PostHelloMessage, map[string]any{},
[]byte("{\"first_name\": \"beeeeeder\"}"))
fmt.Println("Values[0] = ", values[0])
}
func testServer() {
type handlerMethod struct {
Method string
Handler any
OverrideResponse bool // Set this to true when the handler wants to handle the response write.
}
api := new(Api)
m := map[string]handlerMethod{
"/PostHelloMessage": {
Method: "POST",
Handler: api.PostHelloMessage,
},
"/Redirect": {
Method: "GET",
Handler: api.Redirect,
OverrideResponse: true,
},
"/GetMesasge": {
Method: "GET",
Handler: api.GetMesasge,
},
}
middleware := func(writer http.ResponseWriter, request *http.Request) {
methodHandler, ok := m[request.URL.Path]
if !ok {
writer.WriteHeader(http.StatusNotFound)
return
}
extras := map[string]any{
"request": request,
"writer": writer,
}
var body []byte
var values []reflect.Value
switch methodHandler.Method {
case "POST":
// Read the Json in the body
var err error
body, err = ioutil.ReadAll(request.Body)
if err != nil {
writer.WriteHeader(http.StatusBadRequest)
return
}
values = InvokePostFuncCall(methodHandler.Handler, extras, body)
case "GET":
// Parse the query params
parsedValues, err := url.ParseQuery(request.URL.RawQuery)
if err != nil {
writer.WriteHeader(http.StatusBadRequest)
return
}
values = InvokeGet(methodHandler.Handler, extras, parsedValues)
}
if !methodHandler.OverrideResponse {
switch len(values) {
case 2:
if values[1].IsNil() {
bz, err := json.Marshal(values[0].Interface())
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
return
}
writer.Write(bz)
} else {
// Handle error here.
}
}
} else {
// Do nothing since the function already handled the response.
}
}
mux := http.NewServeMux()
mux.Handle("/", http.HandlerFunc(middleware))
fmt.Println("Starting server")
http.ListenAndServe(":3333", mux)
}
func main() {
testServer()
// Unmarshal()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment