The Freak Parade

Strange noises from the mind of Nathan Stults…
  • rss
  • Home
  • About The Freak Parade

Simple ASP.NET MVC Ajax Proxy

February 24, 2009

You may find, as you begin to combine the incredible power of jQuery, OpenLayers, and other JavaScript libraries capable of asynchronous (AJAX) behavior, that you want to serve some data to your client side code that doesn’t originate from your ASP.NET MVC application. Most commonly, I imagine, this need arises due to the fact that for security reasons browsers don’t allow AJAX calls to be made to domains external to the web app that served the original web page. The well known solution to this problem is to have your client side code make a request to your web application which will then make the request to the external resource and pass the result back to the client side AJAX request. There are other reasons, besides permitting cross domain AJAX calls, that you may want to use such a proxy. In our case we are hosting a GeoServer instance that serves web maps displaying proprietary, strategic customer spatial data. GeoServer has a relatively limited security model, at least in terms of remote administration, so we wanted to hide the GeoServer instance behind our firewall and layer our own, more sophisticated, role based security.

Whatever your motivations, this is a trivial feature to implement in almost any environment, and ASP.NET MVC is no exception. ASP.NET MVC’s flexible design also makes it possible to implement this kind of proxy in a very reusable, application-developer friendly way, reducing the code required on your controller to a single line. Of course, you could put your proxy in an HTTP (.axd) module and reduce the line count to zero lines of code if you wanted, but you may not want to open up a generic, free for all proxy mechanism to the client, for the same reasons that the cross domain restriction was created in the first place. I stole part of this solution from here. Here’s the code:

    public class ProxyResult : ViewResult
    {
        public ProxyResult(Uri targetUri)
        {
            TargetUri = targetUri;
        }

        public ProxyResult(string baseUrl,string queryString)
        {
            TargetUri = new Uri(baseUrl + "?" + queryString);
        }

        public Uri TargetUri
        {
            get; set;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            WebRequest proxy = WebRequest.Create(TargetUri);
            using (WebResponse proxyResponse = proxy.GetResponse())
            {
                context.HttpContext.Response.ContentType = proxyResponse.ContentType;
                using (Stream proxyResponseStream = proxyResponse.GetResponseStream())
                {
                    StreamHelper.CopyStream(proxyResponseStream, context.HttpContext.Response.OutputStream);
                }
            }
        }
    }

    public static class StreamHelper
    {
        public static void CopyStream(Stream input, Stream output)
        {
            var buffer = new byte[32768];
            while (true)
            {
                int read = input.Read(buffer, 0, buffer.Length);
                if (read < = 0)
                    return;
                output.Write(buffer, 0, read);
            }

        }

    }

To provide proxied access to a specific, known url to your client side code via your controller:

    public class MapController : SecuredController
    {
        private readonly string _wmsServerUrl = ConfigurationManager.AppSettings["GeoServerUrl"] + "/wms";
        private readonly string _wfsServerUrl = ConfigurationManager.AppSettings["GeoServerUrl"] + "/wfs";

        public ActionResult Show()
        {
            return View("Map");
        }

        public ProxyResult WmsProxy()
        {
            return new ProxyResult(_wmsServerUrl, Request.QueryString.ToString());
        }

        public ProxyResult WfsProxy()
        {
            return new ProxyResult(_wfsServerUrl, Request.QueryString.ToString());
        }
    }

And in your client side javascipt code, you simply reference your local web app, and whatever query string would normally be required:


points = new OpenLayers.Layer.WMS(
            "Units",
            "/Map/WmsProxy",
            {
                layers: 'hsi:someprivatelayer',
                styles: '',
                srs: 'EPSG:4326',
                format: 'image/png',
                tiled: 'true',
                tilesOrigin: "143.60260815000004,-43.851764249999995",
                transparent: true
            },
            {
                'opacity': 1,
                'isBaseLayer': false,
                'wrapDateLine': true
            }
        );
Comments
View Comments
Categories
General
Comments rss Comments rss
Trackback Trackback

The incredible, fallible symmetric Unit Test, and the beauty of Open Source software.

