Scott Hanselman

Breaking All The Rules with WCF

June 10, '09 Comments [35] Posted in Web Services | XML | XmlSerializer
Sponsored By

Sometimes, in my job, I go onsite at partners and work with them, sometimes architecturally, sometimes doing proofs of concepts to make sure they're comfortable with things working together.

This week I’m onsite at a large enterprise and one of the things they wanted to see, amongst many, was .NET interoperating with an existing Web Service. It's not important what platform their Web Service is running on, but it's not Windows and .NET. What was important was that they had WSDL and XSDs for the service, which put them above 99% of the Web Services I come upon in the enterprise.

The team here said that this particular web service used WS-Security and was a compliant web service. I figured, and told them, no problem. That's something .NET is good at. Moving angle-brackets around is something both I, and .NET do pretty well. I figured we had a number of options.

In this scenario was I going to be the Client, I could use:

  • WCF - svcutil.exe - good
  • System.Web.Services - wsdl.exe - pretty good
  • WebClient/XDocument/XmlDocument - not so good, but workable.

You get the idea. There were a few things wrong, though.

Bad-ish WSDL

They gave me the WSDL and when I ran svcutil.exe on it, I got this error (the elements have been changed to protect the innocent.)

C:\Users\Scott\Desktop\foo>svcutil foo.Wsdl foo.xsd /config:app.config
Microsoft (R) Service Model Metadata Tool
[Microsoft (R) Windows (R) Communication Foundation, Version 3.0.4506.2152]
Copyright (c) Microsoft Corporation. All rights reserved.

Error: Cannot import wsdl:binding
Detail: The WSDL binding named FooBinding is not valid because no match for
operation GetFooDetails was found in the corresponding portType definition.
XPath to Error Source: //wsdl:definitions[@targetNamespace='urn:foo:v1']/wsdl:
binding[@name='FooBinding']

Error: Cannot import wsdl:port
Detail: There was an error importing a wsdl:binding that the wsdl:port is dependent on.
XPath to wsdl:binding: //wsdl:definitions[@targetNamespace='urn:foo:v1']
/wsdl:binding[@name='FooBinding']
XPath to Error Source: //wsdl:definitions[@targetNamespace='urn:foo:v1']
/wsdl:service[@name='FooService']/wsdl:port[@name='FooPort']

I googled binged around for this to no avail. After staring at the file long enough, I realized that while this is a lousy error message (to be clear) it was telling me (obscurely) what was up all the while.

Here's a snippet of what I was looking at:

    <Type name="FooType">
<operation name="FooSearch">
<input message="tns:FooSearchRequest"></input>
<output message="tns:FooSearchResponse"></output>
<fault name="FooFault" message="tns:FooFault"></fault>
</operation>
</Type>

<binding name="FooBinding" type="tns:FooType">

<soap:binding style="document" trans="http://schemas.xmlsoap.org/soap/http"></soap:binding>

<operation name="FooSearch">
<soap:operation soapAction=""></soap:operation>
<input name="FooSearchRequest">
<soap:body use="literal"></soap:body>
</input>
<output name="FooSearchResponse">
<soap:body use="literal"></soap:body>
</output>
<fault name="FooFault">
<soap:fault name="FooFault" use="literal"></soap:fault>
</fault>
</operation>
...

The key was that their WSDL didn't have the name="" attribute on the input and output elements of the operation. The name needs to line up to the operation name in the binding.

<Type name="FooType">
<operation name="FooSearch">
<input name="FooSearchRequest" message="tns:FooSearchRequest"></input>
<output name="FooSearchResponse" message="tns:FooSearchResponse"></output>
<fault name="FooFault" message="tns:FooFault"></fault>
</operation>
</Type>

Once these new name="" attributes were added, I was able to generate my client-side stubs. I had to edit their WSDL, which sucks. However, you might argue svcutil.exe could chill out. Either way, a speed bump.

Claiming Compliance

I was told the Web Service would use WS-Security and a usernameToken. However, the actual message seemed like it was missing something.

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" xmlns:tns="urn:foo:v1" xsi:schemaLocation="http://www.w3.org/2003/05/soap-envelope http://www.w3.org/2003/05/soap-envelope/soap-envelope.xsd urn:foo:v1 com.foo.messages.v1.xsd">
<soapenv:Header>
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>secret</wsse:Username>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<tns:FooRequest>
...

It's been a while (about 18 months) since I did any WCF and WS-Security, but UsernameToken really needs to have a Password element also. Additionally, when you're using WS-Security, you typically get WS-Addressing, etc along for the ride. There's other headers I'd expect to see.

