I am currently trying to write a portable class library in c# that will allow me to send SOAP POST requests to a web service.
Because you are limited in what you can reference when making a PCL, I cannot simply "Add a service reference." Instead, I am trying to manually make the SOAP request and I am getting stuck on adding headers to the HttpWebRequest.
My code is as follows:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("website_here");
byte[] xmlStr = getPullCustInfoXml(postParameters);
request.Method = "POST";
request.Headers.Add() <- trying to add headers here.
I can't seem to add any headers because I get the error:
'System.Net.Http.WebHeaderCollectionExtensions.Add(System.Net.WebHeaderCollection, string, string)' is inaccessible due to its protection level
Why would it be inaccessible even after I casted the request as an HttpWebRequest?
Is there a better way to implement SOAP requests when making a portable class library?
I don't get the same error message when I copy and paste your code.
Make sure HttpWebRequest is from System.Net namespace though. The documentation says Headers.Add methods are all public.
Also, try different overload methods. There are:
Add(NameValueCollection)
Add(String)
Add(HttpRequestHeader, String)
Add(HttpResponseHeader, String)
Add(String, String)
More info: http://msdn.microsoft.com/en-us/library/system.net.webheadercollection(v=vs.110).aspx
Related
I am trying to add Digital Security headers to some api calls that leverage a third-party sdk. One of the required security headers is a hash of the entire POST body being sent. The sdk we are leveraging calls a SOAP endpoint and the construction of the soap envelope is very far along in the request process. The sdk offered some obvious BeforeRequest extension points but they are executed before the SoapEnvelope is constructed.
There are a couple ways I have attempted to solve this:
I was trying to take a serializable object and recreate the request with the soap envelope but doing so did not yield a matching string as there were namespaces that were applied when they shouldnt, encoding on innner objects serialization etc (I am sure I am doing things wrong but these struggles got me looking elsewhere)
Next I found the SoapExtension class and thought this would work for me. In my testing I am seeing that I get the full request body (with the SOAP envelope) exactly as it would be sent. But I do not think or cannot find a way to modify the http headers of this request from within the hooks in SoapExtension.
So the question I am asking here is focused on my second approach and wondering if this is possible. If not I will go back to my first attempt and see if I can figure out how to correctly generate an identical request body.
I need to call an external SOAP webservice over HTTP.
I have the WSDL file and added it in Visual Studio via 'Add service reference'. Visual studio then added a number of files, in the reference file i can find this:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="Service.IService")]
public interface IService {
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService/Function", ReplyAction="http://tempuri.org/IService/FunctionResponse")]
namespace.Service.ExecuteFunctionResponse ExecuteFunction(namespace.Service.FunctionRequest request);
}
With additionaly the async version of this call and the objects for sending an receiving, etc.
To call the service I added the folowing code:
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("the address");
serviceChannel = new ServiceClient(binding, endpointAddress).ChannelFactory.CreateChannel();
Response response = serviceChannel.ExecuteFunction(new Request(...));
This results in me getting an exception, error 405 method not allowed.
So it appears I must use a HTTP GET request instead of the default POST request. But i cannot find where this can be altered in with this way of working.
So, where can i set the HTTP method for this call to a webservice?
SOAP services are using HTTP POST because they exchange XML messages (which tend to be complex) and cannot be transferred in a query string.
Are you sure that you must use HTTP GET? Maybe the error you are receiving "405 method not allowed" is caused by some bad configuration.
I would double check the SOAP endpoint URL is set correctly and check that there are no additional security requirements needed.
EDIT
In past, there was a practice to create ASP.NET Web Services which would accept GET as well. But they wouldn't expect XML messages. Instead, you would have to pass all parameters in a querystring. For example: https://foo.bar/service.asmx/Func?param1=X¶m2=Y (where param1 and param2 are the expected parameters).
This way it is possible to call a WebService without a need for WSDL and using GET method. You can achieve it by using HttpClient for example.
The downside of this approach is that you will have to deal with plain data instead of objects.
I hope it might help.
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.
I am trying to dynamically modify XML data in SOAP requests to ASMX services.
I overrided GetWebRequest() method in SoapHttpClientProtocol class in order to read and modify XML data that the RequestStream contains.
The problem is, the request seems to be empty, there is no data in it whatsoever. Is this because the SOAP data hasn't yet been generated and serialized or am I doing something wrong?
What you need is a SoapExtension. You could hook into the SoapMessageStage.AfterSerialize stage in ProcessMessage to modify your soap message. I've done this in the past to add WSSE headers in situations where I couldn't add a dependency on Microsoft's WSE library (since it isn't available for Mono).
Complete tutorial here: http://msdn.microsoft.com/en-us/magazine/cc164007.aspx
GetWebRequest is too early for your purpose, GetWebResponse is too late.
I am trying to create a RESTful web service based on WCF Web API. I also need to control access using OAuth and for this I am using the DotNetOpenAuth open source library.
Has anyone ever been successful integrating the two? I am struggling converting from the HTTP entities representations of the WCF Web API into something that's understandable by DNOA (e.g. HTTP requests, HTTP headers, etc...).
Any tip would be greatly appreciated.
Could you be a little more specific?
In WebAPI a request is represented by the HttpRequestMessage class. A response is represented by the HttpResponseMessage class.
I've no previous knowledge of DNOA, but from what I saw, you can easily create a HttpRequestInfo from an HttpRequestMessage using the public HttpRequestInfo(string httpMethod, Uri requestUrl, string rawUrl, WebHeaderCollection headers, Stream inputStream).
The HTTP method and request uri are directly HttpRequestMessage properties.
The input stream is obtained via the Content property. I don't see a direct way of creating a WebHeaderCollection from the WebAPI's HttpRequestHeaders. However, you can iterate the HttpRequestHeaders entries and insert then on the WebHeaderCollection one by one.