Testing a REST Logging Web Service using C# - c#

I've created a pretty basic REST Web Service in VS2012 using C#. The approach I've taken is seen below in one example:
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "OpenSession/{key}/{source_userid}/{source_id}/{source_version}/{source_ip}")]
string OpenSession(string key, string source_userid, string source_id, string source_version, string source_ip);
The code works just fine and the service is up and running. I get responses, the tokens are handed out, etc. I figured wrapping the data in the URL was just fine as the service is built for logging, so in the end, calls would be (for example):
www.service.site/service.svc/2/5/12/HELLO
(BTW if there's a better way to do this while keeping it a REST service please let me know)
The problem I have is... how do I test this properly? I need to simulate 1,000 concurrent hits to this service and have built a custom C# Windows Application to do this, but I'm encountering a number of problems.
First, I have no idea how to use HttpWebRequest and the stream readers/writers to send/receive data properly in a manageable format. I see The requests have GET and PUT methods, etc. but I honestly can't seem to make heads or tails of separating the post data vs. the URL information. AFAIK I'm not really posting any data as all my data is embedded in the URL request itself; is this correct?
Also, I have no idea what "content type" to set the HttpWebRequest to in order to be compatible with my Operation Contract (I can see the enumeration of JSON but what's the "text" supposed to be?)
And on top of that, HttpWebRequest is monstrously slow. When I open a URL directly to the service (e.g. "http://localhost:51849/Scribe.svc"), it's fast, but the second I try to call a method (e.g. ""http://localhost:51849/Scribe.svc/GetStatus"), I get a 1.5-2 second delay (and I've done all the proxy fixes to ensure it's as fast as possible).
Any help would be greatly appreciated.

I don't know if you are aware of this utility:
SoapUI
You can use it to reference your service and generate the requests to test your service.
The installation is pretty straightforward. After installed, you have to click on file/new SoapUI Project.
Then, in the popup window, you fill in the "Initial WSDL/WADL:" field with your service's wsdl, like this:
http://localhost:51849/Scribe.svc?wsdl
It'll generate the client to correctly do the calls to your service.
Using soap ui you can even test doing a bunch of calls to stress up your service.
This is a good way to test your services.
AFAIK I'm not really posting any data as all my data is embedded in the URL request itself; is this correct?
Yes. That's how a GET request works. The data is passed on your url. You should only use GET requests when not changing the state of the data on the server, otherwise you should use post.
PUT methods you'll use when you want to create/update the data on the server.
I hope it helps.
Regards,
Rodrigo

Maybe you should consider using some performance tool.
There are plenty and them, should be easy to find one suitable for your pourpose.
Jmeter could be a good option.
This is a guide that show how to use Jmeter to test a REST Web Service.
Hope it can help.

Thanks for the responses! I actually have the testing system working now and can slam my service with thousands of timer-controlled sessions. The key to getting around the PUT request issue was to use empty settings:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "PUT";
req.ContentType = "application/json";
req.ContentLength = 0;
I did look at those tools, but for my specific needs, I found writing a custom timer-controlled app was the best approach.
I'm not sure if the delay is still problematic, or possibly even linked to the debug state. once I publish the service to Azure and test it remotely, I'll get a better idea if there's any latency issues with connectivity which are service based...

Related

C# calling a webservice result in 401

I'm trying to call a simple web service to get long and lat of an address, which works when I try it manually :
https://services.gisgraphy.com/geocoding/?address=paris
But with the code, I get 401 unauthorize.. What am I doing wrong ?
WebRequest request = WebRequest.Create("https://services.gisgraphy.com/geocoding/?address=paris");
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "GET";
WebResponse response = request.GetResponse(); // It happens here.
What am I doing wrong?
You haven't read the big blue bar on top of the website belonging to the API you're calling:
Gisgraphy is open sources and only use open data. This server is for demonstration only. Test it, Play with webservices but then Install Gisgraphy locally or subscribe to premium hosted services
So they're probably detecting that you're calling their API from outside their playground, and are denying you to do so.
So, either install it locally, or subscribe to their hosted services. The latter probably gives you an API key that allows you to make API calls.
Of course you can fake your way around this by imitating that your request comes from a browser, for example with User-agent and Accept headers, but surely they'll try and detect this and block your IP address entirely. Just pay up, or host it locally.
Few things on this question :
Check if the URL/web-service is directly accessible via
browser/postman tool.
Secondly , as one of my fellow friend there mentioned this
web-service has some security protocol. May be its not allowing
requests from outside (i.e. other than its domain to come in)
Once you drill down the above two possibility ,you should be in a good shape to move ahead.

Send login credentials from C# client to Yii SOAP server

So, I have SOAP handling classes from https://soapclient.codeplex.com/SourceControl/latest. Using provided methods, I managed to connect to server and retrieve data, like:
SoapClient client = new SoapClient("http://somelink.someserver.net/~johndoe/gogogo/servis");
XElement myEle = client.Invoke("getProjekti");
What I need to do next is to provide HTTP authentication i.e. to send login credentials to web server. There is a way of adding Username and Password to a provided SoapHeader:
client.Header = new SoapHeader();
client.Header.Name = "AuthHeader";
client.Header.Add("UserName", "student");
client.Header.Add("PassWord", "student");
And there's the roadblock for me. What to do next? Common sense and programming experience make me think that there should be, hypothetically, request method that will now somehow send that header to web server, like:
bool sendLoginRequestToServer(client)
or something, returning true if login is successful or false otherwise. Despite vigorous search I was only left puzzled, since many people use many methods, most of which I failed to understand. What makes it even more difficult for me is that most of tutorials cover C# WebService, while I have ordinary WinForms GUI application. Solution within aforementioned SoapClient would be preferable, but I would settle for anything that works.

Adding service reference for Asp.Net webservice

EDIT:
I'm based on some comments that have been deleted, I guess I need to add a service reference and create a instance of the webservice object in the client application to pass the dataset. I haven't been able to add the service reference. When the service runs, it gives a 403 forbidden error, and has done so since I first created the project. I thought this was fine because the apicontroller methods can still be accessed with an HTTP GET, but I have no way of actually sending objects to the service, and can't create the service reference. The service and the client are in separate projects. At this point even a link to a decent tutorial would help. I've found a ton of stuff for creating a full site with a few functions accessed in jquery, and plenty of stuff that includes how to send a post in C#, but nothing that combines both sides.
I saw this question asking a how to do a similar thing. Is there any difference in the client side code I have in c# and the javascript in the question?
EDIT2:
I tried using a memory stream to look at the request content to see if the entire dataset was being transferred. Its only picking up 4000 or so bytes out of 350000 being transferred. I assume this is why the dataset isn't availalbe in teh webservice parameter. but I'm not sure how to continue.
Original:
I'm building a webservice to handle the database calls for my client application. This isn't publicly facing, so I'm fine with passing a dataset instead of a custom model. Heres the client code:
public static void TestSendToDatabase(DataSet Products)
{
string xml = ToXML(Products);
byte[] bytes;
bytes = System.Text.Encoding.ASCII.GetBytes(xml);
StringBuilder urlBuilder = new StringBuilder(string.Format("{0}{1}", BaseUrl, "WalmartProducts/Update"));
string url = urlBuilder.ToString();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/xml";
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = bytes.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(bytes,0,bytes.Length);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
And the service code:
public class WalmartProductsController : ApiController
{
[HttpPost]
[ActionName("Update")]
public object UpdateWalmartProducts([FromBody] byte[] bytes)
{
string xml = System.Text.Encoding.ASCII.GetString(bytes);
DataSet productsFromClient = LoadFromXMLString(xml);
DataUpdater.UpdateWalmartData(productsFromClient.Tables[0]);
return DataUpdater.GetProducts();
}
The byte array is empty in the web service. I'm sure that I'm mixing methods that shouldn't be mixed, but I've gone through at least thirty articles trying to resolve this, and it feels like each one has a completely different method. The webservice is currently returning the dataset in an xml format, so I would prefer to stick with xml over json.
This is not a complete answer, but perhaps it is a starting point. .NET supports two main techniques for writing Web Services. The first is an older technique using .asmx files. This approach is generally called the SOAP approach, and Visual Studio generates signatures of your methods in a .wsdl file. Because the signatures are exposed, you can then ask Visual Studio to create a Web Reference file for you. This web reference exposes all the Web Services as simple classes and methods, hiding all the complexity of the communication across the web service. Any object that can be serialized, including the DataSet object, can be included as a parameter to these web methods, and .NET will take care of serializing and deserializing these objects as they pass across the HTTP boundary. I believe that MS then encapsulated a number of different communication technologies inside of a product they called WCF, which was supposed to allow you to use configuration to choose whether you wanted to use HTTP for communication or TCP/IP.
The downside to this approach is that if you use any MS class in your API's, you have effectively tied your product to MS, and it is then very difficult for any non-MS client to communicate with your web service. The DataSet object is an extremely complicated one, and I'm under the opinion that it would be quite impractical to attempt to write your own serialization/deserialization methods to send a DataSet.
The other Web Service approach that is gaining popularity is using REST API's. Recently, MS added support for these, and the ApiController is one of the starting points. If you google "ASP.NET REST API Tutorial" you should find all kinds of examples. However, with this approach I would discourage you from using the DataSet class. I've had some success with classes such as a Dictionary, provided the object classes you put into the dictionary all resolve down to fairly simple components. Attempting to use a complex MS class like DataSet is likely to lead to a lot of frustration, and will yield a Web Service that very few clients can use.

Consuming a web service using POST instead of the going the usual WSDL route

This is how I have currently managed to consume a particular Microsoft web service. Notice that it is located on an HTTPS server and that it requires a username, a password, and a .cer file to be installed in the operating system's "root certificate authorities".
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.NegotiateServiceCredential = true;
binding.Security.Message.AlgorithmSuite
= System.ServiceModel.Security.SecurityAlgorithmSuite.Default;
binding.Security.Message.EstablishSecurityContext = true;
EndpointAddress endpoint = new EndpointAddress("https://address.of.service");
//"GreatClient" was created for me automatically by running
//"svcutil.exe https://address.of.service?wsdl"
GreatClient client = new GreatClient(binding, endpoint);
//Username and password for the authentication. Notice that I have also installed
//the required .cer certificate into the system's "root certificate authorities".
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
//Now I can start using the client as I wish.
My question is this: How can I obtain all the information necessary so that I can consume the web service with a direct POST to https://address.of.service, and how do I actually perform the POST with C#? I only want to use POST, where I can supply raw XML data using POST directly to https://address.of.service and get back the result as raw XML data. The question is, what is that raw XML data and how exactly should I send it using POST?
(The purpose of this question: The reason I ask is that I wish to consume this service using something other than C# and .NET (such as Ruby, or Cocoa on Mac OS X). I have no way of knowing how on earth to do that, since I don't have any easy-to-use "svcutil.exe" on other platforms to generate the required code for me. This is why I figured that just being able to consume the service using regular POST would allow me to more easily to consume the service on other platforms.)
What you are attempting to do sounds painful to do now and painful to maintain going forwards if anything changes in the server. It's really re-inventing the wheel.
If you haven't considered it already, I would:
(a) Research whether you can use the metadata you have for the service and use a proxy generator native to your target plaform. There aren't many platforms that don't have at least some tooling that might get you part of the way if not all of it. Perhaps repost a question targetting Ruby folk asking what frameworks exist to consume an HTTPS service given it's WSDL?
(b) Failing that, if your scenario allows it I would consider using a proxy written in C# that acts as a facade for the service which translates it into something easier to consume (for example, you might use something like ASP.NET MVC WebAPI which is flexible and can easily serve up standards compliant responses over which you can maintain total control).
I suspect one of these may prove easier and more valuable than the road you are on at the moment.
I had to go through something similar when porting .NET WCF code to other platforms. The easiest approach I found was to enable message logging on the WCF client. This can be configured to save both envelope and body and once everything is working on the .NET side of the house, you can use the message log to have "known-good" XML request/response to port to other platforms.
I found this approach to be more elegant since I didn't have to add an additional behavior to log messages, and it can be easily enabled/disabled/tweaked in the config. The Service Trace Viewer Tool that ships with Visual Studio is also handy for reviewing the log files.
I think when you say that the service should be consumed from other platforms, which do not have proxy class generation logic, you can go with REST services. This will allow you to create input as simple string concatenation instead of complex XML. Though its applicability depends on the situation.
Check this discussion : http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/6907d765-7d4c-48e8-9e29-3ac5b4b9c405/
As far as the certificate is concerned, refer http://msdn.microsoft.com/en-us/library/ms733791.aspx on how to configure it.
I know this is not a very precise answer, but you will be the best person to evaluate above procedure, hence posted. Hope it helps.
What I'll do:
1- Create a small c# app that can post on this webservice (using svcutil). And modify it to show the XML send/received. To view the xml there are several ways: logging, wireshark etc. To add it directly to the small app there is another question here that give a good answer.
2- Once you know what you have to send, you can do it in c# like this:
// implement GetXmlString() to return the XML to post
string xml = GetXmlString();
// create the url
string url = new UriBuilder("http","address.of.service",80).ToString();
// create a client object
using(System.Net.WebClient client = new System.Net.WebClient()) {
// performs an HTTP POST
client.UploadString(url, xml);
}
I'm not a .NET programmer but I've had to interoperate with a few .NET services and have lots of SOAP/WSDL experience. Sounds like you've captured the XML for your service. The other problem you'll face is authentication. OOTB, .NET web services use NTLM for authentication. Open-source language support for NTLMv2 can be hit and miss (although a quick google search pulled up a few possibilities for ruby), and using NTLM auth over HTTP may be something that you have to wire together yourself. To answer a question above: where are the auth creds? If the service is using NTLM over the wire, authentication is happening at some layer below HTTP. If the service is using NTLM to authenticate HTTP, your NTLM creds are in the HTTP Authorization header. You should be able to tell with wireshark where they are. You'll also probably need a SOAPAction header; this can also be sniffed with wireshark. For the C# client, I'm sure there are docs explaining how to add headers to your request.

Both WebClient and HttpWebRequest suddenly failing to pull basic Amazon pages?

We have an in-house application that pulls some data from some of Amazon's pages occassionally (We know they have APIs for certain operations... what we're doing requires some custom info not included in the APIs). We have never had a problem pulling their pages, but suddenly Amazon is returning "(503) Server Unavailable" on pretty much every request, and this has been happening for several days, so we doubt it's a temporary thing. Even something as simple as this:
System.Net.WebClient client = new System.Net.WebClient();
string data = client.DownloadString(new Uri("http://www.amazon.com/Bose-Companion-multimedia-speaker-Graphite/dp/B000HZBR64/"));
The strange thing is that these pages load just fine in a web browser, but any time we try to pull them through code, it is failing.
What could cause these functions to fail? Is it possible that they changed something on their end and that we need to do some custom logic with our calls?
After some further testing, it turns out that this was happening because Amazon needs the Accept parameter of the HttpWebRequest to be specifically set. When setting it to:
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
Everything worked fine. This is a recent change, so they must have altered something on their end.
check the user-agent of you request. make the user-agent the same as your browser. And check if you set any proxy for your app? maybe your browser and your app are using different proxies

Categories

Resources