We’re doing some GIS development with the incredible NHibernate.Spatial library, which, like all of NHibernate has very good test coverage, from what I can tell. Even so, we ran into a puzzling bug where one of the standard spatial aggregations was returning an impossible result. So I downloaded the NHibernate Spatial source code and ran the unit tests, paying particular attention to the test centered around the spatial aggregation in question. All the tests passed with flying colors, but even so, it couldn’t have been correct, we had proof of that.

The problem? Symmetry. The dummy data used in the test produced identical results with incorrect behavior as it did with correct behavior. 

Here’s the data that was dummied up for the suite of tests:

"SRID=32719;POLYGON((0 0,1 0,1 1,0 1,0 0))"
"SRID=32719;POLYGON((0 1,1 1,1 2,0 2,0 1))"
"SRID=32719;POLYGON((1 1,2 1,2 2,1 2,1 1))"
"SRID=32719;POLYGON((1 0,2 0,2 1,1 1,1 0))"
"SRID=32719;POLYGON((0 0,1 0,1 1,0 1,0 0))"
"SRID=32719;POLYGON((0 1,1 1,1 2,0 2,0 1))"
"SRID=32719;POLYGON((1 1,2 1,2 2,1 2,1 1))"
"SRID=32719;POLYGON((1 0,2 0,2 1,1 1,1 0))"
"SRID=32719;POLYGON((0 0,1 0,1 1,0 1,0 0))"
"SRID=32719;POLYGON((0 1,1 1,1 2,0 2,0 1))"
"SRID=32719;POLYGON((1 1,2 1,2 2,1 2,1 1))"
"SRID=32719;POLYGON((1 0,2 0,2 1,1 1,1 0))"
"SRID=32719;POLYGON((0 0,1 0,1 1,0 1,0 0))"
"SRID=32719;POLYGON((0 1,1 1,1 2,0 2,0 1))"
"SRID=32719;POLYGON((1 1,2 1,2 2,1 2,1 1))"
"SRID=32719;POLYGON((1 0,2 0,2 1,1 1,1 0))"
"SRID=32719;POLYGON((0 0,1 0,1 1,0 1,0 0))"
"SRID=32719;POLYGON((0 1,1 1,1 2,0 2,0 1))"
"SRID=32719;POLYGON((1 1,2 1,2 2,1 2,1 1))"
"SRID=32719;POLYGON((1 0,2 0,2 1,1 1,1 0))"

It isn’t hard to imagine this limited set of three numbers combined in many different ways and coming up with identical results. While it makes for easy test writing, it makes for deceptively ambiguous testing.

In case you run into the same problem, the SpatialProjections.Envelope aggregate function for the PostGIS dialect will produce an envelope where the fourth coordinate contains two Y values, a simple transposition in a string format. A less regular distribution of sample data would have caught this straightaway.

The wonderful thing about this experience, however, was that this bug, which was crippling to the feature we were trying to implement, was easy to find, easy to fix, and within an hour we were back on track. When the patch is applied to the trunk, we can sync back up with the project, and in the mean time we won’t be implementing some awkward work around likely to break in future releases, waiting for some team somewhere to find it as important as we do. Of course, this is one of the hallmarks of Open Source, nothing new – but it is rather pleasant to experience it for yourself.

Comments
View Comments
Categories
General
Comments rss Comments rss
Trackback Trackback

Subscribe

Calendar

February 2009
M T W T F S S
« Nov   Oct »
 1
2345678
9101112131415
16171819202122
232425262728  

Recent Posts

  • Lucy in the Sky with Ruby
  • Simple ASP.NET MVC Ajax Proxy
  • The incredible, fallible symmetric Unit Test, and the beauty of Open Source software.
  • You Can’t Fill an Imaginary Hole
  • I don’t know but I’ve been told, ETL is gettin’ mighty old. BAM! BAM! EDA! I want my data right away!

Recent Comments

  • Kim Won on SOA, ESBs and JBOWS, oh my!
  • AbdirahmanDaud on New Open Source .NET CMS/EPS Platform Released Today: Sense/Net 6.0 Beta 1
  • None on Rinsing the SOAP from WCF (or, RESTful WCF Hyperlink Acupuncture)
  • chill71uk on Simple ASP.NET MVC Ajax Proxy
  • mrhookah on Rule Based Access Control using an Expression Evaluator

Tags

TDD Testing

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox