Can HTTP requests be unintentionally cached? - c#

The following HTTP fetch should return the string 262. For some of my users (but not all, or even most) it seems to be returning an older value 261.
string latestVersion = new System.Net.WebClient().DownloadString(
"http://www.pixelchampions.com/latest.txt");
I don't really know why. IIS7 is using mostly default settings. Do some routers/ISPs cache the results of non-query HTTP requests?

You can try this (or one of the other RequestCacheLevel settings):
WebClient MyClient = new System.Net.WebClient() ;
MyClient.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
string latestVersion = MyClient.DownloadString(
"http://www.pixelchampions.com/latest.txt");

Related

MVC 4 RedirectToAction does not see Custom Header

If you start a new Web Project, and create a new MVC4 application (with sub-kind as "WebApi", you can paste the below code in (overwriting HomeController.cs) to get the code to work.
I have a MVC4 application (with WebApi).
I am trying to set a custom-header in a MVC controller method and then do a RedirectToAction. The custom-header is not seen in the second mvc-controller-method.
I am able to set a cookie in the first mvc-controller-method and see it in the second mvc-controller-method (after a RedirectToAction).
Is there a way to see the custom-header I set in the second mvc-controller-method after a RedirectToAction ?
Thanks.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace MyMvc4WebApiProjectNamespace.Controllers
{
public class HomeController : Controller
{
private const string CustomCookieName = "CustomCookieName";
private const string CustomHeaderName = "X-CustomHeaderName";
private const string IISExpressRootUrl = "http://localhost:55937/"; /* open up the project properties and go to the web tab and find the iis-express area to get the correct value for your environment */
public ActionResult Index()
{
IEnumerable<string> webApiValues = null;
string value1 = null;
string value2 = null;
HttpClientHandler handler = new HttpClientHandler
{
UseDefaultCredentials = true,
PreAuthenticate = true
};
using (var client = new HttpClient(handler))
{
string valuesUri = IISExpressRootUrl + "api/Values";
webApiValues = client
.GetAsync(valuesUri)
.Result
.Content.ReadAsAsync<IEnumerable<string>>().Result;
if (null != webApiValues)
{
value1 = webApiValues.ElementAt(0);
value2 = webApiValues.ElementAt(1);
}
else
{
throw new ArgumentOutOfRangeException("WebApi call failed");
}
}
HttpCookie customCookie = new HttpCookie(CustomCookieName, "CustomCookieValue_ThisShowsUpIn_MyHomeControllerAlternateActionResult_Method");
Response.Cookies.Add(customCookie);
HttpContext.Response.AppendHeader(CustomHeaderName, "CustomHeaderValue_This_Does_Not_Show_Up_In_MyHomeControllerAlternateActionResult_Method");
//Response.AppendHeader(CustomHeaderName, value2);
return RedirectToAction("MyHomeControllerAlternateActionResult");
}
public ActionResult MyHomeControllerAlternateActionResult()
{
IEnumerable<string> webApiReturnValues = null;
CookieContainer cookieContainer = new CookieContainer();
foreach (string cookiename in Request.Cookies)
{
if (cookiename.Equals(CustomCookieName, StringComparison.OrdinalIgnoreCase))
{
var cookie = Request.Cookies[cookiename];
cookieContainer.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, "localhost"));
}
}
if (cookieContainer.Count < 1)
{
throw new ArgumentOutOfRangeException("CookieContainer did not find the cookie I was looking for");
}
else
{
Console.WriteLine("This is what actually happens. It finds the cookie.");
}
HttpClientHandler handler = new HttpClientHandler
{
UseCookies = true,
UseDefaultCredentials = true,
PreAuthenticate = true,
CookieContainer = cookieContainer
};
using (var client = new HttpClient(handler))
{
bool customHeaderWasFound = false;
if (null != this.Request.Headers)
{
if (null != this.Request.Headers[CustomHeaderName])
{
IEnumerable<string> headerValues = this.Request.Headers.GetValues(CustomHeaderName);
client.DefaultRequestHeaders.Add(CustomHeaderName, headerValues);
customHeaderWasFound = true;
}
}
/*I wouldn't expect it to be in the below, but I looked for it just in case */
if (null != this.Response.Headers)//
{
if (null != this.Response.Headers[CustomHeaderName])
{
IEnumerable<string> headerValues = this.Response.Headers.GetValues(CustomHeaderName);
client.DefaultRequestHeaders.Add(CustomHeaderName, headerValues);
customHeaderWasFound = true;
}
}
if (!customHeaderWasFound)
{
Console.WriteLine("This is what actually happens. No custom-header found. :( ");
}
string valuesUri = IISExpressRootUrl + "api/Values";
webApiReturnValues = client
.GetAsync(valuesUri)
.Result
.Content.ReadAsAsync<IEnumerable<string>>().Result;
if (null == webApiReturnValues)
{
throw new ArgumentOutOfRangeException("WebApi call failed");
}
}
return View(); /* this will throw a "The view 'MyHomeControllerAlternateActionResult' or its master was not found or no view engine supports the searched locations" error, but that's not the point of this demo. */
}
}
}
Response headers are never copied automatically to requests - so setting any custom headers on response will not impact next request issued to handle 302 redirect.
Note that it is the case even with cookies: response comes with "set this cookie" header, and all subsequent request will get "current cookies" header.
If you have your own client you may be able to handle 302 manually (not possible if you are using browser as client).
As another answer stated, response headers are about this response, not the next one. Redirecting is not a server-side action. A redirect instructs the client to perform a completely new request, and of course in a new request, the response headers for the old request are not present. So return RedirectToAction("MyHomeControllerAlternateActionResult"); is guaranteed to not have this response's headers when the browser initiates the new request.
In trying to solve this problem, one might think of trying to persist the data to the next request server-side, such as through a cookie or in an explicit session variable, or implicitly via use of ViewBag/ViewData/TempData. However, I don't recommend this as using session state heavily has performance implications in large/high-usage web sites, plus there are other negative and subtle side-effects that you may run into down the road. For example, if a person has two browser windows open to the same web site, they can't be doing different actions reliably, as the session data for one window can end up being served to the other one. Avoid session usage as much as possible in your web site design—I promise this will benefit you down the road.
A slightly better way, though still with its problems, is to redirect to a URL with querystring parameters containing a payload. And, instead of the whole set of data, you can provide a key that can be pulled from the session (as long as it's also bound to their IP address and is large like a GUID or two together). However, relying on session state is still not ideal as stated before.
Instead, consider using server-side redirection such as child actions. If you find that hard because what you want to call is a main controller you have a few options:
If you're using dependency injection, add a parameter to the current controller (saving it from the constructor and using it in the request method) that is the desired controller you want to "redirect" to. You can then call that controller directly. This may not be ideal (as all calls to this controller also have to new up a copy of that one), but it does work. Trying to new up the other controller manually can also work, but for reasons I don't fully remember, I think this can give some additional problems. In any case, this method can give issues accessing the HttpRequest context and other context objects correctly, though this can be worked around.
Rearchitect your application so that controllers are not the place where full pages are rendered. Instead, use them as "smart routers" that call child actions to perform the real work. Then, you can call the same child actions from any controller. But this still has problems.
Perhaps the best way is to add custom routing logic through action filters or other means (search the web!) so that the correct controller is hit in the first place! This may not always be possible, but sometimes the need to redirect to another controller mid-procedure actually points to a larger design problem. Focusing on how to cause the knowledge of which controller to hit to be available earlier in the pipeline (such as during routing) can reveal architecture problems, and can reveal likely solutions to them.
There may be other options that I haven't thought of, but at least you have a few alternatives to the simple "no way to do that."
I was able to do something similar like what the user is requesting in the following (rudimentary) way:
In the redirect, add a custom query string parameter
Create a custom Module that checks for that parameter and appends the custom header (read http://dotnetlionet.blogspot.com/2015/06/how-to-add-httpmodule-in-mvc5.html on how to do your own module)
In this way I was able to get my custom headers to be picked up

setExpressCheckout and SSL/TLS error

I'm trying to develop a simple application that will enable users to purchase services off a website through the Paypal API. This application is running on ASP.NET with C#.
I have had very little luck trying to get the Paypal API to co-operate. The method I'm calling is SetExpressCheckout with all the appropriate variables.
I did my research and discovered that since I'm testing in Localhost, it may affect Paypal's ability to communicate with the application. So the next thing I tried was accessing my application through an open port and a publicly accessible IP address, but the same error occurs on the call to SetExpressCheckout.
Here is the error:
Exception Details: System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel.
Source Error:
Line 1790: [return: System.Xml.Serialization.XmlElementAttribute("SetExpressCheckoutResponse", Namespace="urn:ebay:api:PayPalAPI")]
Line 1791: public SetExpressCheckoutResponseType SetExpressCheckout([System.Xml.Serialization.XmlElementAttribute(Namespace="urn:ebay:api:PayPalAPI")] SetExpressCheckoutReq SetExpressCheckoutReq) {
Line 1792: object[] results = this.Invoke("SetExpressCheckout", new object[] {
Line 1793: SetExpressCheckoutReq});
Line 1794: return ((SetExpressCheckoutResponseType)(results[0]));
Source File: c:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\anan_p2\730602d6\31a8d74e\App_WebReferences.c8vgyrf8.2.cs Line: 1792
I've also tried generating certificates using OpenSSL and uploading them to the Paypal account's encrypted seller option but still no effect.
Thank you very much for reading through my question!
Update: As requested here is the code being used.
String hostingOn = ConfigurationManager.AppSettings["default_site_url"];
reqDetails.ReturnURL = hostingOn + "marketplace_confirm.aspx";
reqDetails.CancelURL = hostingOn + "marketplace.aspx";
reqDetails.NoShipping = "1";
reqDetails.ReqConfirmShipping = "0";
reqDetails.OrderTotal = new BasicAmountType()
{
currencyID = CurrencyCodeType.CAD,
Value = payment_amt.Value,
};
SetExpressCheckoutReq req = new SetExpressCheckoutReq()
{
SetExpressCheckoutRequest = new SetExpressCheckoutRequestType()
{
Version = UtilPayPalAPI.Version,
SetExpressCheckoutRequestDetails = reqDetails
}
};
PayPalAPIAASoapBinding paypal = new PayPalAPIAASoapBinding();
paypal.SetExpressCheckout(req);
I am also using the https://api-aa-3t.paypal.com/2.0/ url for accessing the API
Since early 2016, Paypal started requiring TLS 1.2 protocol for communications in the Sandbox, and will enforce it for the live environment starting June 17. See here for reference.
In most .NET applications TLS 1.2 will come disabled by default, and therefore you'll need to enable it.
You need to add the following line, for example, at the beginning of you Application_Start method:
public class Site : HttpApplication
{
protected void Application_Start()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// other configuration
}
}
You're probably connecting to api.paypal.com or api.sandbox.paypal.com, and not sending along your API certificate. The API certificate is a client SSL certificate used to complete the SSL chain.
If you don't have or are not using an API certificate, you should connect to api-3t.paypal.com or api-3t.sandbox.paypal.com for Live or Sandbox respectively.
I've been working with a PayPal (NVP/Signature) Express Checkout integration and have been hit with this SSL/TLS error.
Nothing I did seemed to get around it but then I found the following code to add above my request. For reference, I'm using MVC3/.NET 4 so Tls1.2 isn't available to me by default (like in .NET 4.5 +). This first three lines of this code gets around that. I hope it helps people!
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
ServicePointManager.DefaultConnectionLimit = 9999;
var url = "https://[paypal-api-url]/nvp";
var uri = new Uri(url);
var request = WebRequest.Create(uri);
var encoding = new UTF8Encoding();
var requestData = encoding.GetBytes(data);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
request.Timeout = (300 * 1000);
request.ContentLength = requestData.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(requestData, 0, requestData.Length);
}
var response = request.GetResponse();
...
Thanks a lot that really helps me.
For reference here is my code for establishing the interface in VB.NET
'Create a service Binding in code
Dim ppEndpointAddress As New System.ServiceModel.EndpointAddress("https://api-3t.sandbox.paypal.com/2.0/")
Dim ppBinding As New System.ServiceModel.BasicHttpBinding(System.ServiceModel.BasicHttpSecurityMode.Transport)
Dim ppIface As New PayPalAPI.PayPalAPIAAInterfaceClient(ppBinding, ppEndpointAddress)
Dim ppPaymentReq As New PayPalAPI.DoDirectPaymentReq()
ppPaymentReq.DoDirectPaymentRequest = ppRequest

How do I use the Redmine REST API over https from .NET?

Our internal Redmine server only allows me to connect via HTTPS. Here's how I tried to use the REST API via HTTPS from .NET:
As suggested in Using the REST API with .NET, setting the host variable to "https://redmine.company.com/redmine/" and the apiKey to "ffffffffffffffffffffffffffffffffffffffff".
From scratch with the following code:
using System.IO;
using System.Net;
class Program
{
static void Main(string[] args)
{
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) => true;
var request = (HttpWebRequest)WebRequest.Create(
"https://redmine.company.com/redmine/issues/149.xml?key=ffffffffffffffffffffffffffffffffffffffff");
request.CookieContainer = new CookieContainer();
request.Method = "GET";
using (var response = request.GetResponse()) // Hangs here
using (var responseStream = response.GetResponseStream())
using (var memoryStream = new MemoryStream())
{
responseStream.CopyTo(memoryStream);
}
}
}
Of course, company.com and ffffffffffffffffffffffffffffffffffffffff are just placeholders for my real company and my real API key on my account page. Both attempts hang for some time before timing out with a WebException (see the Hangs here comment in attempt 2). I then tried to download other stuff from the Redmine server (like e.g. time_entries.csv, atom feeds, etc.), each time with exactly the same result.
So far so bad. However, if I copy-paste the URL https://redmine.company.com/redmine/issues/149.xml?key=ffffffffffffffffffffffffffffffffffffffff into my browser, I get exactly the response I would expect. So, it seems as though our Redmine server behaves as it should, but somehow I can't get it to work from .NET.
I have successfully downloaded stuff from other HTTPS sites and have managed to download issue data from http://demo.redmine.org with the code of attempt 2 (of course with adapted URLs, etc.). So, it seems there might be something special about how Redmine communicates over HTTPS.
If anybody is successfully using the Redmine REST API over HTTPS from .NET, I'd be really grateful for some pointers on what I'm doing wrong.
Also, suggestions on how to debug this from the client side would be greatly appreciated. So far I've tried Fiddler2, with no success. As soon as I enable its "Decrypt HTTPS traffic" setting then I no longer get an answer when I make the request in Internet Explorer.
We use redmine-net-api which supports HTTP/S connection and authentication based on API keys.
RedmineManager rm = new RedmineManager("https://&ltyour-address&gt", &ltapi-key&gt, "random-password");
IList&ltIssue&gt issues = rm.GetObjectList&ltIssue&gt(new NameValueCollection() { { "project_id", &ltproject-id&gt } });
Try this, it works for me:
// Allow every secure connection
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, error) => true;
// Create redmine manager (where URL is "https://10.27.10.10/redmine" for me and redmineKey is my redmine API key
RedmineManager redmineManager = new RedmineManager(redmineURL, redmineKey);
// Create your query parameters
NameValueCollection queryParameters = new NameValueCollection { { "project_id", "4" }, {"tracker_id", "17"}, { "offset", "0" } };
// Perform your query
int issuesFound = 0;
foreach (var issue in redmineManager.GetObjectList<Issue>(queryParameters, out issuesFound))
{
// By default you get the 25 first issues of the project_id and tracker_id specified.
// Play with the offset to get the rest
queryParameters["offset"] = ....
}
Explicit passing SecurityProtocolType.Tls12 value for securityProtocolType parameter solved the problem for my case:
RedmineManager redmineManager = new RedmineManager(_host, _apiKey,
securityProtocolType: SecurityProtocolType.Tls12);