I trudged on, built up the message and tried to send it off. First problem was that the endpoint URI I had was http, not https. It's not possible to send a UsernameToken in plain-text - the system explicitly forbids it. However, their system was setup to default to basic HTTP. Some gnashing of teeth and I found an SSL endpoint I could use. However, it's a hassle to debug SSL traffic. I usually use ProxyTrace or TCPTrace but with SSL, not so much.

Sniffing SSL Traffic with a Proxy

I ended up using Charles, an HTTP Proxy that can act as a man-in-the middle, issue an SSL cert, then decrypt the traffic, and forward it along to the real endpoint. However, the SSL Cert Charles issues isn't from a certificate authority, so I had to make a Policy to blindly (temporarily) accept all certificates:

internal class AcceptAllCertificatePolicy : ICertificatePolicy
{
public AcceptAllCertificatePolicy(){}

public bool CheckValidationResult(ServicePoint sPoint,
X509Certificate cert, WebRequest wRequest, int certProb)
{
return true; //Always accept
}
}

Then I apply it in this (obsolete, but easy) way:

ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy();

Now I can run all my traffic through my local man-in-the-middle. I can set the proxy in my config file:

<basicHttpBinding>
<binding name="FooBinding"
...
proxyAddress="http://BigAssLaptop:8888"
useDefaultWebProxy="false">

or in my own binding:

WSHttpBinding oldBinding = new WSHttpBinding();
oldBinding.ProxyAddress = new Uri("http://BIGASSLAPTOP:8888");

FooPortTypeClient svc = new FooPortTypeClient(oldBinding, new EndpointAddress("https://example.com/foo/v1"));

This let me see the outgoing request. I noticed immediately that my WCF client was sending a LOT more stuff that I needed.

Breaking the Rules

It was hard for the client to hear, but here's the deal. They were using the usernameToken element, alone, in the WS-Security namespace in the style of an apiKey. You often see these kinds of APIs in the Web 2.0 world, when intense security isn't needed. You get a key that's unique to you, basically a GUID, and it also acts as a tracker for the provider.

However, this isn't how WS-Security usernameTokens work, or are supposed to work. Perhaps a better way would have been for them to use a custom soap:header, rather than trying to tunnel "apikey" semantics into an existing token.

At this point, regardless of relative-wrongness, I still need to get the WCF client to talk to this unusual endpoint. I could use one of the other XML mechanism available, or, gasp, a StringBuilder, but since I wasn't having trouble with the body of the message, just the envelope.

This essentially means that I wanted WCF to do something incorrect, on purpose. After a call to Steve Maine and team, along with some general freaking out, I was able to get WCF to spit out JUST a usernameToken, like this.

WSHttpBinding oldBinding = new WSHttpBinding();
oldBinding.Security.Mode = SecurityMode.TransportWithMessageCredential;
//Just the username
oldBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
//And basically nothing else
oldBinding.Security.Message.NegotiateServiceCredential = false;
oldBinding.Security.Message.EstablishSecurityContext = false;

//oldBinding.ProxyAddress = new Uri("http://BIGASSLAPTOP:8888");
//oldBinding.UseDefaultWebProxy = false;

//remove the timestamp
BindingElementCollection elements = oldBinding.CreateBindingElements();
elements.Find<SecurityBindingElement>().IncludeTimestamp = false;

//sets the content type to application/soap+xml
elements.Find<TextMessageEncodingBindingElement>().MessageVersion = MessageVersion.Soap12;
CustomBinding newBinding = new CustomBinding(elements);
FooPortTypeClient svc = new FooPortTypeClient(newBinding, new EndpointAddress("https://example.com/foo/v1"));
FooRequest req = new FooRequest();
//...etc...now it's just request and response.

Unfortunate, but I'll put this configuration of a custom binding, and hopefully when they fix it, it'll be a configuration change. This at least got us to a point where I can reliably call their web services.

Long day, but interesting stuff.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

The Weekly Source Code 40 - TweetSharp and Introducing Tweet Sandwich

April 3, '09 Comments [24] Posted in Open Source | Source Code | Windows Client | WPF | XML
Sponsored By

imageI just was at Quiznos hanging out with @QuiznosNick. He's a former Technology Executive who bought a Quiznos Franchise for his retirement. He's a major geek, and while chatting he wonder how he could take orders over Twitter. I wanted to see how easy it'd be to write as a real app. I could use PowerShell or Curl, but it's all in good fun, right?

For no reason at all, here is the thought process and code as I write it. I shall call it Tweet Sandwich.

Step 0 - Make it Pretty

Ok, WPF will do fine. Of course, before I do any work or planning or anything, I'll need an icon. ;) Search the web, find a nice, free non-commercial icon of a sandwich. Make it transparent with Paint.NET, make a 32x32 and a 16x16 at 24 bit color and paste into Visual Studio. Name it app.ico. Waste of time, perhaps, but it motivates me personally to do the pretty stuff first.

