Elsa 3.0 Help

HTTP Workflows

In this guide, we'll take a look at a workflow that can receive HTTP requests, send HTTP requests and write output to the HTTP response object. Our workflow will handle inbound HTTP requests, invoke a backend API using an HTTP call, and write back the response to the client. As a result, we will learn how to use the following HTTP activities:

  • HttpEndpoint

  • SendHttpRequest

  • WriteHttpResponse

  • SetVariable

Before you start

For this guide, we will need the following:

  • An Elsa Server project

  • An Elsa Studio instance

    docker pull elsaworkflows/elsa-studio-v3:latest docker run -t -i -e ASPNETCORE_ENVIRONMENT='Development' -e HTTP_PORTS=8080 -e ELSASERVER__URL=https://localhost:5001/elsa/api -p 14000:8080 elsaworkflows/elsa-studio-v3:latest

Please return here when you are ready.

Workflow Overview

We will define a new workflow called GetUser. The purpose of the workflow is to handle inbound HTTP requests by fetching a user by a given user ID from a backend API and writing them back to the client in JSON format.

For the backend API, we will use reqres.in, which returns fake data using real HTTP responses.

Our workflow will parse the inbound HTTP request by getting the desired user ID from a route parameter and use that value to make an API call to reqres.

The following is an example of such an HTTP request that you can try right now from your browser: https://reqres.in/api/users/2

The response should look similar to this:

{ "data": { "id": 2, "email": "janet.weaver@reqres.in", "first_name": "Janet", "last_name": "Weaver", "avatar": "https://reqres.in/img/faces/2-image.jpg" }, "support": { "url": "https://reqres.in/#support-heading", "text": "To keep ReqRes free, contributions towards server costs are appreciated!" } }

Our workflow will essentially be a proxy sitting in front of the reqres API and return a portion of the response.

Design Workflow

Follow these steps to create the workflow using Elsa Studio

  1. Start Elsa Studio from your browser.

  2. Create a new workflow called Get User

  3. Add the following activities to the design surface:

    • HTTP Endpoint

    • Set Variable

    • HTTP Request (flow)

    • HTTP Response (for 200 OK)

    • HTTP Response (for 404 Not Found)

  4. Create the following variables:

    Name

    Type

    Storage

    RouteData

    ObjectDictionary

    Workflow

    UserId

    string

    Workflow

    User

    Object

    Workflow

  5. Configure the activities as follows:

    HTTP Endpoint

    Property

    Value

    Syntax

    Path

    users/{userid}

    Default

    Supported Methods

    GET

    Default

    Property

    Value

    Route Data

    RouteData

    Property

    Value

    Trigger workflow

    Checked

    Set Variable

    Property

    Value

    Syntax

    Variable

    UserId

    Default

    Value

    return Variables.RouteData["userid"];

    C#

    HTTP Request (flow)

    Property

    Value

    Syntax

    Expected Status Codes

    200, 404

    Default

    Url

    `return $"https://reqres.in/api/users/{Variables.UserId}";`

    C#

    Method

    GET

    Default

    Property

    Value

    Parsed Content

    User

    HTTP Response (200)

    Property

    Value

    Syntax

    Status Code

    OK

    Default

    Content

    getUser().data

    JavaScript

    HTTP Response (404)

    Property

    Value

    Syntax

    Status Code

    NotFound

    Default

    Content

    User not found

    Default

  6. Connect each activity to the next. Ensure that you connect the 200 and 404 outcomes of the HTTP Request (flow) activity to the appropriate HTTP Response activity.

  7. Publish the workflow.

The final result should look like this:

Workflow

Create C# Workflow

Follow these steps to create the workflow from code

  • Create GetUser.cs and add the following code:

    Workflows/GetUser.cs

    using System.Dynamic; using System.Net; using Elsa.Http; using Elsa.Http.Models; using Elsa.Workflows; using Elsa.Workflows.Activities; using Elsa.Workflows.Contracts; namespace WorkflowApp.Web.Workflows; public class GetUser : WorkflowBase { protected override void Build(IWorkflowBuilder builder) { var routeDataVariable = builder.WithVariable<IDictionary<string, object>>(); var userIdVariable = builder.WithVariable<string>(); var userVariable = builder.WithVariable<ExpandoObject>(); builder.Root = new Sequence { Activities = { new HttpEndpoint { Path = new("users/{userid}"), SupportedMethods = new(new[] { HttpMethods.Get }), CanStartWorkflow = true, RouteData = new(routeDataVariable) }, new SetVariable { Variable = userIdVariable, Value = new(context => { var routeData = routeDataVariable.Get(context)!; var userId = routeData["userid"].ToString(); return userId; }) }, new SendHttpRequest { Url = new(context => { var userId = userIdVariable.Get(context); return new Uri($"https://reqres.in/api/users/{userId}"); }), Method = new(HttpMethods.Get), ParsedContent = new(userVariable), ExpectedStatusCodes = { new HttpStatusCodeCase { StatusCode = StatusCodes.Status200OK, Activity = new WriteHttpResponse { Content = new(context => { var user = (dynamic)userVariable.Get(context)!; return user.data; }), StatusCode = new(HttpStatusCode.OK) } }, new HttpStatusCodeCase { StatusCode = StatusCodes.Status404NotFound, Activity = new WriteHttpResponse { Content = new("User not found"), StatusCode = new(HttpStatusCode.NotFound) } } } } } }; } }

