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

1mkdir dni
2cd dni
3func 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:

 1{
 2  "version": "2.0",
 3  "logging": {
 4    "applicationInsights": {
 5      "samplingSettings": {
 6        "isEnabled": true,
 7        "excludedTypes": "Request"
 8      }
 9    }
10  },
11  "extensionBundle": {
12    "id": "Microsoft.Azure.Functions.ExtensionBundle",
13    "version": "[2.*, 3.0.0)"
14  },
15  "customHandler": {
16    "description": {
17      "defaultExecutablePath": "handlers",
18      "workingDirectory": "",
19      "arguments": []
20    },
21    "enableForwardingHttpRequest": true
22  }
23}

Create a new HTTP Trigger Azure Function

Create a new HTTP Trigger Azure Function

1func new -n dni -t httptrigger

Create a golang handler for the function.

Create a handlers.go file with the following contents:

 1package main
 2
 3import (
 4	"encoding/json"
 5	"fmt"
 6	"log"
 7	"net/http"
 8	"os"
 9	"strconv"
10	"strings"
11)
12
13type InvokeRequest struct {
14	Data     map[string]json.RawMessage
15	Metadata map[string]interface{}
16}
17
18type InvokeResponse struct {
19	Outputs     map[string]interface{}
20	Logs        []string
21	ReturnValue interface{}
22}
23
24func dniHandler(w http.ResponseWriter, r *http.Request) {
25	ua := r.Header.Get("User-Agent")
26	fmt.Printf("user agent is: %s \n", ua)
27	invocationid := r.Header.Get("X-Azure-Functions-InvocationId")
28	fmt.Printf("invocationid is: %s \n", invocationid)
29
30	queryParams := r.URL.Query()
31
32	if dni := queryParams["dni"]; dni != nil {
33		valid := validateDNI(dni[0])
34		js, err := json.Marshal(valid)
35		if err != nil {
36			http.Error(w, err.Error(), http.StatusInternalServerError)
37			return
38		}
39
40		w.Header().Set("Content-Type", "application/json")
41		w.Write(js)
42	} else {
43		http.Error(w, "dni query parameter not present", http.StatusInternalServerError)
44	}
45}
46
47func validateDNI(dni string) bool {
48	table := "TRWAGMYFPDXBNJZSQVHLCKE"
49	foreignerDigits := map[string]string{
50		"X": "0",
51		"Y": "1",
52		"Z": "2",
53	}
54	parsedDNI := strings.ToUpper(dni)
55	if len(parsedDNI) == 9 {
56		checkDigit := parsedDNI[8]
57		parsedDNI = parsedDNI[:8]
58		if foreignerDigits[strings.ToUpper(string(parsedDNI[0]))] != "" {
59			parsedDNI = strings.Replace(parsedDNI, string(parsedDNI[0]), foreignerDigits[string(parsedDNI[0])], 1)
60		}
61		dniNumbers, err := strconv.Atoi(parsedDNI)
62		if err != nil {
63			fmt.Println("Error during conversion")
64		}
65
66		return table[dniNumbers%23] == checkDigit
67	}
68	return false
69}
70
71func main() {
72	customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
73	if !exists {
74		customHandlerPort = "8080"
75	}
76	mux := http.NewServeMux()
77	mux.HandleFunc("/api/dni", dniHandler)
78	fmt.Println("Go server Listening on: ", customHandlerPort)
79	log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))
80}

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:

1go build ./handlers.go

Test the Azure Function locally

To test the Azure Function locally run the following command:

1func start --verbose

and from another terminal window run the following command:

1curl -i http://localhost:7071/api/dni?dni=59658914L

You should get a response similar to the following:

1HTTP/1.1 200 OK
2Content-Length: 4
3Content-Type: application/json
4Date: Sun, 26 Mar 2023 11:01:24 GMT
5Server: Kestrel
6
7true

Download all code and files here.

Hope it helps!