Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

Really easy C#/WEb Service questions

I've never used C# nor have I created Web Services, so I am in the process of finding tutorials on the web and going through them.  I found this one over at code project:

http://www.thecodeproject.com/cs/webservices/myservice.asp

I am presented with this method:

    [WebMethod]
    public ClientData[] GetClientData(int Number)
    {
        ClientData [] Clients = null;

        if (Number > 0 && Number <= 10)
        {
            Clients = new ClientData[Number];
            for (int i = 0; i < Number; i++)
            {
                Clients[i].Name = "Client " + i.ToString();
                Clients[i].ID = i;
            }
        }
        return Clients;
    }

Anyhow, on the web page for GetClientData, I decide not to type in a value for the Number field and I click the 'Invoke' button.  I receive this:

System.ArgumentException: Cannot convert  to System.Int32.
Parameter name: type ---> System.FormatException: Input string was not in a correct format.
  at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
  at System.String.System.IConvertible.ToInt32(IFormatProvider provider)
  at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
  at System.Web.Services.Protocols.ScalarFormatter.FromString(String value, Type type)
  --- End of inner exception stack trace ---
  at System.Web.Services.Protocols.ScalarFormatter.FromString(String value, Type type)
  at System.Web.Services.Protocols.ValueCollectionParameterReader.Read(NameValueCollection collection)
  at System.Web.Services.Protocols.HtmlFormParameterReader.Read(HttpRequest request)
  at System.Web.Services.Protocols.HttpServerProtocol.ReadParameters()
  at System.Web.Services.Protocols.WebServiceHandler.Invoke()
  at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()

Question #1:
What can I use to determine if a user has passed a parameter correctly?  Is it null - since I didn't put in a value?  But how would you compare an int to null!?

Question #2:
Is it possible to have default parameters (so it would default to a certain value)?

Question #3:
How do I DEBUG a Web Service?  I set a breakpoint so I could try to figure out what the Number value was - I pressed F5, it launched a browser, I clicked the Invoke button - it didn't reach my breakpoint.

Yep.  As you can see, I am really brand-y-newbie here.

William Campbell
Monday, September 27, 2004

Answer #1: In this example, you can't. If your method signature includes an int parameter, the caller *must* pass a value and it must be convertable to an int. If the caller fails to do this, you can't catch it and they'll get the same error you did. If you want the ability to validate the parameter yourself, change it to a string in the method signature, check if it's empty (Number == "") (throwing your own error if it is), and cast it to an int yourself if it's not empty.

Answer #2: No, you can't have defaults *unless*, again, you change the signature from an int to a string. As I said in Answer #1, if you change it to a string you get to validate it yourself. If no parameter value is supplied by the caller, you can of course default to any value you like.

Answer #3: You didn't reach your breakpoint because your webservice code was never invoked. The HTTP POST call sent to the webserver by the test page gets parsed behind the scenes and attempts to convert the parameters supplied to their destination types (the types defined in your method's signature). If you leave the value of an int parameter blank, it gets POSTed as an empty string, which cannot be cast to an int and throws the error you saw.

Good luck!

Ryan LaNeve
Monday, September 27, 2004

The way to get a default à la C++ is to define an overloaded operator with fewer parameters. In C++ you might write:

public ClientData[] GetClientData(int Number = 42)
{
    ....
}

whereas to do this in C# you would write:

public ClientData[] GetClientData()
{
    return GetClientData(42);
}

public ClientData[] GetClientData(int Number)
{
    ....
}

Christopher Wells
Monday, September 27, 2004

Ryan thanks a lot for your response (and the other guy too - Chris, I think? - can't remember I'm in the middle of posting a message):

>>>>>>>>>>
Answer #1: If your method signature includes an int parameter, the caller *must* pass a value and it must be convertable to an int.
>>>>>>>>>>

Hmm.  I suppose that's true, I was just hoping for a way to enforce this - or at least not let it blow up like it does (handle it gracefully).  I guess that in a normal program - or what I'm used to writing - the application wouldn't compile if the parameter's aren't set up correctly.  But because this is some web service that is getting a GET or POST sent to it ... there isn't anything that would cause the client application to not compile.

