POSTing to a REST API with c#
Prerequisites
I do prefer to take things all the way from start to finish, step by step, but sometimes, (in this instance!), it would mean repeating a lot of stuff that I’ve already covered… So if you’ve not already done so, I’d advise reading the following posts, (in order), to get yourself to this stage:
And although not strictly necessary, if may be worthwhile getting up to speed with JSON as we’ll be using a JSON data structure in this tutorial as part of the POST request:
These posts also have links to the accompanying YouTube videos if you prefer that medium, OK so now on with the tutorial!
Video
If you prefer a more visual medium we have an associated video tutorial here:
User Story
As a developer
I want to make a POST request to a REST API
So that I can add a new data object to our repository
Acceptance Criteria
On completing this tutorial you should:
- Be able to use c# to make a POST request to a REST API
- Cause a data object to be added to our application repository
Ingredients
I used the following ingredients to write this post, if you don’t have the exact same ingredients then feel free to substitute it for an equivalent item:
- Windows PC (I’m using Windows 10 on a self-built desktop)
- Visual Studio Community Edition 2017 (This is free and available from Microsoft)
- Web Browser (I primary use Firefox with the “Live HTTP Headers” add-on)
- Notepad++ (I know it sounds a bit weird using this as well as Visual Studio, but I do like to have a separate notepad running too)
- A REST API that can accept POST requests, in this instance I’m running:
- VMWare Workstation with a Centos 7 Guest OS
- Jira Software – Basically a project management tool used a lot by software development teams, more HERE.
Just looking through my list with the exception of the PC Hardware and Windows OS, everything else here was free, (I paid $10 for a Jira License), which is pretty amazing when you think about it!
If you’d rather not set up a local version of JIRA on a VM etc, you can sign-up for a Cloud Account on the Atlassian web-site.
Or there are other “test REST API” web sites out there that you can dev / test against. I’ve not used any of these so can’t recommend one, if you can though then please leave a comment!
My Lab Set Up
As described in the ingredients section, here’s how my lab is set up:
User Interface Design
We are following directly on from the Authentication to a REST API tutorial, but I’ve made some cosmetic changes to the UI, the primary one’s are:
- Remove the various options that allow you to select the type of authentication (in this tutorial we’re just going to use BASIC authentication)
- Added a ComboBox to allow us to select the HTTP Verb, (more about that below)
- Added a Text Box for the “POST Data” we’ll send with our request
The wireframe with some, (helpful?), mark up is shown below:
Class Diagram
There’s nothing really new apart from the new “postJSON” attribute!
HTTP Verbs
There’s already a wealth of stuff written about HTTP Verbs, (or Request Methods if you prefer), most notably the specification RFC7231, so I’m really not going to add much to that…
I have for completeness though, (and to save you a mouse click!), listed the main methods you’re likely to encounter and use below, (I’m guessing if you’re here you probably have an understanding of this already):
VERB | CRUD* | Description |
---|---|---|
GET | Read | Used to read or return a resource, (JSON, XML etc.). GET requests don’t change data so are considered “safe” |
POST | Create | Used to create a resource at the target. We’ll be using a POST request to send a JSON payload to Jira in the hope we can create a “ticket” in the system, (they are usually referred to as “issues” in Jira). |
PUT | Update / Replace | PUT updates a resource, but replacing the old one in its entirety, it requires more network bandwidth than PATCH, (see below) |
PATCH | Update / Modify | PATCH updates only parts of a required resource, e.g. just update the firstname value of a user object. Requires less bandwidth than PUT |
DELETE | Delete | Self explanatory, used to delete a resource – use with caution! |
* Acronym denoting the common operations of:
Create, Read, Update & Delete
Query Strings
OK so I just want to clarify the anatomy of a URL and the Query String component in particular as I feel this is useful when we come on to talk about the differences between GETing and POSTing data…
The Wikipedia entry HERE is actually great and goes into lots of detail, but I just wanted to reinforce the 2 very broad components of a typical URL, as an example take the following made up URL:
https://blahblah.com/rest/api/2/issue/VP-1?expand=true&maxResults=1000
Basically the “query string” is everything from the question mark, (?), onward. It’s a non-hierarchical string that typically consists of attribute-value pairs separated, (usually), by an ampersand: &. Therefore in the above example, we have 2 sets of attribute-value pairs:
ATTRIBUTE | VALUE |
---|---|
expand | true |
maxResults | 1000 |
Note: The “attribute” and the “value” are separated, (again usually), but an equals sign: =.
So why am I saying usually a lot?
Well while the anatomy of the overall URL is well defined in RFC-1738 by Sir Tim Berners-Lee, the nitty-gritty, (not a technical term), of the query string is “non-standardised”. Meaning that there could be different interpretations of how it could be implemented, (different to the example given above).
Anyway this is all rather academic, the main point(s) I’m trying to make are:
- Most query strings adhere to the example format given above
- They, (query strings), are used to pass “data” to the server
- GET Requests are a great example of where they are used:
- The above example is GETing issue “VP-1” and the attribute value pairs passed as part of that request are telling the Jira server how to additionally process the request
- They really have NOTHING to do with POSTing data!
That’s right I’ve just spent this time telling you about query strings, but finish with the point that they are not really that relevant to POST requests, (POST requests can still have query strings though, but the actual data we are POSTing is not placed here..)
Confused? Read on!
POST Data
Before we go on, here’s the man himself, (Sir Tim), pretending to do some work, (much like I do Monday-Friday, don’t tell my employer…)
OK, moving on…
So I hear you ask, if we’re going to POST data to a server and ask it to create a new resource, why couldn’t we use query strings as the method to do that?
It’s a good question, but the main reasons we would not use the query string as a method to pass data are:
- Physical limitations of URL total length
- Security (URL + Query String are stored in your browsers history)
- The structure of the data we would want to pass
Let’s take each one in turn:
Physical Limitations
There is no theoretical limit to the query string, but there are physical limits as imposed by your choice of web server and web browser, (Google this if you’re interested in actual values), so if we’re wanting to pass “a lot” of data these limitations would scupper our attempts. You may even get a 414 Request-URI Too Long HTTP Status Code! Eurgh!
Security
Have you ever been planning a holiday with your girlfriend and been typing stuff into a web browser when it drops-down and shows previous URL destinations, including embarrassing search terms contained in the query string?
No neither have I, (cough), but you get the point. While query strings may be encrypted over the wire if you’re using HTTPS etc, that query string can still be cached and retrieved from your browser history. Not advisable if you need to POST “sensitive” data…
Data Structure
So aside from size limits and “security concerns”, the last reason you may not want to use a query string is the potentially, complex, hierarchical structure of your data.
As shown in the query string example, the data is structured in a non-hierarchical way, so it doesn’t really lend it’s self to more complex data structures with nested attributes etc. It’s not to say that it would be impossible to construct some kind of schema, but just because you could do it, doesn’t mean to say it should be done. To paraphrase Chris Rock:
“You could drive a car with your knees, but it doesn’t make it a good f@#$ing idea!”
Where does our POST data go?
An even better question, in short our POST data is actually sent in the body of our request message. I think this is where we need to start to code…
Now We Code!
IMPORTANT: Remember, we’re starting from a “base” project here, (you need to follow Authenticating to a REST API to get to the starting point here).
Before we launch into coding proper let’s just set up our UI, by following the design of our wire frame:
So with the exception of the red dots, (used so you can reference the item properties below), our web form is pretty simple, and does indeed follow our basic design.
If you’re wanting to follow along with the code exactly then refer to the UI element property settings below. Once done, were pretty much finished with the UI.
Simple Button Logic
We’ve added a couple of buttons to help when demoing the solution, (just saves a few mouse clicks but they add up!):
- Copy
- Clear
Double-click both buttons on the designer to wire up the event handlers for both, assuming you’ve named everything as I have above then code for each should look as follows:
private void cmdClear_Click(object sender, EventArgs e) { txtResponse.Text = string.Empty; } private void cmdCopy_Click(object sender, EventArgs e) { System.Windows.Forms.Clipboard.SetText(txtResponse.Text); }
Nothing terribly magical there:
- For the “Clear” button we just set the contents of out Response Text Box to “empty”
- For the “Copy” button we “Set the Text” of the clipboard to the contents of the Response Text Box (makes it easier to paste any responses into our JSON editor)
What You’ve Been Waiting For!
So now we turn our attention to our RESTClient Class, the full definition is below, you’ll notice I’ve taken out some redundant code from the previous tutorial, (essentially the stuff that decides with authentication mechanism to use). I’ve coloured any new code in BLUE, (apologies to those of you that are colour blind…)
using System; using System.IO; using System.Net; namespace VP_10_Prototype { public enum httpVerb { GET, POST, PUT, DELETE } public enum authenticationType { Basic, NTLM } class RestClient { public string endPoint { get; set; } public httpVerb httpMethod { get; set; } public authenticationType authType { get; set; } public string userName { get; set; } public string userPassword { get; set; } public string postJSON { get; set; } //New Attribute public RestClient() { endPoint = string.Empty; } public string makeRequest() { string strResponseValue = string.Empty; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(endPoint); request.Method = httpMethod.ToString(); String authHeaer = System.Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(userName + ":" + userPassword)); request.Headers.Add("Authorization", "Basic " + authHeaer); //********* NEW CODE TO SUPPORT POSTING ********* if (request.Method == "POST" && postJSON != string.Empty) { request.ContentType = "application/json"; //Really Important using (StreamWriter swJSONPayload = new StreamWriter(request.GetRequestStream())) { swJSONPayload.Write(postJSON); swJSONPayload.Close(); } } HttpWebResponse response = null; try { response = (HttpWebResponse)request.GetResponse(); //Proecess the resppnse stream... (could be JSON, XML or HTML etc..._ using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { using (StreamReader reader = new StreamReader(responseStream)) { strResponseValue = reader.ReadToEnd(); } } } } catch (Exception ex) { strResponseValue = "{\"errorMessages\":[\"" + ex.Message.ToString() + "\"],\"errors\":{}}"; } finally { if (response != null) { ((IDisposable)response).Dispose(); } } }//End of makeRequest }//End of Class }
In reality the new code if very straight forward:
if (request.Method == "POST" && postJSON != string.Empty) { request.ContentType = "application/json"; //Really Important using (StreamWriter swJSONPayload = new StreamWriter(request.GetRequestStream())) { swJSONPayload.Write(postJSON); swJSONPayload.Close(); } }
We:
- Check to see that our HTTP Request Method is set to POST and that we have a non-empty string for our “payload” – you’ll note there is no real error checking here…
- We set the request “ContentType” to JSON. As I’ve noted in the comment this is important! If you don’t provide this specifier for your format, (JSON, XML etc.), you may get a 415 “unsupported format” error – (I did when I didn’t put this in)
- We then call the “GetRequestStream” method on our HttpWebRequest object, here’s what the MSDN article HERE has to say about it:
This is basically the whole crux of the matter, we open a stream so that we can “attach” & send our JSON data with our request object, (as discussed briefly above).
We then:
- Write our JSON string to the stream
- We close our stream
- Because we create our SteamWriter object in a “Using” statement, the StreamWriter object should be cleanly Disposed (and hence closed). I just put it in because it makes me feel better
- The rest of the code you should be familiar with, but we basically call GetResponse on our request object to actually make the request
Making The Call
Ok all that’s left to do is to make the call, this is what the code of the GO button’s click event looks like:
private void cmdGO_Click(object sender, EventArgs e) { RestClient rClient = new RestClient(); rClient.endPoint = txtRequestURI.Text; switch(cboVerb.Text) { case "POST": rClient.httpMethod = httpVerb.POST; rClient.postJSON = txtPOSTData.Text; break; default: rClient.httpMethod = httpVerb.GET; break; } rClient.userName = txtUserName.Text; rClient.userPassword = txtPassword.Text; string strResponse = string.Empty; strResponse = rClient.makeRequest(); debugOutput(strResponse); }
Again, the only new code is in BLUE, it just:
- Checks to see what we’ve set the verb to in our Combo Box
- If it’s POST, then:
- We set the RestClient objects httpMethod attribute
- We set the RestClient objects postJSON attribute to the contents of our txtPOSTData textbox
- Else we just default the httpMethod to GET
- The rest of the code is as before, we make a makeRequest call
Testing
Ok so this last bit will come down to you! You’ll have to understand the format of both:
- The URL of the POST API you’re using
- The Data payload you want to send with it
You will have to consult with the API documentation for your particular API, as I’m using Jira and want to create an “issue”, I’ll use the following:
API URL: http://localhost:8080/rest/api/2/issue/
JSON POST Data:
{ "fields": { "project": { "key": "TEST" }, "summary": "No REST for the Wicked.", "issuetype": { "id": "10002" }, "reporter": { "name": "binarythistle" } } }
I’m not going into too much detail about the JSON payload, (I had to derive this from the Atlassian documentation), but it’s fairly self explanatory. My next tutorial will actually be a deep dive on a number of the most useful JIRA API calls, in that I’ll detail how you can determine the format of your POST data payloads.
What Happens Next?
Well after clicking “GO”, (and assuming you’ve filled in everything correctly), you should get a success response. In the case of Jira, we’re returned a JSON string that gives us some useful information about the newly created issue!
Video