body parameter 'width'. GET operations cannot have a body?

13,847

Solution 1

In the attributes, you need to tell the runtime that you're expecting the width and height as an URL parameter.

At the moment, the runtime assumes that you're calling the URL without parameters, but the method being called expects parameters, so the runtime really doesn't know how to find the values to pass to your method for width and height.

This can look like

[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, UriTemplate = "picture/{width}/{height}")]
Stream GetImage(string width, string height)
{
    int w, h;
    if (!Int32.TryParse(width, out w))
    {
        // Handle error: use default values
        w = 640;
    }
    if (!Int32.TryParse(height, out h))
    {
        // Handle error use default values
        h = 480;
    }

    ....
}

and you will need to call the URL like http://test.tld/picture/320/200.

Solution 2

UriTemplate = "picture/"

Should be something like

UriTemplate = "picture?w={width}&h={height}"

This tells WCF how to get the width and height parameters from the URL.

Share:
13,847
G Gr
Author by

G Gr

New to C# and Matlab, enjoy learning via questions which makes stackoverflow my adopted home!

Updated on June 08, 2022

Comments

  • G Gr
    G Gr almost 2 years

    Im trying to get an image from a wcf rest service like so:

    [ServiceContract]
    public interface IReceiveData
    {
        [OperationContract]
        [WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Xml, UriTemplate = "picture/")]
        //this line is wrong though
        Stream GetImage(int width, int height);
    }
    public class RawDataService : IReceiveData
    {
        public Stream GetImage(int width, int height)
        {
            // Although this method returns a jpeg, it can be
            // modified to return any data you want within the stream
            Bitmap bitmap = new Bitmap(width, height);
            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    bitmap.SetPixel(i, j, (Math.Abs(i - j) < 2) ? Color.Blue : Color.Yellow);
                }
            }
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            ms.Position = 0;
            WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
            return ms;
        }
    }
    

    In my host application:

    class Program
        {
            static void Main(string[] args)
            {
                string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
                ServiceHost host = new ServiceHost(typeof(RawDataService), new Uri(baseAddress));
                host.AddServiceEndpoint(typeof(IReceiveData), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
                host.Open(); // this line
                Console.WriteLine("Host opened");
                Console.ReadLine();
    

    I get this error:

    Operation 'GetImage' in contract 'IReceiveData' uses GET, but also has body parameter 'width'. GET operations cannot have a body. Either make the parameter 'width' a UriTemplate parameter, or switch from WebGetAttribute to WebInvokeAttribute.

    Im not sure how you set the webinvoke/UriTemplate method for an image or how you get an image and return it. Can some one post the correct way to display an image in this example.

    EDIT

    If I try the below answer and use UriTemplate = "picture?w={width}&h={height}" as my UriTemplate when navigating to http://www.localhost.com:8000/Service/picture?width=50&height=40 I recieve an error in my code:

    public Stream GetImage(int width, int height)
            {
                Bitmap bitmap = new Bitmap(width, height); // this line
                for (int i = 0; i < bitmap.Width; i++)
                {
                    for (int j = 0; j < bitmap.Height; j++)
                    {
                        bitmap.SetPixel(i, j, (Math.Abs(i - j) < 2) ? Color.Blue : Color.Yellow);
                    }
                }
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                ms.Position = 0;
                WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
                return ms;
            }
    

    Which states ArguementException was unhandled by user code:Parameter is not valid.

  • G Gr
    G Gr about 12 years
    This runs but when I try to navigate to http://localhost:8000/Service/picture?width=50&height=40 I get an error on this line: Bitmap bitmap = new Bitmap(width, height); which says Parameter is not valid.
  • G Gr
    G Gr about 12 years
    Your method returns: Operation 'GetImage' in contract 'IReceiveData' has a path variable named 'width' which does not have type 'string'. Variables for UriTemplate path segments must have type 'string'.
  • Thorsten Dittmar
    Thorsten Dittmar about 12 years
    You are right, I forgot to change int to string. A URL has no concept of whether a parameter is an int or a string, so everything is passed as a string and you need to validate and cast the values in your code accordingly. Anyway, my solution is the correct answer, as I have exactly this running in one of my applications, too :-D
  • Thorsten Dittmar
    Thorsten Dittmar about 12 years
    And this is also because {width} and {height} are strings and your method expects int. Use my solution below and you will see that a) the URLs look cleaner than Barg's and b) things work after casting the strings to int.
  • Thorsten Dittmar
    Thorsten Dittmar about 12 years
    Ok, I'll update my answer again to make clear that of course the line // Handle error should be replaced by some code to handle the error...
  • G Gr
    G Gr about 12 years
    yeah i just realised i was being thick.
  • Thorsten Dittmar
    Thorsten Dittmar about 12 years
    No problem :-) I tend to shorten things too much sometimes when I answer here.
  • Thorsten Dittmar
    Thorsten Dittmar about 12 years
    I don't really know what you mean... I assume you want to call /picture and then get an image with default dimensions? This could be done by adding another method that takes no parameters and calls return GetPicture("640", "480"); for example. In my code above, you would of course throw an exception or something instead of setting default dimensions. I was just doing this so one can see that something's happening in case of errors.
  • Thorsten Dittmar
    Thorsten Dittmar about 12 years
    You removed the comment asking on how to call with default values - my above comment was in reply to that, so it looks a bit dumb now. Anyway, it answers the additional question what you could do to be able to call picture/ with default values or passing the desired dimensions :-)
  • G Gr
    G Gr about 12 years
    Yeah sorry Im use to the below method picture?w={width}&h={height} with yours its so much simpler what I ment was navigating the url to the picture is simply picture/300/250 soo much better, I deleted it because I forgot the change in the uri template method.
  • Opsimath
    Opsimath about 12 years
    Also be careful your are consistent between the URL and the UriTemplate. Above the UriTemplate specifies two parameters named 'w' and 'h' but your URL provides parameters called 'width' and 'height'. The correct URL would be: localhost:8000/Service/picture?w=50&h=40