Azure Function HTTP Trigger with Golang
Categories:
3 minute read
Back in 2017 I wrote a post about how to run a precompiled .NET Core Azure Function in a container. Fast forward to 2023 and, as some of you know, I’ve been playing with Golang for a while now so I thought it was about time to translate the .NET code and make it work with Golang.
Prerequisites:
Create an Azure Function with a custom worker runtime
Create an Azure Function with a custom worker runtime
mkdir dni
cd dni
func init --worker-runtime custom
Edit the host.json file
Modify the defaultExecutablePath
to the name of the executable file you want to run. In this case: handler. Also, set the enableForwardingHttpRequest
to true
so the function can access the HTTP request:
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[2.*, 3.0.0)"
},
"customHandler": {
"description": {
"defaultExecutablePath": "handlers",
"workingDirectory": "",
"arguments": []
},
"enableForwardingHttpRequest": true
}
}
Create a new HTTP Trigger Azure Function
Create a new HTTP Trigger Azure Function
func new -n dni -t httptrigger
Create a golang handler for the function.
Create a handlers.go
file with the following contents:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
)
type InvokeRequest struct {
Data map[string]json.RawMessage
Metadata map[string]interface{}
}
type InvokeResponse struct {
Outputs map[string]interface{}
Logs []string
ReturnValue interface{}
}
func dniHandler(w http.ResponseWriter, r *http.Request) {
ua := r.Header.Get("User-Agent")
fmt.Printf("user agent is: %s \n", ua)
invocationid := r.Header.Get("X-Azure-Functions-InvocationId")
fmt.Printf("invocationid is: %s \n", invocationid)
queryParams := r.URL.Query()
if dni := queryParams["dni"]; dni != nil {
valid := validateDNI(dni[0])
js, err := json.Marshal(valid)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
} else {
http.Error(w, "dni query parameter not present", http.StatusInternalServerError)
}
}
func validateDNI(dni string) bool {
table := "TRWAGMYFPDXBNJZSQVHLCKE"
foreignerDigits := map[string]string{
"X": "0",
"Y": "1",
"Z": "2",
}
parsedDNI := strings.ToUpper(dni)
if len(parsedDNI) == 9 {
checkDigit := parsedDNI[8]
parsedDNI = parsedDNI[:8]
if foreignerDigits[strings.ToUpper(string(parsedDNI[0]))] != "" {
parsedDNI = strings.Replace(parsedDNI, string(parsedDNI[0]), foreignerDigits[string(parsedDNI[0])], 1)
}
dniNumbers, err := strconv.Atoi(parsedDNI)
if err != nil {
fmt.Println("Error during conversion")
}
return table[dniNumbers%23] == checkDigit
}
return false
}
func main() {
customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
if !exists {
customHandlerPort = "8080"
}
mux := http.NewServeMux()
mux.HandleFunc("/api/dni", dniHandler)
fmt.Println("Go server Listening on: ", customHandlerPort)
log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))
}
Note: THe code intends to validate a Spanish DNI (Documento Nacional de Identidad) number. It gets the DNI number from the query string and returns a boolean value indicating if the DNI number is valid or not.
Build the executable:
go build ./handlers.go
Test the Azure Function locally
To test the Azure Function locally run the following command:
func start --verbose
and from another terminal window run the following command:
curl -i http://localhost:7071/api/dni?dni=59658914L
You should get a response similar to the following:
HTTP/1.1 200 OK
Content-Length: 4
Content-Type: application/json
Date: Sun, 26 Mar 2023 11:01:24 GMT
Server: Kestrel
true
Download all code and files here.
Hope it helps!