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.
Related
In the ASP .NET website they have an example of how to call a Web API from a .NET Client.
They have this method to make a call which creates a new Product:
static async Task<Uri> CreateProductAsync(Product product)
{
HttpResponseMessage response = await client.PostAsJsonAsync("api/products", product);
response.EnsureSuccessStatusCode();
// Return the URI of the created resource.
return response.Headers.Location;
}
This is like the usual way to make any call to any API in any language or framework, but I see a lot of problems in this line:
HttpResponseMessage response = await client.PostAsJsonAsync("api/products", product);
If the "path" parameter is a string, there is no way to detect possible errors in compilation time. Besides, if the API changes and we have a lot of calls, there is no way to rebuild the calls quickly.
Is there a better way to build the call to avoid this kind of problems? I mean, like we do with Web Services, for instance.
Like Nkosi mentioned, that is how it is designed. However you could create a class with the API URI's and use those instead of literals
public class ApiUris
{
public const string Products = "api/products";
public const string Services = "api/services";
public const string Orders = "api/orders";
/* ... */
}
That way you can easily change the path if your webapi changes.
Even Web Service and WCF service does not gurantee build fail if the server changes the URL or type and/or structure of the parameter. You come to know about the changes only when you call the service and it breaks.
In case of calling Web APIs surely there is no way you can figure out that the URL or the paramemeter is incorrect until you call the API.
At the same time, Web API are not built to be changed frequently. If they are not reliable no one would use them. So if you are using an API you can be sure that they will not change their URL or stop supporting certain input parameters overnight making their consumer applications like yours break. They surely woudn't want to be sued by their customers.
Any API would change gradually and with well advanced notice to their customers so that they get time to change their implementation to support new version of API.
So you can rely on the service which you are using and puting values related to it (such as URLs) in the config file would be good enough. Or you can choose to put API Urls in database so that when it changes you just need to update entry in the database and changes will be effective immediately.
Just extending upon Adam's solution you can get a List of your routes using
System.Web.Routing.RouteTable.Routes.ToList();
Then getting the path depends on the concrete implementation of the RouteBase abstract class.
I've created a very simple REST microservice that receives information about an email and sends it. The microservice send method looks something like this:
//EmailController
[HttpPost]
public IHttpActionResult Send(Email email)
{
// send email via exchange
}
Now in my application, I call it using RestSharp like this:
var client = new RestClient("http://localhost:51467/api/");
var request = new RestRequest("email/send", Method.POST);
request.RequestFormat = DataFormat.Json;
dynamic obj = new ExpandoObject();
obj.FromAddress = from;
obj.ToAddress = to;
obj.Subject = subject;
obj.Body = body;
request.AddBody(obj);
client.Execute(request);
Questions I have:
Is this the best way to do the call? Obviously i'll later have to add error handling etc, but I'm talking more the way I'm using RestSharp to do the call.
I'm finding it a bit uncomfortable that my app needs to kind of know what object the microservice expects to receive - there's no sort of definition/interface/contract that it uses to know for sure. Is this generally accepted as being ok for REST or should I implement some sort of interface that my app has so it can call my microservice in a bit more of a defined way. Is that even something possible with REST?
Thanks for any help!
REST services do not have a schema or WSDL type of function to define the format of the service. This is what makes them more light weight compared to traditional web services.
There is something called WADL, or Web Application Description Language, but this is not really a standard, and isn't widely supported. It's also quite controversial as there are many that feel it's not needed.
http://en.wikipedia.org/wiki/Web_Application_Description_Language
Also see this discussion on Programmers
https://softwareengineering.stackexchange.com/a/133693/4368
I know this is super old but I couldnt help but answer for the sake of currency.
There are two ways of communicating an API contract with other .net services which I find particularly useful.
Ship a nuget package with the contracts (Interfaces describing responses) and possibly some call logic to methodise your api calls
Use swagger to describe your api (seems to have come out as the API description winner, Swashbuckle makes it seamless in .net) and then either hand code the bits you need at the caller or use a codegen
I quite often do both, swagger is also nice for documentation and other language compatibility, and its good practice to be formal about your contracts and backward compatibility.
I would use ASP.NET Web API client libraries. It works with any REST API, whether its coded using .NET or some other framework.
Look here for details: http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Nuget package: Microsoft.AspNet.WebApi.Client
I typically don't bother with extra client Libraries like RestSharp. I feel that the purpose of REST is to stay as close to gold old HTTP as possible, negating the need for anything other than HttpWebRequest/Response. Working with the request/responses directly gives great control and encourages you to think about what's actually going on instead of abstracting everything away like you would with a traditional WCF or ASMX service.
For microservices I've built in the past I've kept the request and response objects within separate libraries and I've the distributed the source to other Developers within my organisation to give them a leg up in calling the service but it probably wouldn't be practical for external consumers; again I guess the point of going for a microservice over a full scale WCF service is that by their nature the request/responses being passed around are small and simple. I also felt a little uncomfortable with this practice at the start; however when I started getting really responsive web apps calling microservices with javascript (usually jquery) just as easily as traditional .NET ones I started seeing the potential for some really good integration of our internal systems. Eventually our intranets were providing actions and views into business applications that weren't possible previously.
HttpWebRequest webRequest = WebRequest.Create("http://localhost:51467/api/email/send") as HttpWebRequest;
webRequest.Method = "POST";
webRequest.Credentials = CredentialCache.DefaultCredentials; //or account you wish to connect as
webRequest.PreAuthenticate = true;
webRequest.ContentType = "application/json"; // or xml if it's your preference
string jsonData = Newtonsoft.Json.JsonConvert.SerializeObject(requestObject);
using (StreamWriter streamWriter = new StreamWriter(webRequest.GetRequestStream()))
{
streamWriter.Write(jsonData);
streamWriter.Flush();
streamWriter.Close();
}
HttpWebResponse webResponse = webRequest.GetResponse() as HttpWebResponse;
if (webResponse.StatusCode != HttpStatusCode.Accepted)
throw new ApplicationException("Unexpected Response Code. - " + webResponse.StatusCode);
string response;
using (System.IO.StreamReader readResponse = new System.IO.StreamReader(webResponse.GetResponseStream()))
{
response = readResponse.ReadToEnd();
}
//swap out for regular xml serializer if you've used xml
dynamic responseObject = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(response);
Also another tip, if you're working with web api, I'd really suggest adding in the web api help pages and test client. You won't have the automatically generated wsdl you get with WCF and ASMX but you can get some really nice documentation about your microservice for other developers (even better in my opinion that auto generated proxy classes) and a test harness that lets to exercise the service from your browser
https://github.com/wuchang/WebApiTestClient
https://www.nuget.org/packages/Microsoft.AspNet.WebApi.HelpPage/
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...
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.
I have a WCF service hosted that is returning a byte[] of protobuf-net serialized data. Originally this was hosted over the default webHttpBinding settings (SOAP) and everything was working properly. I recently found out that I need to call HTTP GETs and POSTs directly from our client so I thought it'd be easier to switch to use a RESTful service. I switched to use the WebGet attribute and the WCF REST Template.
I attempted to create a simple web site to test the client and I'm having trouble deserializing the data. This is an example of how I'm calling the service:
using (WebClient client = new WebClient())
{
result = client.DownloadString(url);
}
// Deserialize
BinaryVehicles binVehs;
using (var ms = new MemoryStream(StrToByteArray(result)))
{
binVehs = Serializer.Deserialize<BinaryVehicles>(ms);
}
An example of what is returned in "result":
< base64Binary xmlns="http://schemas.microsoft.com/2003/10/Serialization/">ChsKCzEyMy00NTYtNzg5EgU0NDAwMBoFQmxhY2sKHAoLOTYzLTg1Mi03NDESBTIzMDAwGgZTaWx2ZXI=< /base64Binary>
I also attempted to deserialize the data between the < base64Binary > tags with no results. Does anyone have any idea on how I should go about sending the binary protobuf-net data from an WebGet method and how I should deserialize the data? Thanks.
protobuf-net primarily handles just the serialization aspects (the protobuf spec by Google doesn't define any further than this). So it really comes down to: how are you serializing it?
I must admit that the WCF GET approach is not something I've looked at hugely, so there is no special handling there. One simple approach may be to look at just returning a string, and handling the base-64 encoding yourself, but to be honest if you are doing HTTP GET, then WCF itself seems overkill.
I blogged here about using ASP.NET MVC at the server for protobuf via HTTP GET. The linked sample code also includes a wire-compatible ASP.NET implementation.
If there is something appropriate we can do to make WCF GET easier, I'm all ears...