Take the Window1, call it Main and setup some controls. Grab a Free Twitter Bird PNG and a picture of a Sandwich, more Paint.NET and I've got a Main Form.

I tell you, being able to use Paint.NET and the clipboard, and a good understanding of how transparency works in Windows is an important skill to have. I'm no artist, but I can hack together a picture of a bird and a sandwich with the best of them.

Tweet Sandwich

What Settings Do I Need to Save

OK. Now, I put the Settings in the Properties dialog for the project.

image

Then I'll put in some small databinding code to make the text boxes in the form fill with the data from the settings.

<Window x:Class="TakeOrdersOverTwitterWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Tweet Sandwich" Height="459" Width="454"
xmlns:local="clr-namespace:TakeOrdersOverTwitterWPF.Properties">
<Window.Resources>
<local:Settings x:Key="settings" />
</Window.Resources>
<Grid>
<GroupBox Header="Settings" Name="groupBox1">
<Grid DataContext="{StaticResource settings}" >
<TextBox Name="twitterUserName" Text="{Binding Path=Default.TwitterUserName}"/>
...etc...

The keys are the Settings Resource that maps to the Properties (Settings) for the app. Then the Binding to the TextBox. Then we save them when the app closes with Settings.Default.Save();

How Often Will I Check For Orders?

Now, I'll setup a Timer to check for orders every five minutes:

DispatcherTimer twitterTimer = null;

private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.twitterTimer = new DispatcherTimer(new TimeSpan(0, 5, 0), DispatcherPriority.Normal, CheckForOrders, this.Dispatcher);
}

private void CheckForOrders(object sender, EventArgs e)
{
...
}

Gotta GET the Twitter Feed now...check the Twitter API. Do I want RSS or an API? The Twitter API has a REST model, and I need to see the replies to QuiznosNick, so I'll need to add some options to my application. I'll need to authenticate as QuiznosNick and ask for his replies list. I need his username and password. I'll probably want to call this API, which will let me see replies since some time. Looks like I can use the Date, or a status id, which is a big number that keeps getting bigger.

statuses/replies

Returns the 20 most recent @replies (status containing @username) for the authenticating user.

URL: http://twitter.com/statuses/replies.format

Formats: xml, json, rss, atom

Method(s): GET

Parameters:

  • since_id.  Optional.  Returns only statuses with an ID greater than (that is, more recent than) the specified ID.  Ex: http://twitter.com/statuses/replies.xml?since_id=12345
  • max_id. Optional.  Returns only statuses with an ID less than (that is, older than) the specified ID.  Ex: http://twitter.com/statuses/replies.xml?max_id=54321
  • since.  Optional.  Narrows the returned results to just those replies created after the specified HTTP-formatted date, up to 24 hours old.  The same behavior is available by setting an If-Modified-Since header in your HTTP request.  Ex: http://twitter.com/statuses/replies.xml?since=Tue%2C+27+Mar+2007+22%3A55%3A48+GMT
  • page.  Optional. Retrieves the 20 next most recent replies.  Ex:http://twitter.com/statuses/replies.xml?page=3

Returns: list of status elements

How Will I Call Twitter?

I could just make a call to Twitter using WebClient and Basic Auth, but since I'll only be paid in Sandwiches, I'll use TweetSharp. It's a smidge overkill for one API call, but it'll let me figure out if TweetSharp is fun or not. I could have also used LinqToTwitter, so try them both out and make your own judgment.

Here's how you would get the replies for an authenticated user using TweetSharp. I might switch this over to DirectMessages, which is less fun, but more secure, if things become a problem.

TwitterClientInfo info = new TwitterClientInfo() { ClientName = "TweetSandwich", ClientVersion = "1.0" };
var twitter = FluentTwitter.CreateRequest(info)
.AuthenticateAs(twitterUserName.Text, Password.Password)
.Statuses()
.Replies().AsXml();

string result = twitter.Request();

At this point, "result" has the XML I want in it.

Text Visualizer (3) 

The general structure of the nodes I'll need is:

statuses
status
created_at
id
text
user
id
name
location

I want "all status's greater than the lastid, and from those, extract the text, user id, name and location, sorting by created_at descending." In LINQ to XML, that might be:

XDocument doc = XDocument.Parse(result);
var statuses = (from d in doc.Descendants("status")
where int.Parse(d.Element("id").Value) > lastOrderNum
where d.Element("text").Value.Contains(orderString.Text)
select new
{
tweetid = int.Parse(d.Element("id").Value),
name = d.Element("user").Element("name").Value,
location = d.Element("user").Element("location").Value,
tweet = d.Element("text").Value,
dateTime = d.Element("created_at").Value.ParseTwitterDateTime()
}).OrderByDescending(t => t.dateTime);

