why do we use HttpContext.Current? - c#

I don't know, why do we use HttpContext.Current?
In this property I use it for Session but I don't know why!
public static string Name
{
get
{
if (HttpContext.Current.Session["_n_"] != null)
return HttpContext.Current.Session["_n_"].ToString();
else return "";
}
set
{
HttpContext.Current.Session["_n_"] = value;
}
}

HttpContext is an object that wraps all http related information into one place. HttpContext.Current is a context that has been created during the active request. Here is the list of some data that you can obtain from it.
Request type (Post, Get)
Request parameters (querystring, posted data)
User's IP address
Cookies
Further you can control your output through this object. In Items property, which is a dictionary, you can store instances of objects to ensure that they are created once for the request. You can control the output stream applying your custom filters.
This is a short list of that what you can do with this property.

It's a way to get access to the current HttpContext someplace that may not have a reference to the context but is within an active web request.

That's like saying "Why do I need to go to a bank to get money?", to which the answer is "Because that's where the money is.
To answer your question. Because that's where the Session is. It's really that simple. You don't have to know why, just that that's where it is.
There's a much longer explanation, which other people are giving with all the technical details. But in the end, the answer just boils down to this.

before asp.net MVC in a web form, there were classes request, response where you can get cookies and session and those staff in MVC all the HTTP information like request and response and their properties are now inside HTTpcontext.

Related

"The page you requested was removed" with 410 and redirect

