Lob's website experience is not optimized for Internet Explorer.
Please choose another browser.

Arrow Up to go to top of page
Hero Image for Lob Deep Dives Blog PostUsing Lob with C# and Flurl
Engineering
November 16, 2021

Using Lob with C# and Flurl

Share this post

2022 update for our dotnet fans: Lob has released a C#/.NET SDK; fully generated using an Open Api 3.0 spec. (Repo / Download the package)

At Lob, our mission is to connect the online and offline world. We support developers in this pursuit by providing direct mail and address verification APIs. Today we are diving into C# to show how to easily implement Lob’s API in your ASP.Core application using the Flurl Http Library. Now, there is no particular reason we are using Flurl in this example. You can easily implement Lob API in whatever HTTP client you choose—RestSharp, ServiceStack, etc. We happened to choose Flurl because it’s the latest hotness.

The complete code for this project is available here.

Prerequisites

You will need the following installed:

.Net 5 - installation instructions here.
An IDE, we used VSCode in this tutorial, but Visual Studio works as well.

Getting Started

We’ll start with a simple Razor application and make use of the address and postcard APIs that Lob provides. With this application we’ll do the following:

  1. Save an address to our account’s address book
  2. Create a postcard using the address and preview it.

First, create a project folder by running this command in your terminal.

dotnet new webapp -o LobExampleApp

This creates a “LobExampleApp” folder to house our code. Change to this directory and run.

dotnet run watch

Pointing your browser to https://localhost:5001 will bring up a welcome message.

If you have trouble getting this screen, here are some troubleshooting docs

The Flurl library is a required dependency. Run the following command in your terminal:

dotnet add package Flurl.Http --version 3.2.0

Login to your Lob account and locate your API keys under Settings. If you don’t have an account yet, It's easy to sign up and absolutely no cost to start sending requests.

API keys, database credentials, etc, should be environment variables to avoid hardcoding them into your project. Nothing is worse than pushing a commit and seeing sensitive login information in the repo. To avoid doing this, we make our Lob API key an environment variable in the `Projects/launchSettings.json` file. Simply add a key called “LOB_API_KEY” to the “environmentVariables” section. Here is an example.

{
 "iisSettings": {
   "windowsAuthentication": false,
   "anonymousAuthentication": true,
   "iisExpress": {
     "applicationUrl": "http://localhost:22988",
     "sslPort": 44381
   }
 },
 "profiles": {
   "IIS Express": {
     "commandName": "IISExpress",
     "launchBrowser": true,
     "environmentVariables": {
       "ASPNETCORE_ENVIRONMENT": "Development"
     }
   },
   "LobExampleApp": {
     "commandName": "Project",
     "dotnetRunMessages": "true",
     "launchBrowser": true,
     "applicationUrl": "https://localhost:5001",
     "environmentVariables": {
       "ASPNETCORE_ENVIRONMENT": "Development",
       "LOB_API_KEY": "<INSERT_YOUR_API_KEY>"
     }
   }
 }
}

Now, let’s create a class that uses our API key to make API requests.

Connect to Lob

The good thing about statically typed languages like C# is that they force us to think about the interaction between our application and external services. We start with a few POCOs (Plain Old C# Objects) that represent messages sent and responses received from Lob API. Making these POCOs helps us when using Flurl to make API calls.

LobModels.cs

using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel.DataAnnotations;

namespace LobExampleApp
{
   public class LobAddressListResult
   {
       public List<LobAddressResult> data { get; set; }
   }

   public class LobAddressResult
   {
       public string id { get; set; }
       public string description { get; set; }
       [Required, StringLength(40, MinimumLength = 3)]
       public string name { get; set; }
       public string company { get; set; }
       public string phone { get; set; }
       public string email { get; set; }
       [Required, StringLength(64)]
       public string address_line1 { get; set; }
       [Required, StringLength(200)]
       public string address_city { get; set; }
       [Required]
       public string address_state { get; set; }
       [Required]
       public string address_zip { get; set; }
       public string address_country { get; set; }
       public string date_created { get; set; }
       public string date_modified { get; set; }
   }

   public class LobPostcardRequest
   {
       public string to { get; set; }
       public string front { get; set; }
       public string back { get; set; }
       public string description { get; set; }
       public object merge_variables { get; set; }
   }

   public class LobPostcardResponse
   {
       public dynamic to { get; set; }
       public string carrier { get; set; }
       public string id { get; set; }
       public string front_template_id { get; set; }
       public string back_template_id { get; set; }
       public string front_template_version_id { get; set; }
       public string back_template_version_id { get; set; }
       public string url { get; set; }
       public dynamic thumbnails { get; set; }
   }

   public class ErrorInfo
   {
       public string message { get; set; }
       public int status_code { get; set; }
       public string code { get; set; }
   }

   public class LobError
   {
       public dynamic error { get; set; }
   }
}

