SOE REST Web Service - How to return different MIME content types?

5829
11
07-09-2010 02:04 PM
BrooksShannon
New Contributor
Folks,

I posted this in the ArcObjects forum, but thought I'd see if anyone here might have an idea, too.

I've been messing around with the new types in ESRI.ArcGIS.SOESupport, starting at 10.0 Prerelease.  I've successfully built two little server object extensions that each expose a REST resource and REST operation.

My first SOE, which encapsulates some spatial and attribute query business logic and returns results as JSON, works fine when upgraded to 10.0 Release.  However, my other SOE, which does not return JSON, but instead a PNG image, doesn't work as expected.

The latter's REST operation functionality is generally (for the purposes of this question) encapsulated in a method whose signature conforms to the ESRI.ArcGIS.SOESupport.OperationHandler delegate.  As such, I'm able to simply return my PNG image as an array of bytes.  Back at 10.0 Prerelease, this worked splendidly.  I was able to do some heavy processing on the server, dynamically create a PNG, and return it back to the client.

After upgrading to 10.0 Release, and making the requisite changes to my REST operation's method signature (by adding an additional parameter, requestProperties), my SOE no longer returns a PNG image that clients can use; instead, it returns the PNG's byte array representation as text.

I've spent some time today looking into the cause of this, and it seems like this is the case:

1) At 10.0 Prerelease, my SOE's HTTP response contained no content type.
2) At 10.0 Release, the response contains this content type: "text/plain;charset=utf-8"

This seems to explain why I'm not seeing an honest-to-God image, but just a text representation of the bytes that make up the image.

Upon discovering this, I hit the documentation, trying to see if there was a way I could coax the SOE into specifying a different content type within the response.  The documentation on resources.arcgis.com didn't appear to say much (in fact, the ESRI.ArcGIS.SOESupport namespace doesn't even appear to exist in the API reference).  For the heck of it, I started poking around in VS 2010's Object Browser.

To my surprise, I noticed a new class - RestResponseProperties.  Looking at its documentation in the object browser, this seemed to be something useful, and might just give me what I need.  However, I'm not actually able to get it to work.  When I try something like this...


private byte[] MyRESTOperation(NameValueCollection boundVariables,
                                       JsonObject operationInput,
                                       string outputFormat,
                                       string requestProperties,
                                   out string responseProperties)
{

// Create the PNG image, and get it as an array of bytes.
byte[] pngAsBytes = myImageGeneratorClass.Generate();

// Then attempt to specify the HTTP response's 
// content type.  Let it be a PNG image (and not something else, like 
// plain text).
RestResponseProperties props = new RestResponseProperties();
props.ContentType = "image/png";
responseProperties = props.ToString();           

// Return the bytes.
return pngAsBytes; 

}


...I get the following response (in Firefox, fwiw)

{"error":{"code":500,"message":"An unexpected error occurred processing the request.","details":[]}}


which, unfortunately, doesn't give me a lot of love.

Does anyone know what the issue might be?  Are there only certain content types that I can use for RestResponseProperties.ContentType?  Or, are there certain values for these four parameters of an OperationHandler

operationInput: A JsonObject instance with the input for the operation.
outputFormat: The requested output format, for example "json".
requestProperties: Well-known properties of the request.
responseProperties: Well-known properties for the response.


that I need to set, or check for?  Perhaps an output format other than "json"?

Thanks!
Brooks
0 Kudos
11 Replies
RahulRavikumar
New Contributor
responseProperties takes a 'jsonified' string for custom headers.

responseProperties[0] = @"{"Content-Type" : "image/png"}"


I hope this helps.
0 Kudos
BrooksShannon
New Contributor
Thanks for the quick reply! I'll give that a try over the weekend and see if it does the trick.

Have a good weekend,
Brooks
0 Kudos
RahulRavikumar
New Contributor
Actually for .NET, the string response properties is an out parameter.

So you should just do a :

 responseProperties = @"{"Content-Type": "..your mime type..", <...other HTTP headers...>}";
0 Kudos
BrooksShannon
New Contributor
Thanks, Rahul.  You're right about the string being an out parameter, I saw that myself, and formatted it as you suggested.

I seem to be making a little bit of progress.  Using the style of responseProperties that you suggested (a jsonified string), I'm able to set them to

responseProperties = "{\"Content-Type\" : \"image/png\"}";


which results in the following, in my response header