I have following requirement:
the user comes to a job page in our customer's website, but the job is already taken, so the page does not exist anymore
the user should NOT get a 404 but a 410(Gone) and then be redirected to a job-overview-page where he gets the information that this job is not available anymore and a list of available jobs
but instead of a 302(temp. moved) or a 404(current behavior) google should get a 410(gone) status to indicate that this page is permanently unavailable
so the old url should be removed from the index and the new not be treated as a replacement
So how i can redirect the user with a 410 status? If i try something like this:
string overviewUrl = _urlContentResolver.GetAbsoluteUrl(overviewPage.ContentLink);
HttpContext context = _httpContextResolver.GetCurrent();
context.Response.Clear();
context.Response.Redirect(overviewUrl, false);
context.Response.StatusCode = 410;
context.Response.TrySkipIisCustomErrors = true;
context.Response.End();
I get a static error page in chrome with nothing but:
The page you requested was removed
But the status-code is correct(410) and also the Location is set correctly, just no redirect.
If i use Redirect and set the status before:
context.Response.StatusCode = 410;
context.Response.Redirect(overviewUrl, true); // true => endReponse
the redirect happens but i get a 302 instead of the desired 410.
Is this possible at all, if yes, how?
I think you're trying to bend the rules of http. The documentation states
The HyperText Transfer Protocol (HTTP) 410 Gone client error response code indicates that access to the target resource is no longer available at the origin server and that this condition is likely to be permanent.
If you don't know whether this condition is temporary or permanent, a 404 status code should be used instead.
In your situation either 404 or 410 seems to be the right status code, but 410 does not have any statement about redirection as a correct behavior that browsers should implement, so you have to assume a redirect is not going to work.
Now, to the philosophically right way to implement your way out of this...
With your stated requirements, "taken" does not mean the resource is gone. It means it exists for the client that claimed it. So, do you 302 Redirect a different client to something else that might be considered correct? You implemented that, and it seems like the right way to do it.
That said, I don't know if you "own" the behavior across the client and server to change the requirements to this approach. Looking at it from the "not found" angle, a 404 also seems reasonable. It's not found because "someone" already has the resource.
In short if your requirements are set in stone, they may be in opposition to the HTTP spec. If you still must have a 410 then you would need to change the behavior on the client-side somehow. If that's JavaScript, you'd need to expect a 410 from the server that returns a helpful payload that the client interprets to do something else (e.g. like a simulated redirect).
If you don't "own" the client code... well that's a different problem.
There's a short blog post by Tommy Griffth that backs up what I am saying. Take a read. It says in part,
The “Gone” error response code means that the page is truly gone—it’s no longer available on the origin server and no redirect was set up.
Sometimes, webmasters want to be very explicit to Google and other search engines that a page is gone. This is a much more direct signal to Google that a page is truly gone and never coming back. It's slightly more direct than a 404.
So, is it possible? Yes, but you're going to need to "fake" it by changing both client and server code.
I will accept Kit's answer since he's right in general, but maybe i have overcomplicated my requirement a bit, so i want to share my solution:
What i wanted actually?
provide crawlers a 410 so that the taken job page is delisted from search engine indexes
provide the user a better exeprience than getting a 404, so redirect him to a job-overview where he can find similar jobs and gets a message
These are two separate requirements and two separate users, so i could simply provide a solution for a crawler and one for a "normal" user.
In case someone needs something similar i can provide more details, just a snippet:
if (HttpContext.Current.IsInSearchBotMode())
{
Deliver410ForSearchBots(HttpContext.Current);
}
else
{
// redirect(301) to job-overview, omitting details
}
private void Deliver410ForSearchBots(HttpContext context)
{
context.Response.Clear();
context.Response.StatusCode = 410;
context.Response.StatusDescription = "410 job taken";
context.Response.TrySkipIisCustomErrors = true;
context.Response.End();
}
public static bool IsInSearchBotMode(this HttpContext context)
{
ISearchBotConfiguration configuration = ServiceLocator.Current.GetInstance<ISearchBotConfiguration>();
string userAgent = context.Request?.UserAgent;
return !(string.IsNullOrEmpty(userAgent) || configuration.UserAgents == null)
&& configuration.UserAgents.Any(bot => userAgent!.IndexOf(bot, StringComparison.InvariantCultureIgnoreCase) >= 0);
}
These user-agents i have used for the crawler detection:
<add key="SearchBot.UserAgents" value="Googlebot;Googlebot-Image;Googlebot-News;APIs-Google;AdsBot-Google;AdsBot-Google-Mobile;AdsBot-Google-Mobile-Apps;DuplexWeb-Google;Google-Site-Verification;Googlebot-Video;Google-Read-Aloud;googleweblight;Mediapartners-Google;Storebot-Google;LinkedInBot;bitlybot;SiteAuditBot;FacebookBot;YandexBot;DataForSeoBot;SiteCheck-sitecrawl;MJ12bot;PetalBot;Yeti;SemrushBot;Roboter;Bingbot;AltaVista;Yahoobot;YahooCrawler;Slurp;MSNbot;Lycos;AskJeaves;IBMResearchWebCrawler;BaiduSpider;facebookexternalhit;XING-contenttabreceiver;Twitterbot;TweetmemeBot" />

Access cookie before View loaded in MVC5?

Let's say I have a cookie with value "OLD", then I update the cookie with following code :
var lang = new HttpCookie("lang");
lang.Value = "NEW";
lang.Expires = DateTime.UtcNow.AddDays(2);
HttpContext.Current.Response.SetCookie(lang); //set updated cookie value
var x = HttpContext.Current.Request.Cookies("lang");
x will still have "OLD" as value because it's not being sent to the client. Is it possible to get the updated value without it being sent to client first?
The Problem
you are setting the cookie in HttpContext.Current.Response and trying to find it in HttpContext.Current.Request even before the next request has come to server.
It is not going to work.
My Advice
For this case, use of cookie is incorrect. You have other options better fit for purpose, like -
ViewData
ViewBag
TempData
Session
ViewModel
The Answer
But if you still want to use cookie this way, search the cookie in HttpContext.Current.Response. I am sure you will get it.
Like this:
var cookieValue = HttpContext.Current.Response.Cookies["OLD"].Value
If you don't want to send value to client side and only want to use for reference you can use TempData for your purpose. Which might help.
I think, you should look for it in Response, not in Request, because request - it's that came from client.

Difference between HttpResponse: SetCookie, AppendCookie, Cookies.Add