Note, we add extra annotations in the `AddressResult` class in order to use this model to send requests to Lob. We take advantage of automatic validation included in Razor pages, which will come into play later on.

With our POCOs in place, let’s create a class to handle the interaction between our application and Lob API.

LobConnector.cs

using Flurl;
using Flurl.Http;
using System.Threading.Tasks;
using System;
using System.IO;

namespace LobExampleApp
{
   public class LobConnector
   {
       private string baseUrl = "https://api.lob.com/v1/";

       private string apiKey = Environment.GetEnvironmentVariable("LOB_API_KEY");
       
       public async Task<LobAddressListResult> listAddresses()
       {
           var url = this.baseUrl + "addresses";
           LobAddressListResult resp = await url.WithBasicAuth(this.apiKey, "").GetJsonAsync<LobAddressListResult>();
           return resp;
       }

       public async Task<LobAddressResult> saveAddress(LobAddressResult address)
       {
           var url = this.baseUrl + "addresses";
           LobAddressResult resp = await url.WithBasicAuth(this.apiKey, "").PostJsonAsync(new {
               description = "",
               name = address.name,
               company = address.company,
               email = address.email,
               phone = address.phone,
               address_line1 = address.address_line1,
               address_line2 = "",
               address_city = address.address_city,
               address_state = address.address_state,
               address_zip = address.address_zip,
               address_country = address.address_country
           }).ReceiveJson<LobAddressResult>();
           return resp;
       }

       public async Task<LobPostcardResponse> previewPostcard(LobPostcardRequest postcard)
       {
           var url = this.baseUrl + "postcards";
           LobPostcardResponse resp = await url.WithBasicAuth(this.apiKey, "").PostJsonAsync(postcard)
               .ReceiveJson<LobPostcardResponse>();
           // We wait a few seconds to allow for the thumbnails to populate. All Postcards created via
           // the Lob API go through a print-ready checklist to make sure that what you see is exactly how
           // the final printed results will be.
           await Task.Delay(5000);
           return resp;
       }
   }
}

Environment variables from `launchSettings.json` are read and used in each method which corresponds to API endpoints provided by Lob. For example, listAddresses maps to Lob’s address endpoint. Looking closer, you’ll see this pattern repeated across the class.

On line 16, we append the endpoint that we are going to connect to. In this example, we hit the “/addresses” endpoint. On line 17, we authenticate to Lob using Basic Authentication. Our Lob API key is placed where the username would be and we leave the password empty. Flurl sets the basic-auth headers using the WithBasicAuth method. For those using RestSharp it looks like this.

var client = new RestClient("https://api.lob.com/v1");
client.Authenticator = new HttpBasicAuthenticator(Environment.GetEnvironmentVariable("LOB_API_KEY"), "");
var getRequest = new RestRequest("addresses");
var resp = client.Execute<LobAddressListResult>(getRequest);

A good thing about Flurl is the ability to cast a JSON response into C# objects via POCOs. This functionality is supported in Flurl and RestSharp.

List and save addresses

`LobConnector.cs` holds our app core logic where we create and update data. Let’s connect this core to the rest of the application by altering `Pages/Index.cshtml.cs` (a razor page) with the following code:

LobConnector.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using LobExampleApp;

namespace LobExampleApp.Pages
{
   public class IndexModel : PageModel
   {
       private readonly ILogger<IndexModel> _logger;
       public List<LobAddressResult> addressResults;

       public IndexModel(ILogger<IndexModel> logger)
       {
           _logger = logger;
       }

       public async Task OnGet()
       {
           var api = new LobConnector();
           var addresses = await api.listAddresses();
           this.addressResults = addresses.data;
       }
   }
}

Razor routes use a convention over configuration approach. `Index.cshtml.cs` corresponds to the C(ontroller) of the MVC paradigm. `Index.cshtml` corresponds to the V(iew) of this paradigm. The model declared in the file is available for use in Index.cshtml. For this reason, we set a property called addresses that is populated with the listAddresses method response from the LobConnector class.

Index.cshtml.cs

@page
@model IndexModel
@{
   ViewData["Title"] = "Lob Sample App";
}