However, TweetSharp has an object model that will deserialize JSON so I don't even need to do this. I can use their objects and still use LINQ, which makes this even cleaner. I can avoid all the strings and the dataType conversions as it's all hidden. Not to mention the hacky ParseTwitterDateTime extension method I got from Wally that he got from Tim Heuer.

TwitterClientInfo info = new TwitterClientInfo() { ClientName = "TweetSandwich", ClientVersion = "1.0" };
var replies = FluentTwitter.CreateRequest(info)
.AuthenticateAs(twitterUserName.Text, Password.Password)
.Statuses()
.Replies()
.Since(lastOrderNum)
.AsJson();

IEnumerable<TwitterStatus> statuses = replies.Request().AsStatuses();

var statusesFiltered = from d in statuses
where d.Id > lastOrderNum
where d.Text.IndexOf(orderString.Text, StringComparison.OrdinalIgnoreCase) != -1
orderby d.CreatedDate descending
select d;

Printing Orders

Now I just need to print them out. Every Quiznos has the same computer and the same printer that they got from the corporate office. I don't care though, I'll just print out an order on whatever default printer they have.

Printing is hard, and I only allocated a few hours to do this, but Printing a "Visual Object" in WPF is easy.

PrintDialog dlg = new PrintDialog();
dlg.PrintVisual(orderCanvas, "Whatever");

I could just make a Canvas with a bunch of controls to represent the last tweeted order, and print that. 

Tweet Sandwich (2)

However, the last time I did anything with Printing it was VB6 and it was hard. How hard it is now to make a real document today in WPF?  I figured I'd find out by trying.

I thought I'd use these FlowDocuments and make one that can show a Tweet. I went File | Add New Item | FlowDocument and call it SandwichOrder.xaml.

Add New Item - TakeOrdersOverTwitterWPF

I made a FlowDocument as a Resource that looked like this I could do it manually, do it in Word and save it, or use an HTML to XAML converter as others have.

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
FontSize="24" FontFamily="Georgia">
<Paragraph>
<TextBlock Text="{Binding Path=User.ScreenName}"/> in
<TextBlock Text="{Binding Path=User.Location}"/>
says on <TextBlock Text="{Binding Path=CreatedDate}"/>:
</Paragraph>
<Paragraph FontFamily="Arial">
<TextBlock Text="{Binding Path=Text}"/>
</Paragraph>
</FlowDocument>

I figured I want to DataBind to it, using the TweetSharp TwitterStatus object. Dynamically creating a document from a resource template, binding to it and printing it seems like a core scenario. Googling around, though found a lot of people having trouble with a few basic things that I was hitting into also.

NOTE: I might be doing this wrong, so I need to ask a WPF expert at Microsoft to see if I'm wrong about some things I think are too hard.

Dynamically Creating a FlowDocument, Data Binding and Printing It

First, I wrote this:

private void PrintOrder(TwitterStatus t)
{
var streamInfo = Application.GetResourceStream(new Uri("resources/SandwichOrder.xaml",UriKind.Relative));
FlowDocument doc = XamlReader.Load(streamInfo.Stream) as FlowDocument;
doc.DataContext = t;
PrintDialog dlg = new PrintDialog();
dlg.PrintDocument(((IDocumentPaginatorSource)doc).DocumentPaginator,"Tweeted Sandwich Order");
}

I felt OK about it, but not awesome. First, it was too hard to get my FlowDocument out of the Embedded Resource. I thought I could do something like App.Resources["SandwichOrder.xaml"]. I also wanted to do lines one and two in all one like like: var doc = FlowDocument.FromResource("SandwichOrder.xaml").

Finally, the weird interface cast in the PrintDocument line was totally counter intuitive. Seemed like PrintDocument should have an overload that takes a FlowDocument.

Then I tried to print. When I printed, the data binding didn't happen. I just got the basic text. More Googling showed there's a threading issue and the binding happens on another thread?

Now I had to add what appears to be the WPF equivalent of "DoEvents" - that big dispatcher call that releases the thread to do pending stuff. This CAN'T be right. I MUST be doing something wrong, so I'll update this post as I learn.

private void PrintOrder(TwitterStatus t)
{
var streamInfo = Application.GetResourceStream(new Uri("resources/SandwichOrder.xaml",UriKind.Relative));
FlowDocument doc = XamlReader.Load(streamInfo.Stream) as FlowDocument;
doc.DataContext = t;
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.SystemIdle, new DispatcherOperationCallback(delegate { return null; }), null);
PrintDialog dlg = new PrintDialog();
dlg.PrintDocument(((IDocumentPaginatorSource)doc).DocumentPaginator,"Tweeted Sandwich Order");
}