Let's go over this workflow section by section.

Workflow Variables

var routeDataVariable = builder.WithVariable<IDictionary<string, object>>(); var userIdVariable = builder.WithVariable<string>(); var userVariable = builder.WithVariable<ExpandoObject>();

Here, we defined 3 workflow variables.

The routeDataVariable variable is used to capture route data output from the HTTP endpoint activity. This variable is a dictionary.

The userIdVariable variable is used to store the user ID value that we get from the routeDataVariable dictionary.

The userVariable variable is used to capture the parsed response from the reqres API call. Since reqres returns JSON content and the capturing variable is of type ExpandoObject, the SendHttpRequest activity will parse the received JSON response into an ExpandoObject.

HttpEndpoint Activity

new HttpEndpoint { Path = new("users/{userid}"), SupportedMethods = new(new[] { HttpMethods.Get }), CanStartWorkflow = true, RouteData = new(routeDataVariable) },

Here we see the HttpEndpoint activity being defined and configured to be a trigger by setting CanStartWorkflow to true.

We set its Path property to respond to users/{userid}. Notice that we are using a route parameter using the name userid. This is the key we will use to grab the provided user ID from the inbound URL path.

To capture the route data, we assign the routeDataVariable variable to the RouteData output of the activity.

SetVariable Activity

new SetVariable { Variable = userIdVariable, Value = new(context => { var routeData = routeDataVariable.Get(context)!; var userId = routeData["userid"].ToString(); return userId; }) },

Here we see the SetVariable activity defined and configured to set the userIdVariable variable to the dictionary entry with key "userid".

We set its Variable property to reference the userIdVariable variable and its Value property to a callback that returns the received user ID from the route data dictionary.

SendHttpRequest Activity

new SendHttpRequest { Url = new(context => { var userId = userIdVariable.Get(context); return new Uri($"https://reqres.in/api/users/{userId}"); }), Method = new(HttpMethods.Get), ParsedContent = new(userVariable), ExpectedStatusCodes = { new HttpStatusCodeCase { StatusCode = StatusCodes.Status200OK, Activity = new WriteHttpResponse { Content = new(context => { var user = (dynamic)userVariable.Get(context)!; return user.data; }), StatusCode = new(HttpStatusCode.OK) } }, new HttpStatusCodeCase { StatusCode = StatusCodes.Status404NotFound, Activity = new WriteHttpResponse { Content = new("User not found"), StatusCode = new(HttpStatusCode.NotFound) } } } }

The SendHttpRequest activity is configured to send an HTTP request to the reqres API endpoint.

We set its Url property to a URL that includes the received user ID.

To capture the response, we assign its ParsedContent output to the userVariable variable.

Since the caller of the workflow might provide user IDs that don't correspond to a user record in the reqres backend, we configure the activity to handle two possible HTTP status codes:

  • 200 OK

  • 404 Not Found

For each of these possible status codes, we assign an appropriate WriteHttpResponse activity.

for the 200 case, the WriteHttpResponse activity access the data field of the user response object received from reqres:

new HttpStatusCodeCase { StatusCode = StatusCodes.Status200OK, Activity = new WriteHttpResponse { Content = new(context => { var user = (dynamic)userVariable.Get(context)!; return user.data; }), StatusCode = new(HttpStatusCode.OK) } },

Run Workflow

Since the workflow uses the HTTP Endpoint activity, it will trigger when we send an HTTP request to the /api/workflows/users/{userId} path.

Try it out by navigating to http://localhost:13000/api/workflows/users/2.

Since the workflow uses the HttpEndpoint activity, it will trigger when we send an HTTP request to the /workflows/users/{userId} path.

Try it out by navigating to https://localhost:5001/workflows/users/2.

The response should look similar to this:

{ "id": 2, "email": "janet.weaver@reqres.in", "first_name": "Janet", "last_name": "Weaver", "avatar": "https://reqres.in/img/faces/2-image.jpg" }

Summary

In this guide, we learned how to define a workflow from code.

In this guide, we learned how to design a workflow using Elsa Studio.

We leveraged the HttpEndpoint activity and used is as a trigger to start the workflow.

The workflow is able to read route parameters and store it in a variable, which we then used as an input to send an API call to the reqres API that in turn returns the requested user.

We have also seen how to handle various responses from reqres: 200 OK and 404 Not Found

The source code for this guide can be found here.

The workflow created in this guide can be found here.

Last modified: 21 September 2024