Visualizing ASP.NET Core endpoints using GraphvizOnline and the DOT language
Tags: aspnetcore, routing
Published: 2020-07-19 16:06:47

---
title: Visualizing ASP.NET Core endpoints using GraphvizOnline and the DOT language
published: true
date: 2020-06-30 10:00:00 UTC
tags: ASPNETCore,Routing
canonical_url: https://andrewlock.net/visualizing-asp-net-core-endpoints-using-graphvizonline-and-the-dot-language/
---

 ![Visualizing ASP.NET Core endpoints using GraphvizOnline and the DOT language](https://andrewlock.net/content/images/2020/graph_banner.png)

In this post I show how the endpoint routes in an ASP.NET Core 3.0 application can be visualized using the [GraphvizOnline](http://dreampuf.github.io/GraphvizOnline) service. That lets you create diagrams like the following, which describe all the endpoints in your application:

![An example endpoint routing graph](https://andrewlock.net/content/images/2020/graph_06.png)

## Drawing graphs with GraphvizOnline and the DOT language

[GraphvizOnline](https://dreampuf.github.io/GraphvizOnline) is [a GitHub project](https://github.com/dreampuf/GraphvizOnline) that provides an online vizualizer for viewing graphs specified in the [DOT graph description language](https://en.wikipedia.org/wiki/DOT_(graph_description_language)). This is a simple language that lets you define various types of graph, which connects _nodes_ with _edges_.

For example, a basic _undirected graph_ could be defined as

```javascript
graph MyGraph {
  a -- b -- c;
  b -- d;
}
```

which describes the following graph:

![A simple undirected graph](https://andrewlock.net/content/images/2020/graph_02.png)

Each node has a name (`a`, `b`, `c`, `d`), and `--` defines the edges between the nodes. The edges define connections between nodes, but they don't have a direction (hence the name, _undirected_).

You can also define a _directed_ graph, in which the edges _do_ have a direction. For a directed edge use `->` instead of `--`. For example:

```javascript
digraph MyGraph {
  a -> b -> c;
  d -> b;
}
```

which describes the following graph:

![A simple directed graph](https://andrewlock.net/content/images/2020/graph_03.png)

You can customise the way the nodes and edges are displayed in multiple ways. For example, you can label the nodes and edges:

```javascript
digraph MySimpleGraph {
   // The label attribute can be used to change the label of a node...
   a [label="Foo"];
   b [label="Bar"];
   // ... or an edge
   a -> b [label="Baz"];
}
```

![A labelled graph](https://andrewlock.net/content/images/2020/graph_04.png)

There's [way more](http://www.graphviz.org/doc/info/attrs.html) you can do with the [DOT graph description language](https://en.wikipedia.org/wiki/DOT_(graph_description_language)), but that's all we need for now. So how does this apply to ASP.NET Core?

## Visualizing ASP.NET Core endpoints as a directed graph

The endpoint routing system in ASP.NET Core effectively works by creating a directed graph of the endpoint URL segments. Incoming requests are then matched, a segment at a time, to the graph to determine the endpoint to execute.

For example, the following simple directed graph represents the endpoints in the default ASP.NET Core 3.0 Razor Pages application template (`dotnet new webapp`), which contains three Razor Pages: _Index.cshtml_, _Error.cshtml_ and _Privacy.cshtml_:

```javascript
digraph DFA {
  1 [label="/Error/"]
  2 [label="/Index/"]
  3 [label="/Privacy/"]
  4 -> 1 [label="/Error"]
  4 -> 2 [label="/Index"]
  4 -> 3 [label="/Privacy"]
  4 [label="/"]
}
```

which describes:

![A basic Razor Pages application](https://andrewlock.net/content/images/2020/graph_05.png).

> In the DOT file above, the nodes are given sequential integer names, `1`, `2`, `3` etc and are labelled using the endpoint name. This is the format ASP.NET Core uses for representing the endpoint graph.

For Razor Pages, the routing is very simple, so the graph is pretty obvious. A more interesting graph is produced by an API application. For example, the `ValuesController` implementation shown below is similar to the version included in the ASP.NET Core 2.0 templates. It uses multiple HTTP verbs, as well as a slightly more complex URL structure:

```csharp
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get() => new string[] { "value1", "value2" };

    // GET api/values/5
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id) => "value";

    // POST api/values
    [HttpPost]
    public void Post([FromBody] string value) { }

    // PUT api/values/5
    [HttpPut("{id}")]
    public void Put(int id, [FromBody] string value) { }

    // DELETE api/values/5
    [HttpDelete("{id}")]
    public void Delete(int id) { }
}
```

For good measure, I also added a basic health check endpoint to `UseEndpoints()`:

```csharp
app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/healthz");
    endpoints.MapControllers();
});
```

This app produces the following graph:

```javascript
digraph DFA {
  1 [label="/healthz/"] 
  2 [label="/api/Values/{...}/ HTTP: GET"] 
  3 [label="/api/Values/{...}/ HTTP: PUT"] 
  4 [label="/api/Values/{...}/ HTTP: DELETE"] 
  5 [label="/api/Values/{...}/ HTTP: *"] 
  6 -> 2 [label="HTTP: GET"]
  6 -> 3 [label="HTTP: PUT"]
  6 -> 4 [label="HTTP: DELETE"]
  6 -> 5 [label="HTTP: *"]
  6 [label="/api/Values/{...}/"] 
  7 [label="/api/Values/ HTTP: GET"] 
  8 [label="/api/Values/ HTTP: POST"] 
  9 [label="/api/Values/ HTTP: *"] 
  10 -> 6 [label="/*"]
  10 -> 7 [label="HTTP: GET"]
  10 -> 8 [label="HTTP: POST"]
  10 -> 9 [label="HTTP: *"]
  10 [label="/api/Values/"] 
  11 -> 10 [label="/Values"]
  11 [label="/api/"] 
  12 -> 1 [label="/healthz"]
  12 -> 11 [label="/api"]
  12 [label="/"] 
}
```

Which is visualised as:

![A ValuesController endpoint routing application](https://andrewlock.net/content/images/2020/graph_06.png)

There's a lot more going on in this graph, as we now have variable route parameter values (`{id}` in the route templates, shown as `{...}` in the graph) and HTTP verb constraints (`GET`/`PUT`/`POST` etc).

When I first saw this graph, I struggled to understand it. Is every node an endpoint? Surely not, as `/api/` shouldn't generate a response. And what about the `HTTP: *` endpoints, do they generate a response?

> To understand further, I went spelunking into [the ASP.NET Core code that can generate these graphs](https://github.com/dotnet/aspnetcore/blob/master/src/Http/Routing/src/Matching/DfaMatcherBuilder.cs), but it's a bit involved, and unfortunately not overly amenable to experimentation, due to extensive use of `internal` classes. I explore this code in a later post.

To better grasp the endpoint graph, we need to understand that not all the nodes are the same. In the next section we'll dig into the different types of node in even this simple graph, and then look at a better graphical representation (in my opinion at least!)

## Understanding the different types of node.

Each node in the graph is associated with a given "depth". This is the number of URL segments that should already be matched. For example, the `/api/Values/` node has a depth of 2—it requires the empty segment `/` and the `/api` segment to already be matched.

When a request reaches the `EndpointRoutingMiddleware` (added by `UseRouting()`), the incoming request URL is compared against this graph. A path is attempted to be found through the graph, starting with the root node at the top of the tree. URL segments are incrementally matched to edges in the graph, and a path is traversed through the graph until the whole request URL is matched.

Each node (represented by [the `DfaNode` in ASP.NET Core](https://github.com/dotnet/aspnetcore/blob/cc3d47f5501cdfae3e5b5be509ef2c0fb8cca069/src/Http/Routing/src/Matching/DfaNode.cs) internally) has several properties . The properties we're interested in for now are:

- `Matches`: This is the `Endpoint`(s) associated with the node. If this node is matched via routing, then this is the `Endpoint` that will be selected for execution. 
- `Literals`: These are the edges that connect nodes. If a `DfaNode` has any `Literals`, it has literal segments that can be further traversed to reached other nodes. For example, the `/api/` node contains a single `Literal` with value `/Values`, pointing to the `/api/Values` node.
- `PolicyEdges`: These are edges that match based on a constraint other than the URL. For example, the verb-based edges in the graph, such as `HTTP: GET`, are policy edges, that point to a different `DfaNode`.
- `Parameters`: If the node has an edge that supports route parameters (e.g. `{id}`), `Parameters` points to the node that handles matching the parameters. This is represented in the graph by the edge `/*`.

> There is an additional property, `CatchAll`, which is relevant in some graphs, but I'll ignore it for now, as it's not required for our API graph.

Based on these properties, we can add some additional richness to our graph, by making use of some of [the other features in the DOT language](http://www.graphviz.org/doc/info/attrs.html), such as shapes, colours, line type and arrowheads:

![A ValuesController endpoint routing application with different styling](https://andrewlock.net/content/images/2020/graph_07.png)

This graph makes the following additions:

- Nodes which don't have any associated `Endpoint` are shown in the default style, black bubbles.
- Nodes with `Matches` are shown as filled, brown, boxes. These are nodes with an `Endpoint`, that can generate a response. For the API example above, this applies to the nodes where a verb has been selected, as well as the health check endpoint. 
- Literal segment edges are shown as default black edges, with a filled arrowhead.
- `Parameters` edges (`/*`) are shown in blue, using a diamond arrowhead.
- `PolicyEdges` are shown in red, with a dashed line, and an empty triangle arrowhead.

Now, I freely admit my design skills suck, but nevertheless I think you can agree that this graph shows way more information than the default! 🙂 This is the definition that generated the graph above, remember you can visualise and play with the display yourself using [an online editor](https://dreampuf.github.io/GraphvizOnline/).

```javascript
digraph DFA {
  1 [label="/healthz/" shape=box style=filled color="brown" fontcolor="white"]
  2 [label="/api/Values/{...}/ HTTP: GET" shape=box style=filled color="brown" fontcolor="white"]
  3 [label="/api/Values/{...}/ HTTP: PUT" shape=box style=filled color="brown" fontcolor="white"]
  4 [label="/api/Values/{...}/ HTTP: DELETE" shape=box style=filled color="brown" fontcolor="white"]
  5 [label="/api/Values/{...}/ HTTP: *" shape=box style=filled color="brown" fontcolor="white"]
  6 -> 2 [label="HTTP: GET" color="red" style=dashed arrowhead=open]
  6 -> 3 [label="HTTP: PUT" color="red" style=dashed arrowhead=open]
  6 -> 4 [label="HTTP: DELETE" color="red" style=dashed arrowhead=open]
  6 -> 5 [label="HTTP: *" color="red" style=dashed arrowhead=open]
  6 [label="/api/Values/{...}/"]
  7 [label="/api/Values/ HTTP: GET" shape=box style=filled color="brown" fontcolor="white"]
  8 [label="/api/Values/ HTTP: POST" shape=box style=filled color="brown" fontcolor="white"]
  9 [label="/api/Values/ HTTP: *" shape=box style=filled color="brown" fontcolor="white"]
  10 -> 6 [label="/*" arrowhead=diamond color="blue"]
  10 -> 7 [label="HTTP: GET" color="red" style=dashed arrowhead=open]
  10 -> 8 [label="HTTP: POST" color="red" style=dashed arrowhead=open]
  10 -> 9 [label="HTTP: *" color="red" style=dashed arrowhead=open]
  10 [label="/api/Values/"]
  11 -> 10 [label="/Values"]
  11 [label="/api/"]
  12 -> 1 [label="/healthz"]
  12 -> 11 [label="/api"]
  12 [label="/"]
}
```

> Note the `"HTTP: *"` nodes are associated with an endpoint, even though you might not expect it, because they return a `405 Method Not Allowed`.

In the next post, I'll show how you can generate endpoint graphs for your own ASP.NET Core applications.

## Summary

In this post I provided an introduction to the DOT language for describing graphs, and showed how you can use [online editors](https://dreampuf.github.io/GraphvizOnline) to create images from the graphs. I then showed how routing for endpoints in an ASP.NET Core 3.x application can be represented as a directed graph. I described the differences between various nodes and edges in the endpoint graph, and tweaked the graph's display to better represent these differences. In later posts I'll show how you can generate your own endpoint graphs for your application, how to customise the display, and how to do more than just view the graphs.