After this printing and databinding worked, except the TextBlocks I was using didn't wrap, so the orders got clipped. I tried using a <Run> but they don't support DataBinding. I ended up having to add a BindableRun class as more Googling showed more confusion. Folks have created Bindable Tables also, it seems and this BindableRun pattern seems common. I need to check on why this isn't built in.

Now my FlowDocument looks like this:

<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:bt="clr-namespace:TakeOrdersOverTwitterWPF.BindableText;assembly=TakeOrdersOverTwitterWPF"
FontSize="24" FontFamily="Arial">
<Paragraph FontSize="48">Twitter Order</Paragraph>
<Paragraph>
<bt:BindableRun BoundText="{Binding Path=User.Name}" />
(<bt:BindableRun BoundText="{Binding Path=User.ScreenName}" />) in
<bt:BindableRun BoundText="{Binding Path=User.Location}" /> says on
<bt:BindableRun BoundText="{Binding Path=CreatedDate}" /> :
</Paragraph>
<Paragraph FontFamily="Arial">
<bt:BindableRun BoundText="{Binding Text}" />
</Paragraph>
</FlowDocument>

And it prints exactly as it should. So, for printing, to recap, I:

  • Made a Template FlowDocument and embedded it as a Resource
    • Had to use a BindableRun
  • Pulled it out of the resuorce, DataBinding, did a weird dispatcher hack, printed it.

Too much Googling on that one. It wasn't nearly as obvious to print as it was to do the Graphical UI.

image

This app, modified, could be used to waste dead trees by printing out tweets that contain words. Mark Nijhof is using TweetSharp to tweet what he blogs. It could also make the computer beep when a sandwich order arrives. Oh! There's an idea!

Tomorrow at lunch, I'll present Tweet Sandwich, the first automated Twitter-based Sandwich Ordering System to @QuiznosNick and hope I earned a free sandwich or ten. ;)

Download the Source from SkyDrive

Please offer feedback, as I'm sure there's lots of ways this can be cleaner. For example, a bound listbox of Replies/Orders (starts to look like a real Twitter Client, then.)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

.NET 4.1 Preview - New Base Class Library (BCL) Extension Methods - RFC

April 1, '09 Comments [16] Posted in ASP.NET | DLR | Javascript | Learning .NET | Microsoft | Musings | Open Source | PHP | Programming | Python | Silverlight | Source Code | Tools | VB | Web Services | Windows Client | XML
Sponsored By

As web programmers, we use a lot of strings to move data around the web. Often we’ll use a string to represent a date or an integer or a boolean. Basically "1" in instead of 1, or "April 1, 2009" rather than a proper ISO-8601 formatted culture-invariant date.

While these strings are flying around via HTTP it's not a huge deal, but sometimes this loose, even sloppy, use of strings can leak into our own code. We might find ourselves leaving the data times as strings longer and longer, or not even bothering to convert them to their proper type at all. This problem is made worse by the proliferation of JSON, and schema-less/namespace-less XML (that I've often called "angle-bracket delimited files" as they're no more useful than CSVs in that point.

.NET 4.0 is pretty much locked down, but version 4.1 still has some really cool "Futures" features that are being argued about. If we don't know the type of a string, or we want to leave the string, as a string, longer than usual, what if we had an class that could be both a string and another type, essentially deferring the decision until the variable is observed. For example:

StringOr<int> userInput= GetUserInput("Quantity"); 
string szUserInput=userInput.StringValue; 
int intUserInput=userInput.OtherValue;

Sometimes you just don't know, or can't know.

This reminds me of a similar, but orthogonal physics concept, that of the Heisenberg Uncertainty Principle. Sometimes you know that an object is a string, and sometimes you know how long it is, but you can’t know both at the same time.

One of my favorite jokes goes:

Heisenberg gets pulled over by the police. The officer asks, “Do you know how fast you were going?” Heisenberg answers, “No, but I know exactly where I am!”

This library doesn't solve THAT issue, with respect to strings, but we’ve got folks in DevDiv working on this and many other metaphysical - and physical - problems as they apply to computer science.

Big thanks to Eilon, who's working hard to get this pushed into the .NET 4.1 Base Class Library. Visit Eilon's blog for more details on this new library, more code, graphics and details on how Intellisense will handle this new case.

Hopefully, someone is working to make this important new library Open Source.

Your thoughts, Dear Reader?

Related Posts

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

The Weekly Source Code 32- Atom, AtomPub and BlogSvc, an AtomPub Server in WCF

August 21, '08 Comments [7] Posted in ASP.NET | Source Code | Web Services | Windows Client | WPF | XML
Sponsored By

In my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you thirty-second in a infinite number of posts of "The Weekly Source Code."