there are some different ways to create multi value cookies in ASP.NET:
var cookie = new HttpCookie("MyCookie");
cookie["Information 1"] = "value 1";
cookie["Information 2"] = "value 2";
// first way
Response.Cookies.Add(cookie);
// second way
Response.AppendCookie(cookie);
// third way
Response.SetCookie(cookie);
When should I use which way? I've read that SetCookie method updates the cookie, if it already exits. Doesn't the other ways update the existing cookie as well?
And is the following code best practice for writing single value cookies?
Response.Cookies["MyCookie"].Value = "value";
If I remember correctly both
Response.Cookies.Add(..)
and
Response.AppendCookie(..)
will allow multiple cookies of the same name to be appended to the response.
On the other hand
Response.SetCookie(..)
and
Response.Cookies[key].Value = value;
will always overwrite previous cookies of the same name.
When should I use which way?
It's depends on what Cookie operation you want to do.
Note that Add and AppendCookie are doing the same functionality except the fact that with AppendCookie you're not referencing the Cookies property of the Response class and it's doing it for you.
Response.Cookies.Add - Adds the specified cookie to the cookie
collection.
Response.AppendCookie - Adds an HTTP cookie to the
intrinsic cookie collection
Response.SetCookie - Updates an existing cookie in the cookie
collection.
Exceptions will not be thrown when duplicates cookies are added or when attempting to update not-exist cookie.
The main exception of these methods is: HttpException (A cookie is appended after the HTTP headers have been sent.)
The Add method allows duplicate cookies in the cookie collection. Use the Set method to ensure the uniqueness of cookies in the cookie collection.
Thanks for MSDN!
To piggyback on tne's comment in Wiktor's reply, AppendCookie and SetCookie shouldn't be used - they're for internal use by the .NET framework. They shouldn't be public but they are, my guess would be as a hack for the IIS pipeline somewhere else.
So you should do your cookie setting this way (or write an extension method for setting multiple cookies):
string cookieName = "SomeCookie";
string cookieValue = "2017";
if (Response.Cookies[cookieName] == null)
{
Response.Cookies.Add(new HttpCookie(cookieName, cookieValue));
}
else
{
Response.Cookies[cookieName].Value = cookieValue;
}

How to pass query string parameter in asp.net?

I am using Access Control service (ACS). I fetched all identity providers (ip) which i set for my application using the following code :
public ActionResult IdentityProviders(string serviceNamespace, string appId)
{
string idpsJsonEndpoint = string.Format(Global.IdentityProviderJsonEndpoint, serviceNamespace, appId);
var client = new WebClient();
var data = client.DownloadData(idpsJsonEndpoint);
return Content(Encoding.UTF8.GetString(data), "application/json");
}
When user click over the signin link the above code called using ajax and get the ips and display them in jquery-ui dialog. And when user click any one of the ips for login the browser redirect to the selected ip login page. After successful login the control return to my control which i set as a returnUrl. Upto this every thing is works fine.
Now what i am trying to do is to pass some values to identity provider (ip) login page and want to get back those values at my returnUrl controller. For this i searched and came to know that there is a query string parameter known as wctx which we can set and get the value at return url. But i dont know how to do this. Can anybody please guid me how can i achieve this?
It is relatively (pretty) easy.
Your URL for listing IdPs looks something like this:
https://[your_namespace].accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=[your_realm]&reply_to=[configured_return_url_for_your_rp]&context=&request_id=&version=1.0&callback=
This is the most complete request for list of Identity Providers. Your may miss some variables (such as context, or reply_to), but what I show is the complete request.
So now you have two options:
inclide your own reply_to parameter. It must be withing the configured realm. So if your realm is https://www.mygreatapp.com/, your default return URL would probably be something like https://www.mygreatapp.com/returnUrl/ (if your controller to handle ACS response is returnUrlController. Now, you can safely change the reply_to to be https://www.mygreatapp.com/returnUrl/?foo=bar, just make sure you URL Encode the query string.
Use the context parameter. It is safer to use and I would suggest using it. Now your URL for fetching list of IdPs will be something like:
https://[your_namespace].accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=[your_realm]&reply_to=[configured_return_url_for_your_rp]&context=[your_custom_string_value_which_you_may_even_encrypt]&request_id=&version=1.0&callback=
Note the now there is context value present in the request for IdP list ([your_custom_string_value_which_you_may_even_encrypt]). In your returnUrl handler controller, you can check for it with code similar (or equal) to the following:
if (ControllerContext.HttpContext.Request.Form["wresult"] != null)
{
// This is a response from the ACS - you can further inspect the message if you will
SignInResponseMessage message =
WSFederationMessage.CreateFromNameValueCollection(
WSFederationMessage.GetBaseUrl(ControllerContext.HttpContext.Request.Url),
ControllerContext.HttpContext.Request.Form)
as SignInResponseMessage;
if (!string.IsNullOrWhiteSpace(message.Context))
{
// do whatever you want with the context value
}
}
You may want to perform any/more additional checks while handling the SignInResponse from ACS.

