Building your own API Playground

I often have to explore APIs, and get a feeling for what I can do with them before either turning it into a proof of concept, or full on production code. I'm pretty sure I'm not the only one in that position.

I don't mind things like Swagger UI for doing some tests directly in the vendor’s sandbox. And it works great for a lot of things. An API for sending text messages? Type in the receiving number in one box and the message in another, and click send. No worries.

But what if you want to do something like generating a unique url to go into that text message? (Just play along with me and accept it as a use case.) That requires something more dynamic on my part. And that requires bringing up “something” that will let me execute the call outside the Swagger UI. I am familiar with Postman and Fiddler, and use them all the time, but they don’t always meet my needs either.

Another minor drawback in some situations is that these tools focus on the JSON that goes over the wire. After you have figured out what goes in, and what comes out, you probably need a conversion process for running the call in your app since you (probably) don't code in JSON. (I love JSON for the purpose it intends to fill, but that doesn't mean it solves the coding for me.)

Inevitably this leads me to being in the Create new project wizard in Visual Studio to further customize my exploration:

Visual Studio Console App

And then you have to create a simple console app, or an ugly Forms app or something, and write extra code that you don’t really need just to pull off that little API call you were interested in. You have your main solution open in one Visual Studio, and have this supporting solution running in parallel in a second instance.

I can hear someone out there laughing and saying “that’s what you get for playing around with C#, if you were just coding in {insert language of choice} things would be way better”. Well, I’m not claiming that this takes me a lot of time and effort in C#, or that I necessarily have problems in doing so. It's just not a smooth workflow. As for the reasons of using C# in the first place — well I happen to like it, and it makes sense for a lot of the things I do. But what if I could do it without using Visual Studio, in C#, and directly in the browser? Sounds like something I can like even more! No, this isn't about weaning you off Visual Studio; I wouldn't recommending trying to write a multi-tier microservice solution without a proper IDE. Since I happen to be a fan of Azure Functions I can see how this sounds like a description of that, so to be clear - no it's not a reimplementation of Azure Functions either :) (Although you can definitely do this in the Functions Web UI.)

Let's build our own little API Playground for this purpose. Actually I sort of started already with the server part of it in my last post:
https://blogs.msdn.microsoft.com/azuredev/2017/03/14/using-azure-functions-as-a-lightweight-api-gateway/

That was about using Azure Functions to create a lightweight API gateway enabling you to juggle between back-end endpoints in a seamless manner. Now, exposing APIs is clearly an important part of playing with APIs, but it's not like they're much use without a client-side on the other end of the TCP/IP conversation. Which is what I thought I would dive into in this post. I didn't go with naming the posts part one and two since they are independent pieces, but they do complement each other.

This isn't going to be a Swagger UI-killer either; I'm just aiming for something simple and developer-friendly which delivers some of the features I look for when testing my APIs :) More like have it running in a container on your dev station as a utility.

An HTTP Method tester

I'll spare you the details of how to create a web app in Visual Studio and stub out the basics of an HTML page. I stepped through the .NET Core Web App Wizard in VS 2017, and worked from that. It could be done in other setups as well, but if you try to load up my code in VS 2015 it's probably going to throw an error or two.

The first part of the playground is fairly simple - you type in the body of the HTTP request (for methods requiring a body), click a button, and the rest happens behind the scenes.

API Playground HTTP Test

This is basically the same as Fiddler/Postman, but with less options. (Alpha version here people.) This isn't all that dynamic yet, but I wanted to at least have the option present.

It's some quick and dirty HTML that enables this:

 
<span class="ms-Table-cell ms-font-xl">
    <button id="btnExecuteGET" type="submit" class="ms-Button ms-Button--compound" onclick="GET()">
        <span class="ms-Button-label">HTTP GET</span>
        <span id="GET-url" class="ms-Button-description">
           https://api01.contoso.local/api/HttpGET?name=Azure
        </span>
    </button>
</span>

It's backed by a controller where the requests actually happen:

 
[HttpPost]
public async Task<IActionResult> Http(Code code)
{
    string output = code.csx;
    using (var client = new HttpClient())
    {
        var reqInput = code.csx;
        var req = JsonConvert.DeserializeObject<HttpTestRequest>(reqInput);
        var requestUrl = req.url;
        var request_content = req.body;

        var responseString = "";

        //For HTTP GET requests (assumption that no body == GET)
        if (request_content == "")
        {
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(requestUrl));
            HttpResponseMessage response = await client.SendAsync(request);
            responseString = await response.Content.ReadAsStringAsync();
            //We feed the default body back into the view just for the sake of visibility; 
            //looks weird when it gets cleared
            code.csx = "{
              \t\"name\":\"Azure\"
            }";
        }

        //For HTTP POST requests
        if (request_content != "")
        {
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(requestUrl))
            {
                Content = new StringContent(request_content, Encoding.UTF8, "application/json")
            };
            //No support for XML bodies at the moment
            //Considering adding this header in the UI
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            HttpResponseMessage response = await client.SendAsync(request);
            responseString = await response.Content.ReadAsStringAsync();
            code.csx = req.body;
        }
        code.output = "{
          \t\"result\":" + responseString + "
        }";
    }
    return View(code);
}

