The Service Template even sticks in a Default.htm page with a couple of sample jQuery
calls - although it will require some serious modifications if you want to do
anything really cool.
NOTE: If you cannot find "jQuery-Enabled WCF Service", Install WCF support for jQuery if you do not have it: http://wcf.codeplex.com/
This demo shows how to create a POHTML (plain old HTML) client page using jQuery
and jQuery Templates to consume one of these services. We'll POST a string of
stock symbols that you've entered with a button click, to the service's Post
method, and it will go out to Yahoo finance, retrieve the quotes data (which
is not JSON, just delimited csv-style text), massage them into C# Quote instances,
convert the resulting List<Quote> collection to legal JSON, and send it
back, whereupon the page will display the stock information in an HTML table
via a JQuery Template.
The first thing to look at is the web.config file, which will contain a couple of
new elements that are important if you want to be able to use a service url like
http://localhost:8326/.
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
</system.webServer>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
The UrlRoutingModule is used to create the route, which is instantiated in Global.asax:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
RegisterRoutes();
}
private void RegisterRoutes()
{
// Edit the base address of the service by replacing the "Service" string
below
RouteTable.Routes.Add(new ServiceRoute("Service", new WebServiceHostFactory3(), typeof(WcfJQueryService)));
}
Here is the actual Service class:
using System;
using System.Collections.Generic;
using System.Json;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using Microsoft.ServiceModel.Web;
namespace WCFjQueryServiceApplication1
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class WcfJQueryService
{
private const string url1 = "http://download.finance.yahoo.com/d/quotes.csv?s=";
private const string url2 = "&f=snkc6";
[WebInvoke(UriTemplate = "", Method = "POST")]
public JsonValue Post(JsonObject body)
{
dynamic item = body["symbols"];
var symbolString = item.ToString();
var wc = new WebClient();
string stuff = wc.DownloadString(url1 + symbolString + url2);
string[] lines = stuff.Split("\r\n".ToCharArray());
var quotes = new List<Quote>();
foreach (string l in lines)
{
// let's get rid of those quotation marks
string tmp = l.Replace('"', ' ');
string[] toParse = tmp.Split(",".ToCharArray());
var q = new Quote(toParse);
quotes.Add(q);
}
JsonValue retval = JsonObject.CreateFrom(quotes);
return retval;
}
}
}
And my Quote class follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Web;
namespace WCFjQueryServiceApplication1
{
[DataContract]
public class Quote
{
[DataMember]
public string Symbol { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string LastTrade { get; set; }
[DataMember]
public string PercentChange { get; set; }
public Quote(string[] toParse)
{
try
{
this.Symbol = toParse[0] ?? toParse[0];
this.Name = toParse[1] ?? toParse[1];
this.LastTrade = toParse[2] ?? toParse[2];
this.PercentChange = toParse[3] ?? toParse[3];
}
catch
{
}
}
}
}
Note that the Quote class must be properly attributed in order for serialization
to work. What I'm doing above in the ctor is to take the Yahoo finance delimited
string, turning it into a string array, and turn it into a Quote instance. Then
the JsonObject.CreateFrom method is used to convert the List<Quote> collection to legal JSON.
Finally, this is what the client html page looks like:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>WcfJQueryService test</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<script id="quotesTemplate" type="text/x-jquery-tmpl">
<table cellspacing="2" cellpadding="2" border="0" width="50%" align="center" >
<tr style="background-color:#CCFFFF">
<td> ${Symbol}</td>
<td>${Name} </td>
<td>${LastTrade} </td>
<td>${PercentChange} </td>
</tr>
</table>
</script>
<script type="text/javascript">
$(document).ready(function () {
$("#submit").click(function (event) {
var symbols = $("#sym").val()
$.ajax({
type: "POST",
url: "./Service/",
data: { symbols: symbols },
cache: false,
success: AjaxSucceeded,
error: AjaxFailed
});
});
});
function AjaxSucceeded(result) {
BuildTable(result);
}
function AjaxFailed(result) {
$('#quotes').html(result.status + ' ' + result.statusText);
}
function BuildTable(msg) {
$("#quotes").empty();
$('#quotesTemplate').tmpl(msg).appendTo('#quotes');
}
</script>
</head>
<body>
<div>Enter quotes, space-separated</div>
<input type=text id=sym value="MSFT YHOO VZ GOOG" size=100 />
<input type=button value ="Get Quotes" id=submit />
<div id=quotes >
</div>
</body>
</html>
This is all pretty standard stuff. If you need more background, you can look at
a previous article here, or view the jQuery documentation.
Download the Visual Studio 2010 Solution here.