Watch the recording of this lesson on YouTube 🎥.
The goal of this lesson is to create your first function which can be triggered by doing an HTTP GET or POST to the function endpoint.
This lessons consists of the following exercises:
📝 Tip - If you're stuck at any point you can have a look at the source code in this repository.
📝 Tip - If you have questions or suggestions about this lesson, feel free to create a Lesson Q&A discussion here on GitHub.
Prerequisite | Exercise |
---|---|
An empty local folder / git repo | 1-5 |
Azure Functions Core Tools | 1-5 |
VS Code with Azure Functions extension | 1-5 |
Rest Client for VS Code or Postman | 1-5 |
See Python prerequisites for more details.
In this exercise, you'll be creating a Function App with the default HTTPTrigger and review the generated code.
-
In VSCode, create the Function App by running
AzureFunctions: Create New Project
in the Command Palette (CTRL+SHIFT+P). -
Browse to the location where you want to save the function app (e.g. AzureFunctions.Http).
📝 Tip - Create a folder with a descriptive name since that will be used as the name for the project.
-
Select the language you will be using to code the function. In this lesson we will be using
python
. -
Select
HTTP trigger
as the template. -
Give the function a name (e.g.
HelloWorldHttpTrigger
). -
Select
Function
for the AccessRights.🔎 Observation - Now a new Azure Functions project is being generated. Once it's done, look at the files in the project. You will see the following:
File Description HelloWorldHttpTrigger/__init__.py
The Python file containing your function code exported as an Azure Function. HelloWorldHttpTrigger/function.json
The Azure Function configuration comprising the function's trigger, bindings, and other configuration settings. requirements.txt
Contains the required Python packages. host.json
Contains global configuration options for all functions in a function app. local.settings.json
Contains app settings and connection strings for local development. 📝 Tip - When you create a Function App, VSCode automatically generates a virtual environment for you. If you navigate to the functions directory you can activate it using
source .venv/bin/activate
. If you install new Python packages you update yourrequirements.txt
file usingpip freeze > requirements.txt
.❔ Question - Review the generated HTTPTrigger function. What is it doing?
-
Install the dependencies defined in the
requirements.txt
viapip install -r requirements.txt
in a shell of your choice. -
Start the Function App by pressing
F5
or viafunc host start
.🔎 Observation - You should see an HTTP endpoint in the output.
-
Now call the function by making a GET request to the above endpoint using a REST client:
GET http://localhost:7071/api/HelloWorldHttpTrigger?name=YourName
📝 Tip - You can use Postman as a REST client.
❔ Question - What is the result of the function? Is it what you expected?
❔ Question - What happens when you don't supply a value for the name?
Let us change the template to find out what parameters can be changed. Depending on the trigger, arguments can be added/removed. Start with only allowing GET requests.
-
Remove the
"post"
entry from the"methods"
array in thefunction.json
file. Now the function can only be triggered by a GET request. -
Switch to the file
__init__.py
. Here we leave thereq.params
parameter unchanged. However, we will remove thebody
parameters associated with the POST request. -
Remove the POST content of the function (but keep the function definition). We'll be writing a new implementation.
-
To get the name from the query string you can do the following:
name = req.params.get('name')
🔎 Observation - In the generated template the response object always returns an HTTP status 200. Let's make the function a bit smarter and return a response object with HTTP status 400.
-
Add an
if
statement to the function that checks if the name value isnull
, an empty string orundefined
. If this is this case we return an HTTP code 400 as response, otherwise we return an HTTP code 200.if name: return func.HttpResponse( f"Hello, {name}. This HTTP triggered function executed successfully.", status_code=200 ) else: return func.HttpResponse( "Pass a name in the query string or in the request body for a personalized response", status_code=400 )
Now the function has proper return values for both correct and incorrect invocations.
📝 Tip - This solution hard codes the HTTP status codes. To make the handling more consistent you can either define your own enumerations for the HTTP codes.
-
Run the function, once without name value in the query string, and once with a name value.
❔ Question - Is the outcome of both runs as expected?
Let's change the function to also allow POST requests and test it by posting a request with JSON content in the request body.
-
Add the
"post"
entry in the"methods"
array in thefunction.json
file. -
The function in the
__init__.p
file currently only handles GET requests. We need to add some logic to use the query string for GET and the request body for POST requests. This can be done by checking the method property of the request parameter as follows:method_type = req.method if method_type == "GET": # Get name from query string # name = ... elif method_type == "POST": # Get name from body # name = ...
-
We will assign values to the variable
name
variable depending on the HTTP method. -
Move the query string logic inside the
if
statement that handles the GET request. Theif
-branch handling GET requests should look like this:method_type = req.method if method_type == "GET": name = req.params.get('name')
-
Now let's add the code to extract the name from the body for a POST request. The
else if
-branch handling the POST request should look like this:elif method_type == "POST": try: req_body = req.get_json() except ValueError: name = None else: name = req_body.get('name')
📝 Tip - We need to check if the body exists at all before we assign the value.
-
We also adopt the message in case of an invalid request to handle the two different error situations. The construction of the response object has the following form after our changes:
if name: return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.") status_code=200 else: return func.HttpResponse( "Pass a name in the query string (GET request) or a JSON body with the attribute name (POST request) for a personalized response.",", status_code=400 )
-
Now run the function and do a POST request and submit JSON content with a
name
attribute. If you're using the VSCode REST client you can use this in a .http file:POST http://localhost:7071/api/HelloWorldHttpTrigger Content-Type: application/json { "name": "Your name" }
❔ Question - Is the outcome of the POST as expected?
❔ Question - What is the response when you use an empty
name
property?
Let's change the function to map the the requst with JSON content to a Python class object. Since we only storing state we can use a dataclass.
-
Copy & paste the folder of the Azure Function from the exercise above and give it a new name e.g.
CustomGreetingHttpTrigger
.📝 Tip - Function names need to be unique within a Function App.
-
Adjust the
"scriptfile"
attribute in thefunction.json
file to the new filename to get a consistent transpilation. -
Remove the
"get"
entry from the"methods"
array in thefunction.json
file. Now the function can only be triggered by a POST request. -
Switch to the the
_init_.py
file and add a new interface namedPerson
to the_init_.py
file.@dataclass class Person: name: str = None
-
Remove the logic inside the function which deals GET Http verb and with the query string.
-
Rewrite the function logic that the request body is assigned to the interface
Person
.try: req_body = req.get_json() except ValueError: person = Person(name=None) else: person = Person(name=req_body.get('name'))
-
Update the logic which checks if the
name
variable is empty. You can now useperson.name
instead. However, be aware that the request body can be empty which would result in an undefined assignment of the attributename
in theif
statement, so we must still check that the person is not undefined. The updated code should look like this:if (person.name): return func.HttpResponse(f"Hello, {person.name}. This HTTP triggered function executed successfully.") status_code=200 else: return func.HttpResponse( "Pass a name in the request's JSON body with the attribute name (POST) for a personalized response." responseStatus = 400 )
-
Run the function.
🔎 Observation You should see two HTTP endpoints in the output of the console.
-
Trigger the new endpoint by making a POST request.
❔ Question Is the outcome as expected?
Ready to get hands-on? Checkout the homework assignment for this lesson.
For more info about the HTTP Trigger have a look at the official Azure Functions HTTP Trigger documentation.
We love to hear from you! Was this lesson useful to you? Is anything missing? Let us know in a Feedback discussion post here on GitHub.