UPDATE: The BlogSvc.NET code has been refactored considerably, so consider checking out the Recent CheckIns to see what's changed..

BlogSvcBlogSvc.NET - The AtomPub Server for WCF and .NET

Much respect to people who not only release Open Source Software, but also take the time to get a nice, clean logo for their project as BlogSvc.NET did. It's written by Jarrett Vance and the code is up on CodePlex with Documentation and details on the main BlogSvc.NET site.

It also appears that the site at http://www.blogsvc.net is also the Demo Site for BlogSvc itself! Actually, if you download the source, you're downloading the complete implementation of the BlogSvc.net website. Booyachaka. Much respect.

Since the project uses Atom and AtomPub he can work against it using Windows Live Writer. You can learn all about how WLW likes Atom by reading the series of posts the most excellent Joe Cheng of the Live Writer team did on how this is all implemented.

Made it this far and wondering what the heck it all means? Let us turn to Wikipedia who gets it mostly right this time.

The name Atom applies to a pair of related standards. The Atom Syndication Format is an XML language used for web feeds, while the Atom Publishing Protocol (short AtomPub or APP) is a simple HTTP-based protocol for creating and updating web resources.

One way to look at it is that RSS is as a syndication format that looked like XML, but didn't really respect XML at its heart. Atom does. For publishing to blogs or content sites, the Blogger/MetaWeblog APIs are based on XML/RPC (a protocol that you either love or it makes you ill. Or both) while AtomPub is based on the RESTmodel and Atom and went through a more Standardsy adoption process. Ultimately it's a safe bet to use Atom and/or AtomPub. Everyone's pretty much on board. Microsoft got on board earlier this year. My hero Pablo Castro, the Principal Architect of ADO Data Services is supporting AtomPub in his project.

That said, BlogSvc is:

"...an open source implementation of the Atom Publishing Protocol. It is built on top of a provider model. There are providers for the file system and databases. The service is compatible with Live Writer."

Enough chatter, let's see some code. It's 0.3, but it's marching right along. When Jarrett starts using .NET 3.5 SP1, he'll likely get to do some "coding via subtraction" and swap out a lot of the boring Atom Object Model code with the new System.ServiceModel.Syndication and use the ServiceDocument. Jarreyy might be able to remove most of his "BlogService.Core" project. The new 3.5 SP1 stuff includes Atom and AtomPub object models as well as an Rss20 object model. It'll be interesting to see if those new object models can stand up against Argotic.

He's got a pretty comprehensive IAtomPub namespace with the UriTemplates that correspond to the "tag" URI Scheme from RFC 4151 that he's complying with.

[ServiceContract]
public interface IAtomPub
{
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "{workspaceName}/{collectionName}")]
Stream CreateResource(string workspaceName, string collectionName, Stream stream);

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "{workspaceName}/{collectionName}/*")]
Stream Annotate(string workspaceName, string collectionName, Stream stream);

[OperationContract]
[WebGet(UriTemplate = "{workspaceName}/{collectionName}/*")]
Stream RetrieveResource(string workspaceName, string collectionName);

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "{workspaceName}/{collectionName}/*", Method = "PUT")]
Stream UpdateResource(string workspaceName, string collectionName, Stream stream);

[OperationContract]
[WebInvoke(UriTemplate = "{workspaceName}/{collectionName}/*", Method = "DELETE")]
void DeleteResource(string workspaceName, string collectionName);

[OperationContract]
[WebInvoke(UriTemplate = "{workspaceName}/{collectionName}/*", Method = "HEAD")]
void HeadResource(string workspaceName, string collectionName);

[OperationContract]
[WebGet(UriTemplate = "service")]
AppService RetrieveService();

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "service", Method = "PUT")]
AppService UpdateService(AppService serviceDoc);

[OperationContract]
[WebGet(UriTemplate = "{workspaceName}/{collectionName}/category?scheme={scheme}")]
AppCategories RetrieveCategories(string workspaceName, string collectionName, string scheme);

[OperationContract]
[WebGet(UriTemplate = "{workspaceName}/{collectionName}")]
AtomFeed RetrieveFeed(string workspaceName, string collectionName);
}

Now, let's look at CreateMedia which would be called as a part of CreateResource for any type of data that's not an Atom content type. CreateMedia is the kind of thing you'd want to do if you were attaching a picture or a video to your blog post.