Grabbing Cookies in Web Browser Control - WP7

In order to log into a certain part of a website the users of my application require their cookie. To do this I need to grab it and pass it to url.
Does anyone know how to grab a certain websites cookie from the browser control?
I saw this method but wasn't quite clear.
Thanks, TP.
As of WP 7.1 Mango "release", if one may call it, please see the WebBrowser Control Overview for Windows Phone. It has been recently updated a little bit, and it turns out that they actually have added some support for cookie-retrieval from the WebBrowser. On the bottom of the page you will find a tiny link GetCookies(WebBrowser) pointing to description of a new class: WebBrowserExtensions with this very handy method. Yes, this class has only that one single member. It's an extension method, I suppose no explanations needed on that.
I have not played with this method much, but it seems that this will allow you to access the very same thing as the JS trick: the cookieset for the current URL. It probably will not allow to set anything, nor to peek cookies for other URLs. Maybe if you play hard with the CookieContainer you will receive, but I doubt.
On the 7.0 release, I've been struggling quite hard to achieve "cookie transparency" for my App. Long story short, my app was doing some background HTTP requests, and also had a WebBrowser to show some online content -- and "it would be great" if both sources of connections would emit the same cookies to the server.. And guess what, my application had to make the first request, then let the browser navigate. With such requirements, there was virtually is no way to achieve consistency of the cookies - bah, even with the current new and glorious GetCookie method, I suppose it would be damn hard. So, to the point - it was possible, but needed to use some hidden API, that is present publicitly on the Phone, but is hidden in the SDK. The API is available the (public) class System.Net.Browser.WebRequestCreator, freely available. The quirk is: in the SDK this class has a single public static property "IWebRequestCreate ClientHttp" with a method "Create" that you can use to "factory" your "raw http" connections - in case you dont want to use the WebClient for some reason. On the phone, and on the emulator, there is a second public static property called "IWebRequestCreate BrowserHttp", easily returned by Reflection:
PropertyInfo brwhttp = typeof(System.Net.Browser.WebRequestCreator)
.GetProperty("BrowserHttp")
with this property, you will be able to obtain a "special" internal instance of IWebRequestCreate that is used internally by the WebBrowser. By opening your background HTTP requests with this class, you will get your cookies automatically set as if they were created/sent by the WebBrowser control, but in turn - you will NOT be able to modify http headers, userprovide http user authentication and neither do a few lowlevel things - because all that settings will be synced with the WebBrowser's data stored for current 'system user instance', if I'm allowed to call it as such on the single-user Phone device heh. The interoperation between connections and the WebBrowser works both ways - if your HTTP connection (created with use of the 'hidden property') receives any settings/cookies/etc -- then the WebBrowser will instantly notice them and update its own cache. No cookie/session loss on neither of the sides!
If you need to passively get cookies for your subsequent connections after some first WebBrowser navigation - please use the GetCookie or the JS way.
But if you need your code to be first, and then pass authz to the WebBrowser -- you will probably have to dig deeper and use the above.. It's been hidden, so please resort to the other means first!
..and don't ask me how did I found it or how much time it took :P
have a nice fun with it
//edit: I've just found out, that the BrowserHttp property is a normal Silverlight's way to access the Browser's connection factory, please see BrowserHttp. It seems that it is only has been hidden in the 'miniSilverlight' written for the WP7 platform!
The approach being described in the post you linked is to use the WebBrowser control's InvokeScript method to run some javascript. However the post appears to use a "cookies" collection which doesn't actually exist.
string cookie = myWebBrowser.InvokeScript("document.cookie") as string;
Now for the hard part the string you get contains all pertinent cookie name/value pairs for the page with the values still being Url encoded. You will need to parse the returned string for the value you need.
See document.cookie property documentation.
Edit:
Looking at it fresh instead of relying on the post, InvokeScript invokes named function on the window of the host browser. Hence the page being displayed in the WebBrowser would itself need to include a function like:-
function getCookie() { return document.cookie; }
Then the InvokeScript would look like:-
string cookie = myWebBrowser.InvokeScript("getCookie");
As #quetzalcoatl already suggested, you can use internal instance of WebRequestCreator to share cookies between browser instances and instances of WebRequest. You don't get to access the cookies directly though, I think that's just a security measure by Microsoft.
This code below creates a WebReqeust object, connected to CookieContainer of WebBrowser instance. It then posts to a url to log in the user and store cookies in the container.
After it's done, all browser instances within the app instance will have required set of cookies.
var browser = new WebBrowser();
var brwhttp = typeof (WebRequestCreator).GetProperty("BrowserHttp");
var requestFactory = brwhttp.GetValue(browser, null) as IWebRequestCreate;
var uri = new Uri("https://www.login.com/login-handler");
var req = requestFactory.Create(uri);
req.Method = "POST";
var postParams = new Dictionary<string, string> {
{"username", "turtlepower"},
{"password": "ZoMgPaSSw0Rd1"}
};
req.BeginGetRequestStream(aReq => {
var webRequest = (HttpWebRequest)aReq.AsyncState;
using (var postStream = webRequest.EndGetRequestStream(aReq)) {
// Build your POST request here
var postDataBuilder = new StringBuilder();
foreach (var pair in paramsDict) {
if (postDataBuilder.Length != 0) {
postDataBuilder.Append("&");
}
postDataBuilder.AppendFormat("{0}={1}", pair.Key, HttpUtility.UrlEncode(pair.Value));
}
var bytes = Encoding.UTF8.GetBytes(postDataBuilder.ToString());
postStream.Write(bytes, 0, bytes.Length);
}
// Receive response
webRequest.BeginGetResponse(aResp => {
var webRequest2 = (HttpWebRequest) aResp.AsyncState;
webRequest = (HttpWebRequest)aResp.AsyncState;
string resp;
using (var response = (HttpWebResponse)webRequest2.EndGetResponse(aResp)) {
using (var streamResponse = response.GetResponseStream()) {
using (var streamReader = new System.IO.StreamReader(streamResponse)) {
resp = streamReader.ReadToEnd();
}
}
}
}, webRequest);
}, req);
One of the issues I couldn't solve though was exceptions thrown when server returns 302 - it seems to throw WebException error with "Not found" description.
// Ensure this is set to true BEFORE navigating to the page
webBrowser1.IsScriptEnabled = true;
// Once the page has loaded, you can read the cookie string
string cookieString = webBrowser1.InvokeScript("eval", new string[] { "document.cookie;" }) as string;
The cookieString variable will contain the full cookie for the document. You can then parse the string.
There is an WebBrowser Extension class which is exactly developed for this:
CookieCollection tempCookies = Microsoft.Phone.Controls.WebBrowserExtensions.GetCookies(this.BrowserControl);

Categories