@if (Model.addressResults.Count > 0) {
<p>Enter in an address to see first hand how easy it is to send mail via Lob. This sample application will show off two different aspects of the Lob API -- Postcards and the Address Book.</p>
<a class="btn btn-primary" href="/new">Add New Address</a>
<br /><br />
<table class="table table-hover">
   <thead class="thead-dark">
       <tr>
           <th scope="col">ID</th>
           <th scope="col">Address</th>
           <th scope="col">City</th>
           <th scope="col">State</th>
           <th scope="col">Zip</th>
           <th scope="col">Action</th>
       </tr>
   </thead>
   <tbody>
       @for (var i = 0; i < Model.addressResults.Count; i++) {
       <tr>
           <th scope="row">@Model.addressResults[i].id</th>
           <td>@Model.addressResults[i].address_line1</td>
           <td>@Model.addressResults[i].address_city</td>
           <td>@Model.addressResults[i].address_state</td>
           <td>@Model.addressResults[i].address_zip</td>
           <td><a class="btn btn-primary" asp-page="Postcard"  asp-route-address="@Model.addressResults[i].id" asp-route-variable1="Access" asp-route-variable2="22">View Postcard Preview</a></td>
       </tr>
       }
   </tbody>
</table>
} else {
   <p>No Addresses saved! Add one by going <a asp-page="./New">here</a></p>
}

We reference the model in `Index.cshtml.cs` on line 2 and check to see if the addresses property has a length longer than 0. If not, we prompt the user to enter an address. If the length is longer than 0, then we list all of the addresses returned, adding a link to preview a postcard. There is a lot going on behind the postcard preview link, but first, let’s focus on adding a new address.

On lines 9 and 36, we reference the ./New route. Remember Razor follows a convention to control routing. This means the framework looks for two files -- New.cshtml and New.cshtml.cs in the same directory as Index.cshtml. Let’s start with the `New.cshtml.cs` file first.

New.cshtml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using LobExampleApp;

namespace LobExampleApp.Pages
{
   public class NewAddressModel : PageModel
   {
       private readonly ILogger<IndexModel> _logger;

       public NewAddressModel(ILogger<IndexModel> logger)
       {
           _logger = logger;
       }

       [BindProperty]
       public LobAddressResult addressResult { get; set; }

       public async Task<IActionResult> OnPost(LobAddressResult addressResult)
       {
           if (!ModelState.IsValid)
           {
               return Page();
           }

           var api = new LobConnector();
           var resp = await api.saveAddress(addressResult);
           
           return RedirectToPage("./Index");
       }
   }
}

On line 20, use a “BindProperty” attribute on the model’s addressResult property. This makes for easy validation on line 25.

“But Shariq, how does Razor know the validation rules?”, you may ask.

Well, let’s take a look at LobModels.cs. at the validation rules we defined when creating the class.

public class LobAddressResult
{
   public string id { get; set; }
   public string description { get; set; }
   
   [Required, StringLength(40, MinimumLength = 3)]
   public string name { get; set; }
   
   public string company { get; set; }
   public string phone { get; set; }
   public string email { get; set; }
   
   [Required, StringLength(64)]
   public string address_line1 { get; set; }
   
   [Required, StringLength(200)]
   public string address_city { get; set; }
   
   [Required]
   public string address_state { get; set; }
   
   [Required]
   public string address_zip { get; set; }
   
   public string address_country { get; set; }
   public string date_created { get; set; }
   public string date_modified { get; set; }
}

We use `Required` and `StringLength` attributes on several properties letting Razor know the validation rules.

Now, add the following code to the New.cshtml:

New.cshtml

@page
@model NewAddressModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Information entered here will be saved to your account <a href="https://dashboard.lob.com/#/addresses">Address Book</a>. Addresses entered here are verified, sanitized, and formatted in a standardized way. They can also be referenced later for other API calls.</p>
<p>You can read more about the <a href="https://docs.lob.com/#tag/Addresses">Address Book API</a> on the Lob Docs. </p>

<form method="post">
   <div asp-validation-summary="ModelOnly" class="text-danger"></div>

   <div class="form-group">
       <label for="addressResult.name">Name</label>
       <input type="text" class="form-control" asp-for="addressResult.name" name="addressResult.name">
       <span asp-validation-for="addressResult.name" class="text-danger"></span>
   </div>

   <div class="form-group">
       <label for="addressResult.company">Company</label>
       <input type="text" class="form-control" name="addressResult.company">
   </div>

   <div class="form-group">
       <label for="addressResult.address_line1">Address Line 1</label>
       <input type="text" class="form-control" name="addressResult.address_line1">
       <span asp-validation-for="addressResult.address_line1" class="text-danger"></span>
   </div>

   <div class="form-group">
       <label for="addressResult.city">City</label>
       <input type="text" class="form-control" name="addressResult.address_city">
       <span asp-validation-for="addressResult.address_city" class="text-danger"></span>
   </div>

   <div class="form-group">
       <label for="addressResult.state">State</label>
       <input type="text" class="form-control" name="addressResult.address_state">
       <span asp-validation-for="addressResult.address_state" class="text-danger"></span>
   </div>

   <div class="form-group">
       <label for="addressResult.zip">Zip</label>
       <input type="text" class="form-control" name="addressResult.address_zip">
       <span asp-validation-for="addressResult.address_zip" class="text-danger"></span>
   </div>

   <button type="submit" class="btn btn-primary">Submit</button>
</form>

