WCF Services hosted in IIS
Lately, I had some trouble hosting WCF Services on IIS on a remote VM running Windows Server 2003. Nothing really serious, but I figured out that it wouldn’t be bad to write down these errors for future reference.
Enabling hosting of WCF Services on IIS
This is probably trivial, but I’m still writing it here so I can refer to it later.
First, if the .NET 2.0 Framework was installed prior to IIS, IIS is not registered with ASP.NET. It can be done manually running the following command:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>aspnet_regiis -i
Then, you have to register .svc files in IIS, which is done by running the following command:
C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Communication Foundation>ServiceModelReg.exe -i
Once this is done, simply run iisreset in any command line window to restart IIS.
That’s it, IIS can now host WCF services.
Please note that if you deploy your website directly from Visual Studio, you’ll have to make sure that Frontpage Server Extensions are installed on the server.
Hosting a WCF Service in IIS
Anyway, hosting the service is very easy. The service endpoint will be a .svc file in which you’ll have to write only one line, referencing the service that is to be hosted.
<%@ ServiceHost Service="MyService" %>
On top of that, the Web.config file has to be modified in order to contain the service’s information like you would do in any WCF host application (ABC, except that you leave the address empty as the endpoint is the uri of the .svc file).
Permission Issues
If the service you are hosting interacts in some way with the file system or any other operation that might require permissions, don’t forget that once they are in IIS, they will have the limited rights of ASPNET (XP) or NETWORK SERVICE (WS2003) account, depending on the operating system.
I’m highlighting this because prior to host my services in IIS, I hosted them in a command line window. This is handy for debugging, and very simple to set up. However, this means that the WCF services are running with lots of privileges (power user or administrator, as you are developing). Once in IIS, you can still debug easily by attaching to the IIS process (Ctrl + Alt + P in Visual Studio).
Extension Methods and null Objects
C# 3.0 allows adding new methods to existing types. These are called Extension Methods. This allow programmers to add methods to existing types, even if they are not partial or event sealed.
These special methods have to be declared in a special way:
- They must be declared within a static class
- They must be static
- The first parameter of the method has to be the type that is "extended", preceded by the this keyword
So, for example, let’s copy the very useful IsNullOrEmpty method as an extension method.
public static class Extensions { public static bool IsNullOrEmpty(this String s) { return String.IsNullOrEmpty(s); } }
String s = ""; s.IsNullOrEmpty();
returns true. You can even do
"".IsNullOrEmpty();
which returns true as well.
Now, all of this is actually an illusion. When the compilers finds
"".IsNullOrEmpty();
Extensions.IsNullOrEmpty("");
So, when an extension method is used, there is one more parameter, that is of the type the extension method is defined on. In this case, String.
But, if it as parameter, it can be null, right? Yes!
String s = null; s.IsNullOrEmpty();
or even
((String) null).IsNullOrEmpty()
will both work, without trowing a NullReferenceException.
Nothing revolutionary here, but I found funny to "call" method on a null object and not have an exception raised.
A WSDL Endpoint for Yahoo! REST Api
As I am currently learning .NET and taking interest in SOA, I was searching for free web services using WSDL as their access point.
I headed for Google website.
Google Search Api
I quickly reached Google developer page only to find out that the Search API was deprecated and replaced by the Google AJAX Search API. The search web service is still available, but Google don’t issue key to use it anymore, making it unusable for new users like me. I found many blog posts criticizing this decision, but this is not the point here.
I headed to my second choice search engine: Yahoo!
Yahoo! REST Api
As you might know, Yahoo! provides a REST Api to use their web search service. The call is made using a simple URI with GET parameters, and the response uses a custom XML format.
In my opinion, it is a shame that their don’t offer a WSDL entry point, as it is a W3C open standard. It also forces each developer to write custom XML parsing code in order to process the answer, while there are many Api to call and process responses from WSDL format services without even seeing any XML.
Again, I stumbled upon may blog posts criticizing that decision, but again, it is not the point here. I had to go for my third search engine choice: Live Search (formerly MSN Search).
Live Search
On Windows Live Dev page, I crawled to find information on the search API. I found that the site is mess, as they recently changed name from msn to live. However, I managed to find what I was looking for: a WSDL file.
After a fight with Live account setting page, I finally found a way to obtain a Application ID that is needed to get a response from their web service.
I was now able to start coding my test application, at last!
First Test Application
Inspecting Live Search response format, I quickly wrote up two structs to hold search results.
The first one was designed to hold a result from a search:
public struct SearchResult { public string Title; public string Summary; public string Url; public string DisplayUrl; public string Date; }
The second one was designed to hold a set of SearchResult and an eventual error message:
public struct SearchResponse { public List ResultSet; public string Error; }
I added the WSDL file URI as a web reference to my project, and wrote up a function in order to call the Live Search web service:
public SearchResponse LiveSearch(string query) { SearchResponse rs = new SearchResponse(); rs.ResultSet = new List(); SearchResult sr; try { com.msn.search.soap.SearchRequest req = new com.msn.search.soap.SearchRequest(); com.msn.search.soap.SourceRequest[] srcreq = new com.msn.search.soap.SourceRequest(); srcreq = new com.msn.search.soap.SourceRequest(); srcreq.Source = com.msn.search.soap.SourceType.Web; req.Requests = srcreq; req.AppID = LiveApiID; req.Query = query; req.CultureInfo = "en-US"; foreach (com.msn.search.soap.SourceResponse response in new com.msn.search.soap.MSNSearchService().Search(req).Responses) { foreach (com.msn.search.soap.Result result in response.Results) { sr = new SearchResult(); sr.Title = result.Title; sr.Summary = result.Summary; sr.Url = result.Url; sr.DisplayUrl = result.DisplayUrl; if (result.DateTime != null) sr.Date = result.DateTime.ToString(); rs.ResultSet.Add(sr); } } } catch (SoapException soapex) { rs.ResultSet.Clear(); rs.Error = soapex.Detail.InnerText; } catch (Exception ex) { rs.ResultSet.Clear(); rs.Error = ex.Message; } return rs; }
A few exceptions later, my Windows Form test application was working fine. But I wanted more.
Second Test Application
During my search for a Yahoo! search Api, I stumbled upon this article , that gave me an idea: why not usie .NET to build a webservice that would do the search and return results using Yahoo! REST Api?
Looking at wat was returned by Yahoo! REST Api, I slightly modified my initial SearchResult struct:
public struct SearchResult { public string Title; public string Summary; public string Url; public string ClickUrl; public string DisplayUrl; public string ModificationDate; public string MimeType; public string Source; }
I then created a new ASP.NET project to hold my web service, copied the two struct into the new class and wrote a function to call Yahoo! RESP Api, parse it using XPath and fill the responses into a SearchResponse:
[WebMethod] public SearchResponse YahooSearch(string query, int maxResult) { SearchResponse rs = new SearchResponse(); rs.ResultSet = new List(); SearchResult sr; try { HttpWebRequest req = WebRequest.Create("http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=" + YahooApiID + "&query=" + query + "&results=" + maxResult.ToString()) as HttpWebRequest; using (HttpWebResponse res = req.GetResponse() as HttpWebResponse) { XmlDocument doc = new XmlDocument(); XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable); ns.AddNamespace("yns", "urn:yahoo:srch"); doc.Load(new StreamReader(res.GetResponseStream())); foreach (XmlNode node in doc.SelectNodes("/yns:ResultSet/yns:Result", ns)) { sr = new SearchResult(); sr.Title = node.SelectSingleNode("yns:Title/text()", ns).Value; sr.Summary = node.SelectSingleNode("yns:Summary/text()", ns).Value; sr.Url = node.SelectSingleNode("yns:Url/text()", ns).Value; sr.ClickUrl = node.SelectSingleNode("yns:ClickUrl/text()", ns).Value; sr.DisplayUrl = node.SelectSingleNode("yns:DisplayUrl/text()", ns).Value; sr.ModificationDate = node.SelectSingleNode("yns:ModificationDate/text()", ns).Value; sr.MimeType = node.SelectSingleNode("yns:MimeType/text()", ns).Value; rs.ResultSet.Add(sr); } } } catch (SoapException soapex) { rs.ResultSet.Clear(); rs.Error = soapex.Detail.InnerText; } catch (Exception ex) { rs.ResultSet.Clear(); rs.Error = ex.Message; } return rs; }
I am not new to XML, but still, I had trouble with namespaces on this one. I know, I know, shame on me…
Anyway, that was working! I then copied Liave Search function into the ASP.NET class in order to provide access to both services trough WSDL, and wrote up a small Windows Form application to do some testing.
Conclusion
I don’t think there is much to conclude from this experiment. To most experienced .NET developers, this will look like a piece of cake, but as I said this is all new to me. I learned some things along the way, so I think it was worth it.
If you want to let me know what you think, that you found mistakes or you want to enlighten me with your knowledge, please drop a comment.
