Applying Lipstick to Frankenstein's Monster
Note: I have written an alternative version of this article and utility here for those who don't want to mix args with their path info in the server routing attributes.
The client utility that I cobbled together for testing my Web API REST calls was getting out-of-hand/unwieldy or, to put it
bluntly, 9X Uglier than a Bag of Butts (or a monster mashup, like Mary Shelley's Adam), as
you can see here:
So I decided to clean up my act by creating a more generic Windows Forms utility. My aim was to avoid having to add a set of
buttons each time I wanted to test a new Controller. All I need to do now is to add the Controller method "decorations" (or
routing attributes) to a combobox or a text file. Then, when I run the util, I either load the text file or select the URI I want to
test from the combobox. The util determines, based on the URI selected, which args needed to be added, and makes (up to
four) textboxes visible in which I can enter the argument values to pass. The util then builds the URI to be sent to the Web API
server.
I'm adding the code to this util to this article, but I'm not going to feature it here because I realize that some of it is kind of
kludgy and many of you could elegantize it and make it more "performant" - IOW, the code used is not necessarily the best way
of accomplishing the ends in all cases - but it works pretty well for me, so that's why I'm sharing it; I'll also add the .exe as a
download. The point of this article is to step you through the usage of it.
Speaking of inelegant code, after increasing the count of args from 4 to 10, I replaced the brute force/verbose VisiblizeCandidateArgs()
code with the following:
private void VisiblizeCandidateArgs(bool visiblize)
{
const int CANDIDATE_COUNT = 10;
for (int i = 1; i <= CANDIDATE_COUNT; i++)
{
Control lbl = GetControlByName(string.Format("labelArg{0}", i));
lbl.Visible = visiblize;
}
for (int i = 1; i <= CANDIDATE_COUNT; i++)
{
Control txtbx = GetControlByName(string.Format("textBoxArg{0}", i));
txtbx.Visible = visiblize;
if (!visiblize)
{
txtbx.Text = string.Empty;
}
}
}
The Seven Step Method to This Madness
At this point in time and space, I will lead you through the use of the util. And actually, I have placed numbers on the form's
controls to indicate the steps in which you should plod (or race) through this util.
First, run it, and you will see something highly provocative (YMMV):
Shocking!!! You see here that the base URI for my app is in step 0 (Enter Base URI)! What an egotist! Well, really
not so, because it's just there (for you) as a "living" example ("It's Alive!") -- you can (and should) change the URI to whatever is
right for the REST service you are going to est.
Mash the "1) Load Routing Attributes From File" button to do so (first, though, you must have such a file or, if you've
downloaded the code, you can add the URIs you want to the combobox's Items
property).
A conditionally sufficient word (or two): For background information on decorating your Controller methods with
explicit attribute routing, see this article.
A text file you load might have contents like this:
- [Route("api/SQLServerPOC/Count")]
- [Route("api/SQLServerPOC/GetAll")]
- [Route("api/SQLServerPOC/{ID:int}")]
- [Route("api/SQLServerPOC/GetByFullName/{FName}/{MName}/{LName}")]
- [Route("api/SQLServerPOC/GetByLastName/{LName}")]
- [Route("api/SQLServerPOC/GetByStateOrProvince/{StateOrProvince}")]
- [Route("api/SQLServerPOC/GetByZip/{PostalCode}")]
- [Route("api/SQLServerPOC/GetByCountry/{Country}")]
Now that you've populated the combo box, you can go to step 2 - select one of the items / URIs.
The "3) Discover Attribute Args" button examines the URI and determines any "differentiator" contained in the URI (such as
"GetAll
" or "GetByLName
" or whatever) and also if there are any args and if so, how many. If there are some, it makes the
appropriate number of label/textbox pairs visible (up to four) so that you can enter an appropriate value to pass as the argument
in the text box (the label indicates the name of the arg and its data type). Here's what you might see after selecting a URI with
three args and entering some values into the text boxes:
If you then hit the "5) (Re)build URI" button, it will do so, appending to the Base URI the Controller Name, the
"Differentiator" if it has one (GetByFullName
in this case), and any arg vals. Don't believe me? Czech it out:(
The only thing left to do is mash the "6) Test the URI" button. But since I want to show more data, I'm going to change the
URI to select by State and then do so. The results are:
Known Issue: The GetRESTData()
method (which is called when you mash the "Test the URI" button)
blows up if only one json "record" is returned. I guess because it doesn't consider one measly element an array, and it returns a
JArray
. So that is an exercise left to the reader: figure out a fix for that.
I did spin a clever but kludgy way to get around the crashing of GetRESTData()
when only one json array element is found. I
changed this code:
private JArray GetRESTData(string uri)
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
return JsonConvert.DeserializeObject<jarray>(s);
}
...to this:
private JArray GetRESTData(string uri)
{
try
{
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
var webResponse = (HttpWebResponse)webRequest.GetResponse();
var reader = new StreamReader(webResponse.GetResponseStream());
string s = reader.ReadToEnd();
return JsonConvert.DeserializeObject<jarray>(s);
}
catch {
try
{
MessageBox.Show(GetScalarVal(uri));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
return null;
}
private string GetScalarVal(string uri)
{
var client = new WebClient();
return client.DownloadString(uri);
}
UPDATE: I've added another file that can be downloaded; the latest version of my test util (as of 2/7/2014) as a project template. It is the file named "Web_API_GET_REST_Test.zip". Feel free to adapt, adopt, tweak, but do not fold, spindle, mutilate, or otherwise catawamptiously chaw it up.
If you like this article/utility, be kind and rewind ... all the cassette tapes in the known universe - and before teatime, at that!
Alternative service: Tiptoe through the Tulips.