At this point, you should be able to add and save addresses to your Lob address book. Add a few addresses because, in the next section, we generate a preview of a postcard using these saved addresses.

Create a postcard and preview

For our last step, we generate a preview of a postcard via Lob’s API. We take a couple of query parameters (Variable1 and Variable2, respectively) and merge these into the postcard along with the selected address.

We start by creating `Postcard.cshtml.cs` and adding the code is below:

Postcard.cshtml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using LobExampleApp;
using Flurl.Http;
using System.Threading;

namespace LobExampleApp.Pages
{
   public class PostcardModel : PageModel
   {
       private readonly ILogger<IndexModel> _logger;

       protected LobConnector api;
       protected string htmlTextFront;
       protected string htmlTextBack;
       public string previewURLFront;
       public string previewURLBack;
       public string? variable1;
       public string? variable2;
       public string activeAddress;

       public PostcardModel(ILogger<IndexModel> logger)
       {
           _logger = logger;
           // Create a template with the name "C# Postcard - Front" and "C# Postcard - Back"
           this.api = new LobConnector();
           this.htmlTextFront = "<html><body>This postcard was sent with Lob.</body></html>";
           this.htmlTextBack = "<html><body>This card was generated using C#. These are the merge variables for this template: 'variable1' and 'variable2'. Variable 1 is {{variable1}}. Variable 2 is {{variable2}}.</body></html>";
       }

       public async Task OnGet()
       {
           // Now we make a call to the Postcards API to generate a preview of the card that we are going to send
           this.activeAddress = Request.Query["address"];
           Console.WriteLine(Request.Query["variable2"]);
           this.variable1 = Request.Query["variable1"] == "" ? "Access" : Request.Query["variable1"];
           this.variable2 = Request.Query["variable2"] == "" ? "22" : Request.Query["variable2"];

           var postcardRequest = new LobPostcardRequest
           {
               to = this.activeAddress,
               front = this.htmlTextFront,
               back = this.htmlTextBack,
               description = "C# Postcard",
               merge_variables = new { variable1 = this.variable1, variable2 = this.variable2 }
           };

           try
           {
               var postcardResp = await this.api.previewPostcard(postcardRequest);
               Console.WriteLine(postcardResp.thumbnails[0].small);
               this.previewURLFront = postcardResp.thumbnails[0].large;
               this.previewURLBack = postcardResp.thumbnails[1].large;
           }
           catch (FlurlHttpException ex)
           {
               Console.WriteLine("error!");
               var err = await ex.GetResponseJsonAsync<LobError>();
               Console.WriteLine(err.error);
           }
       }
   }
}

On lines 34 and 35, we set the template for the postcard. This is a bare-bones example of a postcard, but feel free to add CSS, images, etc to your own template. You should follow these guidelines to make sure that your template is print-ready.

On lines 39-42, we grab the parameters ( address id, variable1 and variable2) from the query string portion of our url. These were set in Index.cshtml on line 30. On lines 43-48 we gather all of the information into a LobPostcardRequest instance. On line 48, we set the merge variables in order to insert dynamic content into our postcards. We use an anonymous object to pass this info along to the Lob API.

The only task remaining is create `Postcard.cshtml` where we add the following code:

Postcard.cshtml

@page
@model PostcardModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<p>Because we have saved the address in your Lob Address Book, you can access it any time by going <a href="https://dashboard.lob.com/#/addresses/@Model.activeAddress">here</a></p>
<p>You can change the merge variables in the form below. <a href="https://www.lob.com/guides#html_and_merge">Merge Variables</a> are the way that Lob allows dynamic content in mail templates.</p>

<div class="row">
   <div class="col">
       <form class="form-inline" method="get">
           <div class="input-group">
               <input type="text" class="form-control" placeholder="variable1" asp-for="variable1" />
               <input type="text" class="form-control" placeholder="variable2" asp-for="variable2" />
               <input type="hidden" name="address" value="@Model.activeAddress" />
               <button type="submit" class="btn btn-primary mb-2">Submit</button>
           </div>
       </form>
   </div>
</div>

<br /><br />

<div class="row">
   <table class="table">
       <tr>
           <td style="width:50%"><strong>Front</strong></td>
           <td><img src="@Model.previewURLFront" style="border:1px solid black" width="50%" /></td>
       </tr>
       <tr>
           <td style="width:50%"><strong>Back</strong></td>
           <td><img src="@Model.previewURLBack" style="border:1px solid black" width="50%" /></td>
       </tr>
   </table>
</div>

This form allows you to set the query variables on this page and trigger a new postcard preview.

Conclusion

You just built a complete C# application using Lob’s API to save addresses and generate postcards from your addresses. This is only a small subset of the functionality available from Lob; you can send checks, letters, self-mailers, and verify addresses using Lob.

If you have any questions, feel free to comment on this article or contact us on Github.

Continue Reading