I have a program that submits data to a third party web service. They are implementing two-factor authentication where I have to create a nonce key (using NewGuid), they provide an encoded API key to include, as well as a screen name, URL and time stamp.
I have the nonce being created, and then using the api key, I sign the data and in the end, I'll have a long string such as this:
2017-04-15T17:08:57Z-1265fb1e-bbc3-453a-be409e2a808cbaaeWpyp6EJnIUlSX1rB/YRJxRyp8cXxw2IIrFMnnvuB06cUBabyRLnD 5hPj+ndH8zSIhojvNgc10/az2N+hh6SaMA==
It states that it would be sent in an HTTP header called X-WME-API-Token.
We have a MeterDataService.cs file that was generated from the SVCUTIL command line tool that creates a proxy client. I don't see anywhere in there that I can add the customer header.
I know that you can do something like this if you have something derived from WebRequest:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(urlString);
req.ContentType = "text/xml";
req.Headers["X-SPP-API-Token"] = Token;
req.Method = "POST";
However, none of the resulting objects is derived from WebRequest, so I'm not sure how to do this.
One object, the MeterDataPortClient object, has an Endpoint property. Is that where I would do it? How do I do that? I guess that's my question.
I did try modifying the MeterDataRequest object they created by having it inherit from WebRequest, but then I got this error:
"The type PostMeterDataRequest defines a MessageContract but also derives from a type System.Net.WebRequest that does not define a MessageContract. All of the objects in the inheritance hierarchy of PostMeterDataRequest must defines a MessageContract."
Here's the the definition of the PostMeterDataRequest and I see the MessageContractAttribute being applied to the class. I'm just not familiar with what that means. I just need to find a way to add the HTTP header to the request object before it's sent to the web service since the service at the receiving end expects the token to be in the HTTP header and I have no idea how to put it in with the code that I have at hand.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(IsWrapped = false)]
public partial class PostMeterDataRequest : WebRequest // WebRequest added my me later on.
{
[System.ServiceModel.MessageBodyMemberAttribute(Namespace = "http://someorg.com/schema/MeterDataSchema/v2", Order = 0)]
public PostMeterDataType PostMeterData;
public PostMeterDataRequest()
{
}
public PostMeterDataRequest(PostMeterDataType PostMeterData)
{
this.PostMeterData = PostMeterData;
}
}
Finally found a solution. It was this earlier post that I never found before as much as I searched, but it worked.
How to add Custom HTTP Header for C# Web Service Client
Following the directions in the first answer worked beautifully.
Related
I am using the 2010 SharePoint Lists web service to return a Content Type and it's fields through a c# application. The Library is in a web whose language is set to welsh, but with an alternate language of english set. This means if the internet options in the browser are set to english, the library displays in English.
I have been able to set the Accepts-Language header for requests made using the client object but have not been able to do so for the web service.
Is it possible to see a header on requests made through the SharePoint Web Services, and if so, how?
In case of ASMX web services you could consider the following approach. SoapHttpClientProtocol Class contains GetWebRequest Method that could be used for specifying a custom headers.
Once the proxy class is generated, create a class that derives from it and set custom header as shown below:
public class ListsEx : Lists
{
protected override WebRequest GetWebRequest(Uri uri)
{
var request = base.GetWebRequest(uri);
//Add the Accept-Language header (for Danish) in the request.
request.Headers.Add("Accept-Language:da");
return request;
}
}
where Lists is the name of generated proxy class.
Usage
using (var client = new ListsEx())
{
client.Url = webUri + "/_vti_bin/Lists.asmx";
var reult = client.GetList("Pages");
//...
}
Result
I don't know what type of web services you use. In case of WCF services, you can use: How to add a custom HTTP header to every WCF call?
In case of ASMX web services: Adding SOAP headers to ASMX service requests .
In both cases use Accept-Language header like "Accept-Language:en"
i was trying to get the C# version of the following java code snippet,
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestProperty("Range", "bytes=1024-");
this is what i have so far
WebRequest request = WebRequest.Create(someUri);
request.Headers.Add("Range", "bytes=1024-");
but it is not working,what is the right way for me go?
Presumably your URI is HTTP since Java's HttpURLConnection is designed for a HTTP connection. WebRequest is abstract and can handle multiple protocols. However, by specifiying a HttpWebRequest type, you can access HTTP-specific methods.
The Range header is protected and you should use AddRange to set the property instead of directly adding it to the Header collection.
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(someUri);
request.AddRange("bytes",1024);
You are setting two different things.
A request property is a value passed to the page.
A header property is a header in the HTTP request. Something like setting the HTTP REFERER (sic).
I've a simple question, for you, that I just can't seem to get my head around.
The situation is the following:
We create and send a request to a web service, using WSE 3.
The web service is written in Java.
Most of the things are fine, but I can't seem to have an impact on the ContentType of either the WebResuest or WebResponse and that's causing some problems.
The errormessage I keep getting is the following:
Client found a response content type of ' application/xop+xml;type="text/xml; charset=utf-8" ' but expected 'text/xml'.
The request failed with the error message: ....
In the details of the error message it has the response to our call from the server and it's coming through properly.
Obviously it's not good as it is at the moment as it's coming through an exception :).
So, how could I set the expected content type for the response?
If I'm correct, the Request and the Response in WSE 3.0 has to have the same ContentType. So I thought I would try to set the request.Headers[HttpRequestHeader.ContentType] to the expected one, but with no luck. (also, I can set the HttpWebRequest's contenttype in quite a few places, but none of them seem to do the trick)
This has been solved luckily, so here's the solution for future reference:
Our client inherits from the WebServicesClientProtocol class, in which there's a method called GetWebResponse(..).
Simply overriding that method and changing the ContentType of the Response seemed to work out for us.
protected override WebResponse GetWebResponse(System.Net.WebRequest request)
{
WebResponse response = base.GetWebResponse(request);
response.Headers[HttpResponseHeader.ContentType] = "text/xml";
return response;
}
I first posted this: HttpWebRequest: How to find a postal code at Canada Post through a WebRequest with x-www-form-enclosed?.
Following AnthonyWJones suggestions, I changed my code following his suggestions.
On a continuation of my inquiry, I have noticed with time that the content-type of Canada Post is more likely to be "application/xhtml+xml, text/xml, text/html; charset=utf-8".
My questions are:
How do we webrequest against such a content-type website?
Do we have to keep on going with the NameValueCollection object?
According to Scott Lance who generously provided me with precious information within my preceding question, the WebRequest shall return the type of information whatever the content-type may be, am I missing something here?
Do I have to change my code because of the content-type change?
Here is my code so that it might be easier to understand my progress.
internal class PostalServicesFactory {
/// <summary>
/// Initializes an instance of GI.BusinessSolutions.Services.PostalServices.Types.PostalServicesFactory class.
/// </summary>
internal PostalServicesFactory() {
}
/// <summary>
/// Finds a Canadian postal code for the provided Canadian address.
/// </summary>
/// <param name="address">The instance of GI.BusinessSolutions.Services.PostalServices.ICanadianCityAddress for which to find the postal code.</param>
/// <returns>The postal code found, otherwise null.</returns>
internal string FindPostalCode(ICanadianCityAddress address) {
if (address == null)
throw new InvalidOperationException("No valid address specified.");
using (ServicesWebClient swc = new ServicesWebClient()) {
var values = new System.Collections.Specialized.NameValueCollection();
values.Add("streetNumber", address.StreetNumber.ToString());
values.Add("numberSuffix", address.NumberSuffix);
values.Add("suite", address.Suite);
values.Add("streetName", address.StreetName);
values.Add("streetDirection", address.StreetDirection);
values.Add("city", address.City);
values.Add("province", address.Province);
byte[] resultData = swc.UploadValues(#"http://www.canadapost.ca/cpotools/apps/fpc/personal/findByCity", "POST", values);
return Encoding.UTF8.GetString(resultData);
}
}
private class ServicesWebClient : WebClient {
public ServicesWebClient()
: base() {
}
protected override WebRequest GetWebRequest(Uri address) {
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = new CookieContainer();
return request;
}
}
}
This code actually returns the HTML source code of the form one must fill with the required information in order to process with the postal code search. What I wish is to get the HTML source code or whatever it may be with the found postal code.
EDIT: Here's the WebException I get now: "Unable to send a content body with this type of verb." (This is a translation from the French exception "Impossible d'envoyer un corps de contenu avec ce type de verbe.")
Here's my code:
internal string FindPostalCode(string url, ICanadianAddress address) {
string htmlResult = null;
using (var swc = new ServiceWebClient()) {
var values = new System.Collections.Specialized.NameValueCollection();
values.Add("streetNumber", address.StreetNumber.ToString());
values.Add("numberSuffix", address.NumberSuffix);
values.Add("suite", address.Suite);
values.Add("streetName", address.StreetName);
values.Add("streetDirection", address.StreetDirection);
values.Add("city", address.City);
values.Add("province", address.Province);
swc.UploadValues(url, #"POST", values);
string redirectUrl = swc.ResponseHeaders.GetValues(#"Location")[0];
=> swc.UploadValues(redirectUrl, #"GET", values);
}
return htmlResult;
}
The line that causes the exception is pointed with "=>". It seems that I can't use GET as the method, yet this is what has been told me me to do...
Any idea what I'm missing here? I try to do what Justin (see answer) recommended me to do.
Thanks in advance for any help! :-)
As an introduction to the world of screen-scraping, you've picked a very hard case! Canada post's lookup page works like this:
the first page is a form which accepts the address values
this page POSTs to a second URL.
that second URL in turn redirects (using an HTTP 302 redirect) to a third URL which actually shows you the HTML response containing the postal code.
Making matters worse, the page in step #3 needs to know the cookie set in step #1. So you need to use the same CookieContainer for all three requests (although it may possibly be sufficient to send the same CookieContainer to #2 and #3 only).
Furthermore, you may need to send additional HTTP headers in these requests as well, like Accept. I suspect where you're running into problems is that HttpWebRequest by default handles redirect transparently for you-- but when it transparently redirects it may not add the right HTTP headers necessary to impersonate a browser.
The solution is to set the HttpWebRequest's AllowAutoRedirect property to false, and handle the redirect yourself. In other words, once the first request returns a redirection, you'll need to pull out the URL in the HttpWebResponse's Location: header. Then you'll need to create a new HttpWebRequest (this time a regular GET request, not a POST) for that URL. Remeber to send the same cookie! (the CookieContainer class makes this very easy)
You also may need to make an additional request (#1 in my list above) in order to set up the session cookie. If I were you, I'd assume that this is required, simply to eliminate it as a problem, and try removing that step later and see if your solution still works.
You'll want to download and use Fiddler (www.fiddlertool.com) to help you with all this. Fiddler allows you to watch the HTTP requests going over the wire, and allows you (via the request builder feature) allows you to create HTTP requests so you can see which headers are actually required.
Forgive me if this is a stupid question. I am not very experienced with Web programming.
I am implementing the payment component of my .net mvc application. The component interacts with an external payment service. The payment service accepts http post request in the following form
http://somepaymentservice.com/pay.do?MerchantID=xxx&Price=xxx&otherparameters
I know this is dead easy to do by adding a form in View. However, I do not want my views to deal with third party parameters. I would like my view to submit information to my controller, then controller generates the required url and then send out the request. Following is the pseudo code.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult PayForOrder(OrderForm order)
{
var url = _paymentService.GetUrlFromOrder(order);
SendPostRequest(url);
return View("FinishedPayment");
}
Is it possible to do so? Does c# have built-in library to generate http request?
Thanks in advance.
You'll want to use the HttpWebRequest class. Be sure to set the Method property to post - here's an example.
There certainly is a built in library to generate http requests. Below are two helpful functions that I quickly converted from VB.NET to C#. The first method performs a post the second performs a get. I hope you find them useful.
You'll want to make sure to import the System.Net namespace.
public static HttpWebResponse SendPostRequest(string data, string url)
{
//Data parameter Example
//string data = "name=" + value
HttpWebRequest httpRequest = HttpWebRequest.Create(url);
httpRequest.Method = "POST";
httpRequest.ContentType = "application/x-www-form-urlencoded";
httpRequest.ContentLength = data.Length;
var streamWriter = new StreamWriter(httpRequest.GetRequestStream());
streamWriter.Write(data);
streamWriter.Close();
return httpRequest.GetResponse();
}
public static HttpWebResponse SendGetRequest(string url)
{
HttpWebRequest httpRequest = HttpWebRequest.Create(url);
httpRequest.Method = "GET";
return httpRequest.GetResponse();
}
It realy makes a difference if ASP.NET makes a request or the the client makes a request.
If the documentation of the the provider says that you should use a form with the given action that has to be submited by the client browser then this might be necessary.
In lots of cases the user (the client) posts some values to the provider, enters some data at the providers site and then gets redirected to your site again. You can not do this applicationflow on the serverside.