The post Offline JSON Pretty Printing appeared first on Tales of a Code Monkey.
]]>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.
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.
JSON.parse(yourJsonInput)
If that fails, I’m showing the parsing error and it’s done. If it succeeds, I get back a JavaScript object/array/value, which then I’m inspecting. For objects, I’m using basic tree navigation to go through all the properties and nested objects/arrays/values for pretty printing. That’s it, really simple. No need to transmit the data anywhere — it stays right in your browser!
So like it, hate it, use it or don’t: cymbeline.ch JSON Pretty Printer
The post Offline JSON Pretty Printing appeared first on Tales of a Code Monkey.
]]>The post Gzip Encoding an HTTP POST Request Body appeared first on Tales of a Code Monkey.
]]>The HttpMessageHandler abstract base class and its derived classes are used by the HttpClient class to asynchronously send HTTP requests and receive the response from the server. But since we don’t actually want to send the message ourselves – just massage the body and headers a little bit before sending – we’ll derive a new class GzipCompressingHandler
from DelegatingHandler so we can delegate sending (and receiving) to another handler and just focus on the transformation of the content. So here’s what that looks like.
public sealed class GzipCompressingHandler : DelegatingHandler { public GzipCompressingHandler(HttpMessageHandler innerHandler) { if (null == innerHandler) { throw new ArgumentNullException("innerHandler"); } InnerHandler = innerHandler; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpContent content = request.Content; if (request.Method == HttpMethod.Post) { // Wrap the original HttpContent in our custom GzipContent class. // If you want to compress only certain content, make the decision here! request.Content = new GzipContent(request.Content); } return base.SendAsync(request, cancellationToken); } }
As you can see, all we’re doing is just wrapping the original HttpContent in our GzipContent class. So let’s get right to that.
We’re almost there, all we need to do is actually compressing the content and modify the request headers to indicate the new content encoding.
internal sealed class GzipContent : HttpContent { private readonly HttpContent content; public GzipContent(HttpContent content) { this.content = content; // Keep the original content's headers ... foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers) { Headers.TryAddWithoutValidation(header.Key, header.Value); } // ... and let the server know we've Gzip-compressed the body of this request. Headers.ContentEncoding.Add("gzip"); } protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context) { // Open a GZipStream that writes to the specified output stream. using (GZipStream gzip = new GZipStream(stream, CompressionMode.Compress, true)) { // Copy all the input content to the GZip stream. await content.CopyToAsync(gzip); } } protected override bool TryComputeLength(out long length) { length = -1; return false; } }
Easy, right? Of course you could add other supported compression algorithms, using more or less the same code (or even adding some abstraction for different compression algorithms), but this is basically all that’s required.
Using the HttpMessageHandler and its associated classes makes it extremely easy to apply transformations to all (or a well-defined subset) of HTTP requests you’re sending. In this case, we’re applying Gzip-compression to the bodies of all outgoing POST requests, but the logic to decide when to compress can be as customized as you want; you could even apply Gzip-compression only if the requested URI ends with “.gzip” or for certain content types.
The post Gzip Encoding an HTTP POST Request Body appeared first on Tales of a Code Monkey.
]]>The post Migrating ASP.NET Web Services to WCF appeared first on Tales of a Code Monkey.
]]>[WebService(Namespace = "http://foo.bar.com/Service/Math")] public class MathAddService : WebService { [WebMethod] public int Add(int x, int y) { // Let's ignore overflows here ;-) return x + y; } }
The first thing we need to do is create a new interface which offers the same methods as the web service did and mark it as a service contract. This is required because the WCF endpoints are contract based, i.e. they need such an interface. So we extract the public web service interface of the MathAddService class and decorate it with the WCF attributes:
[ServiceContract(Namespace = "http://foo.bar.com/Service/Math")] [XmlSerializerFormat] public interface IMathAddService { [OperationContract(Action = "http://foo.bar.com/Service/Math/Add")] int Add(int x, int y); }
The ServiceContract attribute tells WCF to use the same namespace for the web service as ASP.NET did. If you don’t do this, your clients will not be able to use the migrated service because the namespaces don’t match. The XmlSerializerFormat attribute is used to make sure that WCF uses the standard SOAP format for messages. If you don’t specify this, your clients will likely see strange error messages of mismatching operations / messages. Then, for each method you exposed in the former web service, you need to add the exact same signature here, plus make sure that the OperationContract attribute for each method has the Action property set to
Now the next step is to implement this interface in a class, but we basically already have this in the former MathAddService class. So we just adapt the class’ definition as follows.
[WebService(Namespace = "http://foo.bar.com/Service/Math")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] [ServiceBehavior(Namespace = "http://foo.bar.com/Service/Math")] public class MathAddService : WebService, IMathAddService { [WebMethod] public int Add(int x, int y) { // Let's ignore overflows here ;-) return x + y; } }
As you can see, we’re also adding two new attributes. AspNetCompatibilityRequirements are used to make sure that the new WCF service is really capable of serving old clients. The ServiceBehavior attribute is used to make sure that the WCF hosted service really uses the correct namespace, i.e. the same as the old ASP.NET service used. By the way, you should find all the additional attributes in the System.ServiceModel and System.ServiceModel.Activation namespaces (from the System.ServiceModel assembly).
Now lets get to the configuration of endpoints and bindings for the web service. The following block shows you the new sections in the web.config file for the virtual directory which hosts the WCF service.
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.web> <!-- ... --> </system.web> <system.serviceModel> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <services> <service name="MathAddService" behaviorConfiguration="MathAddServiceBehavior"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="httpsIwa" bindingNamespace="http://foo.bar.com/Service/Math" contract="IMathAddService"/> </service> </services> <bindings> <basicHttpBinding> <binding name="httpsIwa"> <security mode="Transport"> <transport clientCredentialType="Windows" /> </security> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="MathAddServiceBehavior"> <serviceMetadata httpsGetEnabled="true" /> <serviceDebug httpsHelpPageEnabled="true" includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
As you can see on lines 19 and 20, we are using HTTPS and IWA for this particular binding, but you should of course make it the same as you had for your ASP.NET service. If you served all requests without HTTP based authentication and without SSL/TLS, then you should stick to that so you don’t break your clients :). You have to make sure that you are offering at least one basicHttpBinding, because that’s what closest matches the ASP.NET SOAP interface.
Finally, we add a new file called ‘MathAddService.svc’ in the virtual directory on IIS with the following contents.
<%@ ServiceHost Service="MathAddService" %>
This will use the implementation of the MathAddService class to serve the request for the IMathAddService interface. Of course your clients will have to be updated to use the new URL now (or you can try a 302 redirect but depending on the client’s policies, this may fail). In case your requests to the new SVC file produce strange results (or send you back the above contents of the file), in the IIS administrative tools make sure that the .svc extension is mapped properly. If it isn’t, you can run the aspnet_regiis.exe tool from the .NET framework to get that done.
The post Migrating ASP.NET Web Services to WCF appeared first on Tales of a Code Monkey.
]]>The post Fun with JSON and WCF, Part II appeared first on Tales of a Code Monkey.
]]>[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.BindingContext.BuildInnerChannelListener() +66 System.ServiceModel.Channels.MessageEncodingBindingElement.InternalBuildChannelListener(BindingContext context) +67 System.ServiceModel.Channels.WebMessageEncodingBindingElement.BuildChannelListener(BindingContext context) +47 System.ServiceModel.Channels.BindingContext.BuildInnerChannelListener() +66 [...]
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.
<configuration> <!-- ... --> <system.serviceModel> <behaviors> <!-- ... --> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <services> <service behaviorConfiguration="MyServiceTypeBehavior" name="MyService"> <endpoint address="" behaviorConfiguration="MyServiceAspNetAjaxBehavior" binding="webHttpBinding" bindingConfiguration="ServiceAuth" contract="MyService" /> </service> </services> <bindings> <webHttpBinding> <binding name="ServiceAuth"> <security mode="TransportCredentialOnly"> <transport clientCredentialType="Windows"/> </security> </binding> </webHttpBinding> </bindings> </system.serviceModel> </configuration>
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.
The post Fun with JSON and WCF, Part II appeared first on Tales of a Code Monkey.
]]>The post Fun with JSON and WCF appeared first on Tales of a Code Monkey.
]]>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:
<configuration> <!-- ... --> <system.serviceModel> <behaviors> <!-- ... --> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true"> <baseAddressPrefixFilters> <add prefix="http://foo.bar.com" /> </baseAddressPrefixFilters> </serviceHostingEnvironment> <services> <!-- ... --> </services> <bindings> <!-- ... --> </bindings> </system.serviceModel> </configuration>
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.
The post Fun with JSON and WCF appeared first on Tales of a Code Monkey.
]]>