>>>>>>>>>>>
change it to a string in the method signature, check if it's empty (Number == "") (throwing your own error if it is), and cast it to an int yourself if it's not empty.
>>>>>>>>>>>

How often is something like this done?  Or are Web Service writers resigned to the fact that if the client app doesn't get the parameters right, their app will blow up.  As someone that has barely paid attention to a lot of the security issues with the web (various hacks, buffer overruns, etc).  The fact that this web service "blows up" - or throws the exception - isn't a concern security wise?  Maybe that's a dumb question -- I don't know.

>>>>>>>>>>>>>>>>>>>
Answer #3: You didn't reach your breakpoint
>>>>>>>>>>>>>>>>>>>

Ohhhhh.  Duh.  I'm a dummy.  For some reason I thought it was blowing up in the first reference to that parameter in the function (the if statement where it was checking to see if it was between 0 and 10).  What was I thinking - good explanation, that made sense.

William Campbell
Tuesday, September 28, 2004

On #1 -- it's late bound.

If you have, say, a COM object which takes an int, and put it into VB, and pass it the wrong type, it will also fail before it hits your code.

You catch the exception in the *calling* code.

mb
Tuesday, September 28, 2004

>>>>>>>>>>>
On #1 -- it's late bound.

If you have, say, a COM object which takes an int, and put it into VB, and pass it the wrong type, it will also fail before it hits your code.

You catch the exception in the *calling* code
>>>>>>>>>>>

Hmmm.  I think I need to see one of these Web Services in action.  I was under the impression that it would be a URL string that would be POSTed (or GET) to the server that hosts the Web Service.  Therefore, the client application would be forming a string that they would then send to the server using the language's Internet API calls.

Therefore, there would be nothing that would tell them they sent the wrong parameters.  It's just a URL string.

Now, if I'm sending a URL string, I'm expecting some type of HTTP code in response (200) and I'm expecting a certain type of result - in XML (that seems to be the response of a web service).

I'm wondering what type of HTTP code would be returned.  Never seeing this in operation, I'm curious.  I also wonder how the Internet API would handle that exception - and if I could reasonably get the exact error message from the exception.

William Campbell
Wednesday, September 29, 2004

SOAP over HTTP return exceptions over HTTP. as XML, i believe.

Try this method (psuedocode)

void TestMethod(void)
{
throw (new Exception("test exception"))
}

and watch the HTTP request/response.

yes, the data sent to the POST is a string, but it's a serialization of typed data which gets de-serialized on the other end.

mb
Wednesday, September 29, 2004

Another thought.

Make your own proxy.  Create an appropriate class that can be called on the local machine.  Have a member of that class be your function.  Inside that function, call the webservice.  That way, you'll get the compile-time type checking and still be using the webservice to get the work done.

Scotty
Monday, October 04, 2004

William,

Although one of the really good things about soap being xml-based is that you can create strings and POST them at at a server, many times this is not done.

Instead there's this whole other thing called WSDL (web service description language) which web service authors can use to um - describe - exactly how they need to be called. Among other things, the WSDL can be used by client-side frameworks to generate strongly-typed functions at design-time.

At runtime, when you call these functions, they will internally create the strings and do the posting for you. Thus, assuming you trust the framework, it becomes a compile-time error to pass the wrong type of data to the webservice and it is impossible to get this ugly runtime error.

ASP.Net creates this WSDL for you, and you can check it out by hitting F5 (like you did) and adding "?wsdl" to the URL.

On the client-side, Visual Studio.Net knows how to consume the WSDL to generate the strongly-typed methods I mentioned earlier. Create a new command line project in VS.Net and do Project > Add Web Reference.... Put in the URL from above (w/o the ?wsdl).

===============

Of course, there's no reason why you have to use the WSDL. You can still post regular old strings at the webservice all day long.

But it's your responsibility as the client-side developer to make sure that you do all validation ahead of time and only send data which you know will be valid. Otherwise .NET will complain. Loudly.

Aaron
Tuesday, October 05, 2004

*  Recent Topics

*  Fog Creek Home