In the last post I tried to show how to use HTTP GET to get data back from my Services. Now I want to put my thoughts in order and show how I created new Employees and updated them using programmatic POST using the ubiquitous Northwind database.
My first attempts where at trying to update a record using values embedded in the querystring... easier said than done, it was quite an effort.. At then end it worked, it just required a shift in the way I was tackling the problem. I ended up having my contract accept a Stream parameter and then had to read the stream and get the values back.
My contract was:
//STREAM EXAMPLE [OperationContract] [WebInvoke(UriTemplate = "*", Method = "POST")] void UpdateEmployeeName(Stream id);
My uri template was "*" since I wanted to have the querystring collection of values straight after the 'root' of my Service.svc
My implementation was very simple...
public void UpdateEmployeeName(Stream id) { StreamReader reader = new StreamReader(id); String res = reader.ReadToEnd(); NameValueCollection coll = HttpUtility.ParseQueryString(res); int employeeID = Int32.Parse(coll["elid"]); String newName = coll["newName"];
// Bless LinqToSql NorthwindDataContext cts = new NorthwindDataContext(ConnectionManager.GetHomeLocalConnectionString); Employee e = cts.Employees.Where(a => a.EmployeeID == employeeID ).Single(); e.FirstName = newName; cts.SubmitChanges(); }
Now, how the heck do I make a programmatic POST to my contract?
I just created a WebRequest and set the content type to form-urlencoded and set the request length to the length of my queryString collection.
//set the data ASCIIEncoding enc = new ASCIIEncoding(); string datatext = "elid=" + elEmployeeID.Text; datatext += "&newName=" + elEmployeeNewName.Text; byte[] data = enc.GetBytes(datatext); //HTTP POST query WebRequest request = HttpWebRequest.Create("http://localhost/DemoWCF/Service.svc"); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = data.Length; Stream datasteam = request.GetRequestStream(); datasteam.Write(data, 0, data.Length); datasteam.Close(); WebResponse response = request.GetResponse();
To test it I threw a few textfields together and magic....
And checking the db....
I also wanted to create a new Employee using Javascript using a programmatic post as above but this time using Javascript Object Notation (JSON ) http://www.json.org/
My contract..
[OperationContract] [WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "/CreateEmployee")] void CreateEmployee(string FirstName, string LastName);
Implementation:
public void CreateEmployee(string FirstName, string LastName) { NorthwindDataContext cts = new NorthwindDataContext(); Employee e = new Employee(); e.LastName = LastName; e.FirstName = FirstName; cts.Employees.InsertOnSubmit(e); cts.SubmitChanges(); }
To generate an HTTP POST call with JavaScript, I created a XMLHTTPRequest and then I just constructed the url, set the header content type to json and was careful to format my JSON pair's payload..
<asp:Button ID="btnJSONCreateEmployee" runat="server" OnClientClick="doEmployeeCreate()"
function doEmployeeCreate() { var newFirstName = document.getElementById('TextBox1').value; var newLastName = document.getElementById('TextBox2').value; var xmlHttp = new XMLHttpRequest(); // Create result handler xmlHttp.onreadystatechange=function() { if(xmlHttp.readyState == 4) { document.getElementById("result").value = xmlHttp.responseText; } } var url = "Service.svc/CreateEmployee"; var body = '{"FirstName": '+ '"' + newFirstName + '"'; body += ', "LastName": ' + '"'+newLastName + '"' + '}'; //Send the Http Request xmlHttp.open("POST", url, true); xmlHttp.setRequestHeader("Content-type", "application/json"); xmlHttp.send(body); }
To create a JSON payload in C# is very simple, just format a string carefully, escaping the " and you'll be fine..
protected void btnCallService_Clicked(object sender, EventArgs args) { ASCIIEncoding enc = new ASCIIEncoding(); string employeeID = txtJsonEmpID.Text; string firstName = txtJsonFirstName.Text; string lastName = txtJsonLastName.Text; string datatext = "{\"EmployeeID\":{0}, \"FirstName\":\"{1}\" , \"LastName\":\"{2}\" }"; datatext = datatext.Replace("{0}", employeeID) .Replace("{1}",firstName) .Replace("{2}", lastName); byte[] data = enc.GetBytes(datatext); WebRequest request = HttpWebRequest.Create("http://localhost/DemoWCF/Service.svc/CallJason"); request.Method = "POST"; request.ContentType = "application/json"; request.ContentLength = data.Length;
Stream datasteam = request.GetRequestStream(); datasteam.Write(data, 0, data.Length); datasteam.Close(); WebResponse response = request.GetResponse(); }
Ok, now unfortunately one of the new Employees didn't leave good comments in SVN and el# got a bit upset when he had a look at the logs. So let's fire the guy, tough choice yes, but then also we need to demo a programmatic HTTP DELETE.
Simple contract...I just specify the request format, the response format and the method that my client will be using..
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "/FireEmployee", Method="DELETE" )] void FireEmployee(int EmployeeID);
Implementation is dead simple...this has got nothing to do with WCF, but I just post it here for completeness.
public void FireEmployee(int EmployeeID) { NorthwindDataContext ctx = new NorthwindDataContext(); Employee emp = ctx.Employees.Where(e => e.EmployeeID == EmployeeID).Single(); ctx.Employees.DeleteOnSubmit(emp); ctx.SubmitChanges(); }
Calling using JavaScript is v. simple too..
function fireTheGuy() { var employeeID = document.getElementById('txtToDeleteEmployeeID').value; var xmlHttp = new XMLHttpRequest(); // Create result handler xmlHttp.onreadystatechange=function() { if(xmlHttp.readyState == 4) { document.getElementById("result").value = xmlHttp.responseText; } } // void CreateEmployee(string FirstName, string LastName); var url = "Service.svc/FireEmployee"; var body = ' { "EmployeeID": ' + employeeID + ' }'; //Send the Http Request xmlHttp.open("DELETE", url, true); xmlHttp.setRequestHeader("Content-type", "application/json"); xmlHttp.send(body); }
And trust me, it works like a charm.
Next post I would like to return complex types back to the client, as in Employee's objects etc..
1 comment:
Thank you, thank you! I've been searching for hours for an example like this!
Post a Comment