public AtomEntry CreateMedia(string workspace, string collection, Stream stream)
{
try
{
if (!Authenticate(true)) return null;
string slug = WebOperationContext.Current.IncomingRequest.Headers["Slug"];
string contentType = WebOperationContext.Current.IncomingRequest.ContentType;
string etag;
AtomEntry entry = Atom.Provider.GetService().GetCollection(workspace, collection).CreateMedia(stream, slug, contentType, out etag);
WebOperationContext.Current.OutgoingResponse.ContentType = Atom.ContentTypeEntry;
WebOperationContext.Current.OutgoingResponse.Headers["Content-Location"] = entry.Location.AbsoluteUri;
WebOperationContext.Current.OutgoingResponse.ETag = etag;
WebOperationContext.Current.OutgoingResponse.SetStatusAsCreated(entry.Location);
return entry;
}
catch (ResourceNotFoundException rnfe)
{
WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound(rnfe.Message);
}
catch (InvalidContentTypeException icte)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotAcceptable;
WebOperationContext.Current.OutgoingResponse.StatusDescription = icte.Message;
}
catch (Exception ex)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
WebOperationContext.Current.OutgoingResponse.StatusDescription = ex.Message;
}
return null;
}

Personally I'd have done a little refactoring in and pulled out WebOperationContext.Current.OutgoingResponse into a variable, but you get the idea. I think there's some more opportunity for some larger, more profound refactorings as this method looks a lot like most of the others. CRUD (Create, Read, Update, Delete) code does tend to repeat.

I think Jarrett will be able to move all the try/catch and the caching work for eTags and what not into some helpers. The project site says the next step is to add some authentication and have that factored out into a module, and that's confirmed by his copious TODO comments.

I really enjoy reading code where someone, like Jarrett, has taken the time to actually put in TODO: comments. It's easier to get into the programmers head if you know where they are going, and TODOs also tell you where they've been. I can tell when I'm reading this that the project owner knows what's good and what's not, and from that, gauge the trajectory of the project.

image

Great stuff and I hope that we see more work like this in the .NET Open Source space around Atom and AtomPub. Why should you care? Perhaps you work for a company that has a content management system, or a sales catalog system where sales people want to edit project web pages. Why not put something like BlogSvc in front of it and let your sales folk use Windows Live Writer or some other AtomPub compliant editor? The possibilities are really endless.

WEIRD SIDE NOTE: I had a weird COM Interop Error when opening his "Web.csproj" - I'd get "System.Runtime.InteropServices.COMException." I opened the csproj in notepad and noticed two things. First, the IISUrl was set to his computer name, http://rocket, so I changed that to http://localhost. The other was that it said UseIIS="True" so I suspect that was a lame error message trying to tell me that I didn't have the IIS 6.0 compatibility extensions on my Vista system. I set it to UseIIS="False" and the project loaded fine.
CONFIRMED: I ran into this bug on Connect and I shall now yell about it internally.

It'll be interesting to see how BlogSvc and ADO Data Services in 3.5 SP1 intersect. BTW: You can learn more about ADO Data Services by watching the How Do I videos on MSDN. (Yes, the video player sucks, but you can download the WMVs.)

Technorati Tags: ,,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

The Weekly Source Code 30 - VB.NET with XML Literals as a View Engine for ASP.NET MVC

July 3, '08 Comments [12] Posted in ASP.NET | ASP.NET MVC | VB | XML
Sponsored By

I was literally in the middle of writing the post when I saw a message from Andrew Davey about how he had implemented the same idea! Of course, his is way better, so I got to code via subtraction. That means subtracting out the crap I had written in a few minutes and dig into his code.

There are no unique ideas, right? ;) Either way, it's fun when the same idea is being thought about simultaneously.

Here's the general concept. A few weeks back I was talking with Avner Aharoni, a Language Program Manager, and he had been kicking around the idea of VB9's XML Literals making friendlier Views within ASP.NET MVC.

I've blogged about VB9's rocking sweet XML support before. It lets you create XML like this. Note the lack of strings...the XML is there in the language and the compiler and intellisense are all aware of it.

 Dim books = <bookstore xmlns="http://examples.books.com">
<book publicationdate=<%= publicationdate %> ISBN=<%= isbn %>>
<title>ASP.NET Book</title>
<price><%= price %></price>
<author>
<first-name><%= a.FirstName %></first-name>
<last-name><%= a.LastName %></last-name>
</author>
</book>
</bookstore>

Views in ASP.NET MVC

Starting with the Northwind ASP.NET MVC Sample Code for Preview 3 that Phil updated, let's look at the List View:

<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<h2><%=ViewData.CategoryName %></h2>
<ul>
<% foreach (Product product in ViewData.Model.Products.Model) { %>
<li id="prod<%= product.ProductID %>">
<%= product.ProductName %>
<span class="editlink"">
(<%= Html.ActionLink("Edit", new { Action="Edit", ID=product.ProductID })%>)
</span>
</li>
<% } %>
</ul>
<%= Html.ActionLink("Add New Product", new { Action="New" }) %>
</asp:Content>

