How do I get the destination URL of a shortened URL? - c#

I have an API (https://www.readability.com/developers/api/parser#idm386426118064) to extract the contents of the webapges, but on passing a shortened url or an url that redirects to other, it gives error.
I am developing windows phone 8.1 (xaml) app. Is there any way to get the destination url in c# or any work around?
eg url - http://www.bing.com/r/2/BB7Q4J4?a=1&m=EN-IN

You could intercept the Location header value before the HttpClient follows it like this:
using (var handler = new HttpClientHandler())
{
handler.AllowAutoRedirect = false;
using (var client = new HttpClient(handler))
{
var response = await client.GetAsync("shortUrl");
var longUrl = response.Headers.Location.ToString();
}
}
This solution will always be the most efficient because it only issue one request.
It is possible however, that the short url will reference another short url and consequently cause this method to fail.
An alternative solution would be to allow the HttpClient to follow the Location header value and observe the destination:
using (var client = new HttpClient())
{
var response = client.GetAsync("shortUrl").Result;
var longUrl = response.RequestMessage.RequestUri;
}
This method is both terser and more reliable than the first.
The drawback is that this code will issue two requests instead of one.

You can get the ResponseUri from GetResponse():
string redirectedURL = WebRequest.Create("http://www.bing.com/r/2/BB7Q4J4?a=1&m=EN-IN")
.GetResponse()
.ResponseUri
.ToString();
Interesting article, by the way.

You need to inspect the headers returned from the URL.
If you get HTTP return codes 301 or 302, then you are being notified that the page is redirecting you to another URL.
See http://www.w3.org/Protocols/HTTP/HTRESP.html for more details about HTTP return codes.

Related

Using MultipartContent - It's working, but is it working right?

I've got a little app that generates an HttpMessage with Multipart content ...
using (var client = new HttpClient())
{
using (var content = new MultipartContent("mixed", "----123"))
{
content.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/mixed; boundary=----123");
// repeated calls to content.Add(...)
var result = client.PostAsync(url, content). Result;
Console.WriteLine(result);
}
}
and I have a little HttpServer that listens for POST calls and does this when it gets one...
var streamContent = new StreamContent(inputStream);
streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/mixed; boundary=----123");
var provider = streamContent.ReadAsMultipartAsync().Result;
foreach (var httpContent in provider.Contents)
{
var t = httpContent.Headers.ContentType;
var c = httpContent.ReadAsStringAsync().Result;
}
And it all works.
But if, in my receiver code, I do not include the line streamContent.Headers.ContentType... the receiver crashes on the var provider... line with the error Invalid 'HttpContent' instance provided. It does not have a content-type header value. 'HttpContent' instances must have a content-type header starting with 'multipart/'..
So, whilst I have code that works, it will only work if I know, in advance, what the boundary is going to be.
This can't be right.
I've looked through, and tried, dozens of permutations based on questions here in SO and elsewhere but I can't find anything that works without me setting the ContentType header in the receiver and, therefore, knowing what the boundary value is.
What should I be doing?
UPDATE
If I remove the boundary part of the ContentType header in the receiver, it still crashes, but with a different error...Invalid 'HttpContent' instance provided. It does not have a 'multipart' content-type header with a 'boundary' parameter.
I don't think your server is doing what you think it is doing. new StreamContent(Stream) is used when you create your own stream content with the intent to return it from controller action. And the stream you pass to it should contain the raw data (entity, response body) that will be returned. It doesn't try to interpret the data from the stream in any way. Even if you pass valid http stream in the parameter, it won't try to parse content-type headers from it - you have to supply it. And that's a big IF, you didn't show where you get the inputStream, it's not standard part of MVC.
Actual content you received from client is accessible in Request.Content, along with the proper headers like content-type or boundary. ReadAsMultipartAsync should work on that too, but I never tried that extension in practice.
As a side note, using Task.Result should be last resort. Make your controller action async and await that task.
Edit: for illustration, I think this would work and doesn't require knowing the boundary in advance. Still, it's very suboptimal solution when you can just call Request.Content.ReadAsMultipartAsync():
var streamContent = new StreamContent(inputStream);
streamContent.Headers.ContentType = Request.Content.Headers.ContentType;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
var provider = streamContent.ReadAsMultipartAsync().Result;
foreach (var httpContent in provider.Contents)
{
var t = httpContent.Headers.ContentType;
var c = httpContent.ReadAsStringAsync().Result;
}

How to execute two dependent odata url's in same session from C# Code

I need to call two odata URL's of sap from C# Code.
The first URL will set the Filters and the second URL (generic URL) will give the actual response.
First URL:
https://<>/sap/opu/odata/SALM/TM_SERVICE/setFilters?sap-language=EN&filter='2%2Ctime%3D%E2%80%8E4%E2%80%8E%2F%E2%80%8E1%E2%80%8E%2F%E2%80%8E2019%E2%80%8E%20%E2%80%8E10%E2%80%8E%3A%E2%80%8E31%E2%80%8E%3A%E2%80%8E24%E2%80%8E%20%E2%80%8EAMc%3ATest%20Suite%20for%20Focused%20Build%2Cw%3A%2CT%3D<>'
Second URL :
https://<>//sap/opu/odata/SALM/TM_SERVICE/TILECollection(ConfigID='1%20',PageID='2%20',TileID='8%20')?$format=json&sap-language=EN
Second URL is generic and constant URL but after first execution of first URL with different parameter values second URL will give different response.
In a browser I am able to execute these two URL's and see the response difference with different parameter values.
Need C# Code to simulate the browser behavior. In C# Code second URL response is constant and not being changed.
Tried using following : WebRequest,HttpWebRequest, HttpClient,WebClient along with passing cookie ( cookie received from First response is passed to Request while executing the second url )
var httpClientHandler = new HttpClientHandler()
{
Credentials = credential,
CookieContainer = cookieContainer
};
using (HttpClient client = new HttpClient(httpClientHandler))
{
using (HttpResponseMessage response = client.GetAsync("<<first url>>").Result)
{
Uri uri = new Uri(string.Format(ConfigurationManager.AppSettings["TestPlanSetFilters"], s));
IEnumerable<Cookie> responseCookies = cookieContainer.GetCookies(uri).Cast<Cookie>();
foreach (Cookie cookie in responseCookies)
{
cookieContainer.Add(cookie);
}
}
using (HttpResponseMessage response1 = client.GetAsync("<<second url>>").Result)
{
using (HttpContent content1 = response1.Content)
{
var json2 = content1.ReadAsStringAsync().Result;
}
}
}

Differences between using C# HttpClient API and the postman testing? Client call works on postman, but not C# httpClient getAsync

I am testing a REST API post, and it works well when I try it on Postman. However, in some scenario (related to the posting XML data) if I post with HttpClient API, I would receive the following error:
Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
But the same XML content works fine on Postman with status OK and proper response.
What is the differences between using the C# HttpClient API and the postman testing? How can I configure my API call to match with the behavior on postman?
Here I attached the source code, and the Postman screenshot
public void createLoan()
{
string baseCreateLoanUrl = #"https://serverhost/create?key=";
var strUCDExport = XDocument.Load(#"C:\CreateLoan_testcase.xml");
using (var client = new HttpClient())
{
var content = new StringContent(strUCDExport.ToString(), Encoding.UTF8, Mediatype);
string createLoanApi = string.Concat(baseCreateLoanUrl, APIKey);
try
{
var response = client.PostAsync(createLoanApi, content).Result;
}
catch (Exception ex)
{
MessageBox.Show("Error Happened here...");
throw;
}
if (response.IsSuccessStatusCode)
{
// Access variables from the returned JSON object
string responseString = response.Content.ReadAsStringAsync().Result;
JObject jObj = JObject.Parse(responseString);
if (jObj.SelectToken("failure") == null)
{
// First get the authToken
string LoanID = jObj["loanStatus"]["id"].ToString();
MessageBox.Show("Loan ID: " + LoanID);
}
else
{
string getTokenErrorMsg = string.Empty;
JArray errorOjbs = (JArray) jObj["failure"]["errors"];
foreach (var errorObj in errorOjbs)
{
getTokenErrorMsg += errorObj["message"].ToString() + Environment.NewLine;
}
getTokenErrorMsg.Dump();
}
}
}
Thanks for Nard's comment, after comparing the header, I found the issue my client header has this:
Expect: 100-continue
While postman doesn't has.
Once I removed this by using the ServicePointManager:
ServicePointManager.Expect100Continue = false;
Everything seems fine now. Thanks all the input!
My gut tells me it's something simple. First, we know the API works, so I'm thinking it's down to how you are using the HttpClient.
First things first, try as suggested by this SO answer, creating it as a singleton and drop the using statement altogether since the consensus is that HttpClient doesn't need to be disposed:
private static readonly HttpClient HttpClient = new HttpClient();
I would think it would be either there or an issue with your content encoding line that is causing issues with the API. Is there something you are missing that it doesn't like, I bet there is a difference in the requests in Postman vs here. Maybe try sending it as JSON ala:
var json = JsonConvert.SerializeObject(strUCDExport.ToString());
var content = new StringContent(json, Encoding.UTF8, Mediatype);
Maybe the header from Postman vs yours will show something missing, I think the real answer will be there. Have fiddler running in the background, send it via Postman, check it, then run your code and recheck. Pay close attention to all the attribute tags on the header from Postman, the API works so something is missing. Fiddler will tell you.
I was struggling with this for 2 days when I stumbled over Fiddler which lets you record the traffic to the service. After comparing the calls I saw that I had missed a header in my code.

POST json to another url

I've a problem as I need to send some json to a url. When I send all my json and token to the page.
Then there will be no content JSON value into the system.
I have checked up on whether there is some content and it is there, but it sends just do not like json values.
string apiKeyToken = model.reepaytoken; // TOKEN HERE.
string URLLink = APIClassPay.HelperPay.CreateCustomerURL;//URL to send it json to.
WebClient client = new WebClient();
//JSON coming here!
var JSONCustomer = APIClassPay.HelperPay.CreateCustomer(model.Brugernavn, model.Adresse, model.Byen, model.Postnr.ToString(), model.Mobil.ToString(), model.Fornavn, model.Efternavn);
client.Headers.Add("text/json", JSONCustomer);
client.Headers.Set("X-Auth-Token", apiKeyToken);
string reply = client.DownloadString(URLLink);
When I blow my json looks like this.
[HttpPost]
public ActionResult information(BuyMedlemskabViewModel model)
{
DataLinqDB db = new DataLinqDB();
var Pric = db.PriceValues.FirstOrDefault(i => i.id == model.HiddenIdMedlemskab);
if (Pric != null)
{
string _OrderValue = DateTime.Now.Year + Helper.Settings.PlanValue();
Session[HelperTextClass.HelperText.SessionName.OrderId] = _OrderValue;
Session[HelperTextClass.HelperText.SessionName.FakturaId] = model.HiddenIdMedlemskab;
Session[HelperTextClass.HelperText.SessionName.fornavn] = model.Fornavn;
Session[HelperTextClass.HelperText.SessionName.efternavn] = model.Efternavn;
Session[HelperTextClass.HelperText.SessionName.Adresse] = model.Adresse;
Session[HelperTextClass.HelperText.SessionName.Post] = model.Postnr;
Session[HelperTextClass.HelperText.SessionName.Byen] = model.Byen;
Session[HelperTextClass.HelperText.SessionName.Mobil] = model.Mobil;
string apiKeyToken = model.reepaytoken;.
string URLLink = APIClassPay.HelperPay.CreateCustomerURL;//URL to send it json to.
WebClient client = new WebClient();
//JSON coming here!
var JSONCustomer = APIClassPay.HelperPay.CreateCustomer(model.Brugernavn, model.Adresse, model.Byen, model.Postnr.ToString(), model.Mobil.ToString(), model.Fornavn, model.Efternavn);
client.Headers.Add("text/json", JSONCustomer);
client.Headers.Set("X-Auth-Token", apiKeyToken);
string reply = client.DownloadString(URLLink);
}
return RedirectToAction("information");
}
EDIT - Update (ERROR HERE):
ReePay API reference: https://docs.reepay.com/api/
I think there are a few things, you'll have to fix:
First of all you're obviously trying to create a ressource (usually a POST or PUT, speaking in REST-words but you're using WebClient's DownloadString-method which performs a GET. So I think you should probably use a POST or PUT instead but which one to chose exactly depends on the web service you're contacting.
Then you seem to have mistaken the Content-Type-header and tried to pack the payload in there. The payload - your customer JSON - will have to be put into the request's body.
Based on your previous questions I assume the service you're trying to contact is either PayPal or QuickPay. To further help you with this question, it'd be helpful if you could specify which one you use.
If it's QuickPay, please notice that there's an official .NET client which you could use instead of using WebClient on you own.
But anyway for making HTTP requests I'd suggest you to use HttpClient in favor of WebClient. You'd generally do it in a way like this:
using (var httpClient = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post,
APIClassPay.HelperPay.CreateCustomerURL);
request.Headers.Add("X-Auth-Token", apiKeyToken);
request.Headers.Add("Content-Type", "application/json");
request.Content = new StringContent(JSONCustomer);
var response = await httpClient.SendAsync(request);
}
EDIT:
As you clarified in a comment, the service you're using is Reepay. If you take a look at the documentation of the create customer method, you can see, that the necessary HTTP method is POST. So the code snippet above should generally fit.
Regarding the compilation error you faced, I updated the code-snipped above. There was a mistake in the variable names I chose. Please note, that you dropped the keyword await as I can see from your screenshot. Please re-enter it. If the compiler complains about it, it's very likely that the .NET framework version of your project is less than 4.5 which is necessary to use async/await.
So you should update your project's .NET framework version at best to version 4.6.1 as Microsoft recently announced that support for 4.5 and others is discontinued. Have a look here on how to do that.

Load webpage with reseting cookies in c# net

I want to make a simple program, that loads a webpage(eg. to webclieant control) and i want to reset cookies in every time, when i load this page. I don't know, how to do it, so maybe can give me a example how to do it?
Thank you for any help :)
If you want to make simple loads using WebClient class of the Framework, it is actually simple. WebClient class can use Cookies by using Headers and ResponseHeaders properties. If you want to clear cookies on every request, just clean the right Header before doing a request. I don't know exactly if this is your question, so I will send an example on how to work with cookies using WebClient. I hope it is what you are looking for.
You can set cookies on WebClient easily by using the Headers property, and get the Cookies that must be sent back using the ResponseCookies property. If you want to manage your cookies, try doing something like this:
class Program
{
static void Main(string[] args)
{
// Put your cookies content here
string myCookies = "Cookie1=Value; Cookie2=Value";
// The URL you want to send a request
string url = "https://www.google.com.br/";
using (var client = new WebClient())
{
// If you want to attach cookies, you can do it
// easily using this code.
client.Headers.Add("Cookie", myCookies);
// Now you get the content of the response the way
// better suits your application.
// Here i'm using DownloadString, a method that returns
// the HTML of the response as a System.String.
// You can use other options, like OpenRead, wich
// creates a Stream to get the Response content.
string responseContent = client.DownloadString(url);
// If you want to save the cookies of the response to
// use them later, just use the ResponseHeaders property.
string responseCookies = client.ResponseHeaders["Set-Cookie"];
// Here is the response of your question (I I'm correct).
// If you need to use the same instance of WebClient to make
// another request, you will need to clear the headers that
// are being used as cookies.
// You can do it easily by using this code.
client.Headers.Remove("Cookie");
}
}
}
HttpCookie aCookie;
string cookieName;
int limit = Request.Cookies.Count;
for (int i=0; i<limit; i++)
{
cookieName = Request.Cookies[i].Name;
aCookie = new HttpCookie(cookieName);
aCookie.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(aCookie);
}

Categories

Resources