Today when you’re dealing with Web APIs, you often find yourself in the situation of handling JSON, either in the input for these APIs or in the output, or both. Some browsers have the means to pretty print the JSON from their dev tools. But you don’t always have that opportunity. That’s why there are tools to pretty print JSON. I’ve found quite a few of them on the web, but all the ones I’ve found have one terrible flaw: they actually send the JSON you’re trying to pretty print to the server (*shudder*). I don’t want my JSON data (sensitive or not) to be sent to some random servers!
All your JSON are belong to us!
Now as I wrote, I don’t particularly like the fact that my JSON data is sent over the wire for pretty printing. It may not be super secret or anything, but in these days, you cannot be careful enough. Besides, it’s completely unnecessary to do it. All you need is already in your browser! So I quickly built my own JSON pretty printer (and syntax highlighter). You can find it right here.
Offline JSON Pretty Printing to the Rescue
Actually, the design is very simple. All my JSON pretty printer is doing, is to take your JSON input and try to parse it as JSON in the browser.
So like it, hate it, use it or don’t: cymbeline.ch JSON Pretty Printer
Following the web app I mentioned in Fun with JSON and WCF (Part I), I ran into another issue with WCF hosted in IIS and serving the callers through JSON objects. My application uses integrated windows authentication to authenticate the users and grant / deny access based on the given credentials. Therefore, I have turned off anonymous access for the entire virtual directory the application is running in and turned on integrated windows authentication. Now when invoking the JSON service, I get the following exception.
[NotSupportedException: Security settings for this service require 'Anonymous' Authentication but it is not enabled for the IIS application that hosts this service.]
System.ServiceModel.Channels.HttpChannelListener.ApplyHostedContext(VirtualPathExtension virtualPathExtension, Boolean isMetadataListener) +11453217
System.ServiceModel.Activation.VirtualPathExtension.ApplyHostedContext(TransportChannelListener listener, BindingContext context) +75
System.ServiceModel.Channels.HttpTransportBindingElement.BuildChannelListener(BindingContext context) +119
System.ServiceModel.Channels.MessageEncodingBindingElement.InternalBuildChannelListener(BindingContext context) +67
System.ServiceModel.Channels.WebMessageEncodingBindingElement.BuildChannelListener(BindingContext context) +47
This indicates that according to the configuration of the service binding, anonymous access is to be allowed however IIS does not allow it. Apart from the fact that I don’t understand in the first place, why the service would care about this (if it was the other way around, I’d understand), fixing it is simple. It again requires changes in the Web.config, like follows.
<!-- ... -->
<!-- ... -->
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<service behaviorConfiguration="MyServiceTypeBehavior" name="MyService">
<endpoint address="" behaviorConfiguration="MyServiceAspNetAjaxBehavior"
The bindingConfiguration attribute on line 11 refers to the new webHttpBinding definition from lines 17 to 21. Client authentication is there specified to be integrated windows authentication.
One of the projects I am working on at home is something like a media player web application which I use to listen to my favorite music from anywhere. It has a backend database which keeps the music files in a way which allows fast search on information about the files from the tags in the files. The files are then played in a handcrafted media player written in Silverlight 2.0. This being a fancy web 2.0 application, I use AJAX to search the database and return the results as JSON objects. Luckily enough, WCF supports you with this since .net 3.5. All you basically need to do is create a WCF service for your web project in Visual Studio 2008 (SP1). Then you even have IntellliSense support for the client side wrapper of the service, which of course gets generated automatically. On top of this, you do not have to care too much about inter-browser compatibility: The generated scripts with the base libraries work fine with both IE and Firefox.
If on the other hand, you are running the application on a virtual site in IIS which supports multiple host headers (let’s say: foo.bar.com and www.foo.bar.com) you’re likely to run into an exception like the following.
[ArgumentException: This collection already contains an address with scheme http. There can be at most one address per scheme in this collection.
Parameter name: item]
System.ServiceModel.UriSchemeKeyedCollection.InsertItem(Int32 index, Uri item) +11520590
System.Collections.Generic.SynchronizedCollection`1.Add(T item) +67
System.ServiceModel.UriSchemeKeyedCollection..ctor(Uri addresses) +49
System.ServiceModel.ServiceHost..ctor(Type serviceType, Uri baseAddresses) +129
System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(Type serviceType, Uri baseAddresses) +28
System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri baseAddresses) +331
System.ServiceModel.HostingManager.CreateService(String normalizedVirtualPath) +11659932
System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +42
System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +479
This indicates that a binding address which starts with http is already in use, when trying to automatically add the second address. The solution to this is as simple as updating your Web.Config file like shown here. Please take a look at line #9:
<!-- ... -->
<!-- ... -->
<add prefix="http://foo.bar.com" />
<!-- ... -->
<!-- ... -->
Addendum from Feb 6: Apparently I was not entirely correct about using the service on a site with multiple host headers. While the above changes to Web.config fix the initial problem, they introduce a new problem. You’ll realize that this will work only for the requests using one of the host headers. The request to the service using the other host headers will throw a 404. This is a reported bug and will hopefully be fixed soon.