This is straight from the ASPX page with inline C#.

ASP.NET MVC Views using VB9's XML Literal Support

imageNow, we thought it'd be cool/interesting/potentially-something if we could use the XML Literal support to get, as Andrew puts it "compiled, strongly typed, intellisense friendly views." Sure, we mostly get that with the ASPX pages, but perhaps this would be better/easier/something? Keep in mind here that we're playing.

Your opinions on if this is a good idea or something to move forward would be useful. Leave comments and I'll compile and give them directly on to the VB and MVC team(s)! Remember, it can look like however you think it should, so don't be constrained by my spike or Andrew's.

Here's why Andrew thinks it's cool, quoted from a post on the ALT.NET mailing list:

Some key features I've used.
- <%= From ... Select ... %> to create repeated elements
- VB's ternary "If" operator for conditional output
- X-linq to post process the HTML before sending it
- choose between indented and compressed XML output
- modules of functions as "controls" - it's so simple :)

Here's what my solution looks like in Visual Studio. See how the ListVB.aspx has no "+" sign. There's no code-behind .cs file there even though this is a C# project. The meat of the View is in another assembly (although you could conceivably do something freaky and get VB and C# to live in the same assembly (be sure to read the comments)).

Actually, the ListVB.aspx file is VB, not C# and refers not to to a code-behind, but another class in another DLL, specifically the VBViews assembly.

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" 
AutoEventWireup="true" Inherits="VBViews.ListVB" Title="Products" %>
<%@ Import Namespace="NorthwindModel" %>
<%@ Import Namespace="System.Collections.Generic" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<h2><%=ViewData.Model.CategoryName%></h2>
<% = GetList() %>
</asp:Content>

Here's the Visual Basic code in the other assembly.

Imports System.Xml.Linq
Imports NorthwindDemo.Models

Partial Public Class ListVB
Inherits System.Web.Mvc.ViewPage

Function GetList() As XElement
Dim c As Category = CType(ViewData.Model, Category)
Return <ul><%= From product In c.Products _
Select _
<li id=<%= "prod" & product.ProductID %>>
<span class="editlink">
<a href=<%= "/Products/Edit/" & product.ProductID %>>
<%= product.ProductName %>
</a>
</span>
</li> %>
</ul>
End Function
End Class

This won't really be clear without some syntax highlighting to make the point, so here it is again, but this time as a screenshot. See now the VB code, XML and <% %> blocks are all together in same line? VB is just generating XElement's which in turn will turn into a string when "rendered" by the ASPX page.

The same VB9 code as before, except this time in color.

Andrew's Take #2 on the VB9 XML Literals as ASP.NET MVC Views

Andrew Davey's NRest project is more than just VB9 Views. It's a REST web framework for ASP.NET using the Nemerle programming language (using the May CTP). You can browser or GET his code with SVN here: http://svn.assembla.com/svn/nrest/trunk/. It's also a nicely laid out solution that uses the Ninject IOC but I'll cover that later. Do check out Andrew's screencast about his NRest project.

His code is a mix of C#, Nemerle and VB. The Website, Tests and Services are in C# and the Ninject modules are in Nemerle, along with the meat of the main NRest project. I think he could have used more of System.MVC, specifically the View Engines, that he did, but I'm still grokking his intent.

He's got a hierarchy in VB with a MainPageBase, Page, in order to achieve a kind of Master Pages:

Public Class MainPageBase(Of TChrome As MainChrome, TContent)
Inherits Page(Of TChrome, TContent)

Public Overrides Function GetHtml() As XElement
Return _
<html>
<head>
<title><%= Chrome.Title %></title>
<link href="/styles/demo.css" type="text/css" rel="Stylesheet"/>
<%= GetHeadContents().Elements() %>
</head>
<body>
<h1><%= Chrome.Title %></h1>
<%= GetBodyContents().Elements() %>
</body>
</html>
End Function

Public Overridable Function GetHeadContents() As XElement
Return <_></_>
End Function

Public Overridable Function GetBodyContents() As XElement
Return <_></_>
End Function

End Class

So a Hello World page in VB would be very simple, just this:

Public Class CustomerPage
Inherits MainPageBase(Of CustomerPageData)

Public Overrides Function GetBodyContents() As XElement
Return _
<_>
<p>Hello <%= Content.FirstName & " " & Content.LastName %></p>
</_>
End Function
End Class

All of this is a work in progress, but it's really cool that we're all trying to push the envelope and not afraid to try crazy stuff in order to make things better. It'll be cool for me to read this post in a year and either say "ew" or "cool!" depending on what direction we all went.

Have you done anything cool or crazy with Views and ViewEngines, Dear Reader?

Related Posts

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Page 1 of 59 in the XML category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.