Content-Type: image/png; charset=utf-8


Unfortunately, both IE and Firefox claim the PNG that my SOE generates and returns isn't displayable / contains errors.  Looking at the raw data in WFetch, I do see some differences (in the encoding?) between what my SOE now returns, and what it used to return, when it worked, at 10.0 Prerelease. 

When it worked, the PNG data in the response looked like this:

�?�PNG\r\n
\x01A\n
\x000\x000\x000\rIHDR\x000\x000\x002�?�\x000\x000\x001\x008\x006\x000\x000\x000>ó�?%\x000\x000\x000\x001sRGB\x000®�?\x01Cé\x000\x000\x000\x004gAMA\x000\x000±\x00Büa\x005\x000\x000\x000\tpHYs\x000\x000\x00E�?\x000\x000\x00E�?\x001�?o¨d\x000\x000ÿ¥IDATx^ìý�?ÏmͶæ\x007íýs�?�sï9ç\x016\t\x006C�??JS\n
\x019�?TB \x004\x002\x004�?)�?�?�?�?�?\x015\x017�?]·Ý>�?;îd�?r�?ìXnZîXr�?�??;\x016B)ã\x004D�?��?�·ð-ò\x014{3ŸñŽ'ö3Ÿ5FD�?µ�?wïý}gméÝk�?�?�?1cFù�?�Ï�?�?ùþã�?ÿ�?wŸþ½Ï�?î\x017�?³0�?��?2®ŸþôÝû_üâÝ;üâ\x006~ü³Ÿ}r�?�?�?\x011�?�ÿèÏsõ�??g�?�ëª0êþá�?
»w�?|£W½{\x0077M�?qþ\x01EnL+~�?í|õùìû�?÷ñ{�?ûä�?�s�?�?íW~åÝG�?\x01Fç8�?Ÿú�?ýWõ�?ÿ\x007?x¹\x01E¿�?�\x01BŽù\x007g=p�?��?��?��?�ã©é�?\x01D�?Ÿ~\x01EFÏ5�?�?9»^ãÐ穎ݍçú;;v?\x016\x01Có2�?�?¿:Ÿ�?�åw´ÁQ/\x00Bÿ�?��?ìV÷^w5ù\x00CÏû*ò�?�?�?�ðŸ;*Ý
3>šŽG¯¿7ï?çu«òí�?rïu]\x01B,�?��?"#^µÝ~�?L�?ë�?�\x003o�??\x003ïÁç\x01F�?�?\x015�?¼#�?�]�?�?�?�û1ý�?¯�?\x01E\x00F¿\n
ZÝ1"ª*°�? �?\x01A\x000MÁ�??ÿùzðñ°8÷øx\x003�?F'�?0zŽô9\þú¯\n


The PNG data I get back at 10.0 Release, in my not-quite-functional SOE, looks like this:

�PNG\r\n
\x01A\n
\x000\x000\x000\rIHDR\x000\x000\x002�\x000\x000\x001�\x008\x006\x000\x000\x000>��%\x000\x000\x000\tpHYs\x000\x000\x00E�\x000\x000\x00E�\x001�o�d\x000\x000��IDATx^��\x00B�u�Z�\x007�ͩZ\x00FKDDJ�V�Gj�V=R���\x014\x00F����9��
�" *nN\x00B�����@j��AC\x008�\x015kCH�j�\x00E�����\r��\x010��?�?5D�!�\x0105;��U�|�o��7�^�u�1�|�������?9ï
¿½ï¿½ï¿½ï¿½c��7����o����u/���M���%��u���v����^x�?¿ï¿½ï¿½/��%/P�������?s\x01F��ï¿
½ï¿½ï¿½ï¿½\x017�my\w�����_x�C>$\x01F��\x00B�?�??�a��&n�r���?���\x00F��\x017n>�C_������>��_�������?o


Could there be something else that I need to specify in the responseProperties or outputFormat that indicates what encoding should be used (if, in fact, this is an encoding issue)?

Thanks!
Brooks
0 Kudos
RahulRavikumar
New Contributor
Looks like there is something wrong with the encoding of the image. The PNG header that you have shown, is invalid.

My guess is that the image bytes are getting re-encoded to UTF-8. So my question is, how are you generating the image? Can you show the code snippet that you are using?
0 Kudos
BrooksShannon
New Contributor
My guess is that the image bytes are getting re-encoded to UTF-8. So my question is, how are you generating the image? Can you show the code snippet that you are using?


Rahul,

Here's the section of my code that constructs a PNG image and returns it as an array of bytes.  (This assumes that you have an array of integers that represent the image's pixels, called "pixels," before starting.)

        // Create a BitmapSource instance, using the "pixels" array as the
        // source of the image.
        BitmapSource bitmapSource = BitmapSource.Create(
             width,
             height,
             96,
             96,
             PixelFormats.Pbgra32,
             null,
             pixels,
             (width * PixelFormats.Pbgra32.BitsPerPixel + 7) / 8);

        // Create a PngBitmapEncoder, which we'll use to actually create the
        // PNG image.
        PngBitmapEncoder encoder = new PngBitmapEncoder();
        // Add a single bitmap frame to the encoder. The BitmapFrame represents
        // the image.
        encoder.Frames.Add(BitmapFrame.Create(bitmapSource));

        // Then, create a MemoryStream. Since we're going to return the PNG
        // as an array of bytes, we need to convert the image contents to 
        // a byte array - and the MemoryStream serves as an intermediary 
        // vehicle to perform this task.
        MemoryStream memStream = new MemoryStream();
        // Tell the PngBitmapEncoder to save its contents to the memory
        // stream. (NOTE: You could use other streams, such as FileStream,
        // to save the contents to a different "media" if you wish.)
        encoder.Save(memStream);
        // Get an array of bytes from the MemoryStream, and return them.
        byte[] bytes = memStream.ToArray();       
        return bytes;


Bear in mind that this all worked at ArcGIS Server 10.0 Prerelease, and that my code hasn't changed.  It's as though ArcGIS Server 10.0 Release is, itself, (re)encoding the bytes in UTF-8 after I return them from my SOE REST operation handler.

Thanks again for all your help, Rahul. I appreciate it!
-Brooks
0 Kudos
BrooksShannon
New Contributor
Rahul / Anyone else who might be following this -

Given my last post, might anyone else have any ideas?

Thanks!
Brooks
0 Kudos
RahulRavikumar
New Contributor
The source for the image generation part looks correct to me. Can you set the content-type to something like "application/octet-stream", download the response from the SOE, save it as a PNG and open it in a Paint or something similar ?

Another way that i think that you could solve this problem is, you can write a file to your arcgisoutput directory (virtual output directory) and return a URL to the image as JSON (text/plain).
0 Kudos
SergeyMalgin
New Contributor
Hello.

I tried to reproduce this situation from scratch. I downloaded 'SimpleRESTSOE' example from ESRI (here)

and added simple 'png' method that reads a PNG file from disk and returns it as byte array.

I added the following snippet to CreateRestSchema()
RestOperation png = new RestOperation("png",
                                                      null,
                                                      new string[] { "png" },
                                                      PngHandler);
soeResource.operations.Add(png);


and the handler itself:
 private byte[] PngHandler(System.Collections.Specialized.NameValueCollection boundVariables,
            ESRI.ArcGIS.SOESupport.JsonObject operationInput,
            string outputFormat,
            string requestProperties,
            out string responseProperties)
        {

            responseProperties = "{\"Content-Type\" : \"image/png\"}";
            System.IO.FileStream fs = new System.IO.FileStream("c:\\temp\\8540.png", System.IO.FileMode.Open);
            System.IO.BinaryReader br = new System.IO.BinaryReader(fs);


            System.IO.MemoryStream ms = new System.IO.MemoryStream();

            const int count = 1024;
            while(true)
            {
                byte[] buf = br.ReadBytes(count);
                ms.Write(buf, 0, buf.Length);
                if (buf.Length < count)
                    break;
            }

            br.Close();
            fs.Close();
            fs.Dispose();

            return ms.ToArray();
        }


requested: h ttp://localhost/ArcGIS/rest/services/Test/MapServer/exts/SimpleRESTSOE/png?f=png

And I got back valid PNG displayed in browser. HTTP response headers are here:

HTTP/1.1 200 OK
Content-Type: image/png
ETag: -969848622
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Thu, 22 Jul 2010 18:16:45 GMT
Content-Length: 2561


NOTE: There's no extra charset=utf-8 . I believe this extra encoding bit makes all the difference. Please verify that you do not add this encoding somewhere else in your code.

Hope this helps.
Sergey Malgin
0 Kudos