If you think it's slightly bad practice to just use the button description as the url to call you are of course entirely right.

A code-based tester

This is where it gets more interesting, as this allows for much greater control, and it will let you chain together methods and calls to pull off scenarios like the one I described above. Instead of hiding the details of the HTTP calls in the controller we will expose the actual code for calling the APIs. The code isn't modified, so if you make a typo in the UI there isn't any "fixing" done behind the scenes.

I actually covered remote execution of C# code previously so I'll build upon those learnings here:
https://contos.io/a2558344c256

To give a code editor feel I'm using the Monaco editor which is the component powering Visual Studio Code, and things like the Typescript Playground: https://www.typescriptlang.org/play/index.html. You think this might have been an inspiration for me doing this? Ok, I'll admit I might have browsed to that web site a time or two before making my own playground. And of course I also like playgrounds like the Graph Explorer for exploring the Microsoft Graph. I guess this ends up as a hybrid combo of these.

API Playground C# Tester

The HTML here isn't all that jazzy either (at least the contents here doesn't go directly into the execution of the test):

 
<span class="ms-Table-cell ms-font-xl">
    <button id="btnTemplateGET" type="button" class="ms-Button ms-Button--compound" onclick="loadGet()">
        <span class="ms-Button-label">HTTP GET</span>
        <span class="ms-Button-description">https://api.contoso.local/FOO</span>
    </button>
</span> 

Some JavaScript to load the template:

 
function loadGet() {
    document.getElementById("csxEditor").innerHTML = "";
    var strGetTemplate = [
        'using System;',
        'using System.Net.Http;',
        '\npublic class HelloAPI',
        '{',
        '\tpublic static void Main()',
        '\t{',
        '\t\tusing (var client = new HttpClient())',
        '\t\t{',
        '\t\t\tstring requestUrl = $\"https://api01.contoso.local/api/HttpGET?name=Azure\";',
        '\t\t\tHttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);',
        '\t\t\tHttpResponseMessage response = client.SendAsync(request).Result;',
        '\t\t\tvar responseString = response.Content.ReadAsStringAsync().Result;',
        '\t\t\tConsole.WriteLine(responseString);',
        '\t\t}\n\t}\n}',
    ];
    window.editor = monaco.editor.create(document.getElementById('csxEditor'), {
        value: strGetTemplate.join('\n'),
        language: 'csharp',
        wrappingColumn: '0',
        wrappingIndent: 'indent'
    });
}

Best template system ever, with the added bonus of counting the number of tabs? Sure thing.

And some C# in the Controller to make it all work:

 
[HttpPost]
public async Task<IActionResult> CSharp(Code code)
{
    string output = code.csx;
    var apiURL = "https://www.microsoft.com/net/api/code";
    using (var client = new HttpClient())
    {
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, new Uri(apiURL));
        request.Headers.Add("Referer", "https://www.microsoft.com/net");
        var codeSnippet = code.csx;

        var codeInput = new CodeRequest { language = "csharp", captureStats = false, sources = new string[1] };
        codeInput.sources[0] = codeSnippet;

        var request_content = JsonConvert.SerializeObject(codeInput);
        request.Content = new StringContent(request_content, Encoding.UTF8, "application/json");
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        HttpResponseMessage response = await client.SendAsync(request);
        var responseString = await response.Content.ReadAsStringAsync();
        var codeOutput = JsonConvert.DeserializeObject<CodeResponse>(responseString);
        code.output = codeOutput.Output[0];
    }
    return View(code);
}

If you look at the complete code and you're confused by the way I handle the model passing between the controller and the view it's because I need to do some extra JavaScript to convert between formatted and "clean" code. It needs to look good, and actually compile.

The templating system is a bit rough, but load up a known-good sample, modify the code to your liking, and send it through the bit pipe to Microsoft for execution. The results will come back to you, and be printed to the output window. It basically works like a console app - you need to use Console.WriteLine to see the results . I have not tested which packages you can include, or if there are things filtered out for security purposes, but the focus is not on building complete apps in this little editor area :)

I'll admit that I'm not entirely happy relying on a Microsoft endpoint that could be pulled at any time, and it would of course have been even better to be able to keep it all locally on the dev box. That's certainly on the list, and I'm investigating an approach based on Azure Functions running locally. I'm not all there yet though, but intend to see if I can hack it into something better. (Since I'm using Visual Studio 2017 support for Azure Functions tooling currently isn't out there, so there are minor things like this tripping me up in addition to my own learning while doing approach.)

Of course it's all on GitHub so you can compile & run it yourself:
https://github.com/ahelland/API-Playground

I hope you'll agree that we are starting to get in shape with regards to experimenting with APIs! While I don't know the details of future blog posts I believe this puts me in a better position to illustrate code-based concepts.