What's wrong with my web service client?

Web service is created in PHP im calling by adding a reference in C#
funcRequest aa = new funcRequest();
aa.param = "ZZ";
string z;
funcResponse a = new funcResponse();
z = a.result;
i created like this to call the web service from C# but looks its not giving any value back .. where am i wrong ?
You shouldn't be creating the response object yourself. You should be doing something like:
FuncRequest request = new FuncRequest("ZZ");
MyWebService service = new MyWebService();
FuncResponse response = service.DoSomething(request);
Obviously the exact details will depend on how you're connecting to the service, whether you're generating the proxy code etc, but basically you need to get something involved which represents the service itself.
You'll need to instantiate and make requests with the generated client proxy class or something similar, you can't just new up requests and responses and in this manner, you need to use and retrieve them, respectively. For instance, if your service reference was named MyService then you ought to have a MyServiceClient class available to you, so that:
using (var myServiceClient = new MyServiceClient())
{
var request = new MyServiceRequestType();
request.MyProperty = "zzz";
var response = myServiceClient.MakeRequest(request);
}

Amazon (AWS) - The request must contain the parameter Signature

I'm struggling with the final part of getting my first bit of code working with the AWS - I have got this far, I attached the web reference in VS and this have this
amazon.AWSECommerceService service = new amazon.AWSECommerceService();
// prepare an ItemSearch request
amazon.ItemSearchRequest request = new amazon.ItemSearchRequest();
request.SearchIndex = "DVD";
request.Title = "scream";
request.ResponseGroup = new string[] { "Small" };
amazon.ItemSearch itemSearch = new amazon.ItemSearch();
itemSearch.AssociateTag = "";
itemSearch.Request = new ItemSearchRequest[] { request };
itemSearch.AWSAccessKeyId = ConfigurationManager.AppSettings["AwsAccessKeyId"];
itemSearch.Request = new ItemSearchRequest[] { request };
ItemSearchResponse response = service.ItemSearch(itemSearch);
// write out the results
foreach (var item in response.Items[0].Item)
{
Response.Write(item.ItemAttributes.Title + "<br>");
}
I get the error
The request must contain the parameter Signature.
I know you have to 'sign' requests now, but can't figure out 'where' I would do this or how? any help greatly appreciated?
You have to add to the SOAP request headers including your Amazon access key ID, a timestamp, and the SHA256 hash of the request operation and the timestamp. To accomplish that, you would need access to the SOAP message just before it is going to be sent out. There's a walkthrough and a sample project I put together at http://flyingpies.wordpress.com/2009/08/01/17/.
For the record:
Another reason to get this error is due to keywords with spaces in it.
Example:
'http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=xxx&AssociateTag=usernetmax-20&Version=2011-08-01&Operation=ItemSearch&ResponseGroup=Medium,Offers&SearchIndex=All&Keywords=Baby
Stroller&MerchantId=All&Condition=All&Availability=Available&ItemPage=1&Timestamp=2012-05-16T02:17:32Z&Signature=ye5c2jo99cr3%2BPXVkMyXX8vMhTC21UO4XfHpA21%2BUCs%3D'
It should be:
'http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=xxx&AssociateTag=usernetmax-20&Version=2011-08-01&Operation=ItemSearch&ResponseGroup=Medium,Offers&SearchIndex=All&Keywords=Baby%20Stroller&MerchantId=All&Condition=All&Availability=Available&ItemPage=1&Timestamp=2012-05-16T02:17:32Z&Signature=ye5c2jo99cr3%2BPXVkMyXX8vMhTC21UO4XfHpA21%2BUCs%3D'
PHP solution:
$Keywords = str_replace(' ', '%20', $Keywords);
or
$Keywords = urlencode($Keywords);

Categories

Resources