Unable to cast object of type 'System.Web.Caching.CachedRawResponse' to type 'System.Web.Caching.CachedVary'.
I'm getting this on an AJAX call to an aspx page, and can find no information about it in webland. CachedRawResponse isn't even on MSDN. Does anyone know anything about it, or maybe point me in the right direction?
We recently had the same problem, and it turned out (in our case) that the page output cache module is rather sensitive to how you set your Response.Cache.VaryByXyz properties. We used code like the following in our HTTP compression module:
if (IsBrowserSupported(userAgent))
{
Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
...
}
Unfortunately, this causes ASP.NET to throw a fit when the page is cached after a call by a non-supported browser, and subsequently requested from the cache by a supported browser.
Not setting any VaryByXyz causes a CachedRawResponse to be stored in the ASP.NET output cache, but if you do set any VaryByXyz during your request, ASP.NET expects a CachedVary at that location. And instead of checking whether the cached page is of the right type, the framework just casts, resulting in an InvalidCastException.
Moral of the story: always set the VaryByXyz consistently, regardless of the request headers or other non-request-related variables. In our case, placing the VaryByHeaders outside of the if solved the error.
Related
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" />
i'm developing an MVC 4 web application.
I'm trying to make an url that changes in an authorized/unauthorized context.
I'm generating the following url for unauthorized user:
http://localhost/vendas-web/Login?ReturnUrl=%2Fvendas-web%2FClienteNovo%2FIndex%299999
The first time I've tested, it worked just fine.
But.. the second time I've tried, the query string got lost.. and the url turned into:
http://localhost/vendas-web/Login
When i test it against chrome on anonymous tab, it works FINE.
When i change the value of the last parameter, it works FINE.
There's some sort of cache related to this ?
What i'm doing wrong ?
Soo, my question is:
How do i keep my full url in any scenario ??
Ty
There's really not enough information here, but what you're likely talking about is that the first time a user needs to be authorized, they are automatically redirected to the first URL, which includes the ReturnUrl bit. That's built into the framework to allow the user to be redirected back to that URL after logging in. However, if you need to persist this past that initial first redirect to the login page, that's on you. Any links must manually add the query string param:
#Url.Action("SomeAction", new { ReturnUrl = Request["ReturnUrl"] })
And any forms must include it as a hidden input:
#Html.Hidden("ReturnUrl", Request["ReturnUrl"])
Otherwise, yes, it will be lost, because the literal URL you're now requesting doesn't include it. It's not just magically appended.
My problem was cache...
I've used this annotation to avoid using cache by application.
[OutputCache(NoStore = true, Duration = 0)]
I have a controller which processes an uploaded file.
In that controller, I return the user to a SharePoint list depending on the successful parsing of that file. I am able to enter a direct URL, but I am opening this page in a form so I need to change the window.top.location instead of just window.location. I tried doing this a few ways such as returning a JavaScript result, but I received some browser warning messages I'd like to avoid.
I ended up making a partial razor view which grabs a parameter from the query string in order to determine which list it should go to. The function works fine, but the page is seemingly inactive when I return it using:
return Redirect("~/Parsing/ParsingRedirector?List=MasterDealer");
My page exists in the folder, but I get an error stating "The resource cannot be found. "
Any reason why that's happening? I admittedly don't have a full understanding of MVC or even close to it at this point.
Try this:
return RedirectToAction("ParsingRedirector", "Parsing", new { List = "MasterDealer"});
This may be of help:
http://www.dotnet-tricks.com/Tutorial/mvc/4XDc110313-return-View()-vs-return-RedirectToAction()-vs-return-Redirect()-vs-return-RedirectToRoute().html
Keep in mind that, per that article, in the case of Redirect "you have to specify the full URL to redirect."
This is a WCF Oneway operation. HttpContext.Current.User is cleared in those operations, that is the reason I added a behavior that saves the User before it is cleared. Later I want to re-set the HttpContext.Current.User with the value I saved but I'm getting an exception:
HttpContext.Current.User = (RolePrincipal)userThatWasSavedBefore;
Object reference not set to an instance of an object.
at System.Web.HttpContext.SetPrincipalNoDemand(IPrincipal principal, Boolean needToSetNativePrincipal)
at System.Web.HttpContext.set_User(IPrincipal value)
at (My Function)
Why can't I set the user? What is the problem?
The reason you can't set it is most likely that the request has finished. Setting the User property results in ASP.NET trying to call back into per-request data structures, and if these data structures have been released then the resulting behavior is undefined.
For one-way operations, WCF invokes application code while simultaneously telling ASP.NET not to wait for the application code to finish and to just complete the request immediately. For this reason is it strongly recommended that you don't access HttpContext from a one-way operation.
Clearly some object in the line throwing the exception is null.
Check whether you have a current HttpContext, that is, I suspect HttpContext.Current is null.
The call stack shows an instance method of HttpContext is executed.
In other words - HttpContext is there, the exception is provoked by something else - a missing NulRef check likely for _notificationContext.
The NotificationContext is internal and unset only by OnRequestNotificationCompletionHelper as I write.
Exception scenario seem to look like:
Some work is to be done async - does not block/prevent a request from being completed
Request processing is finished
Your worker item sets HttpContext.User for the finished request
This looks like a framework bug :(
This problem is either because some object in these classes is null (such as HttpContext.Current)
OR
The user which you are passing there (and you saved before) is actually pointing to null. As you can see from stack trace, this is very likely the case, because the exception is thrown by System.Web.HttpContext.set_User(IPrincipal value)
User is actually not a variable, but a property. And changing it to another value is calling a function, that throws this exception in case new value for user is null.
In order to find out what is causing this problem I recommend you to set a breakpoint to the line which is throwing the exception and check if any part of that line isn't pointing to null
crash proof alternative could be:
if (HttpContext.Current != null && userThatWasSavedBefore != null)
{
HttpContext.Current.User = (RolePrincipal)userThatWasSavedBefore;
}
I have a certain document type in Kentico that has a boolean field that when true i need the page to redirect to another URL (in this case a 404 page).
Where is the best place to do this?
and how do i access the kentico data context in code so that i can write code that pulls the document types field and redirects based on it (because currently trying to access Dataitem("MyFieldName") errors because Kentico doesn't use DataItem for data binding, even though Eval("MyFieldName") still works.
You can access Kentico context data via the CMSContext object.
<%
if ((bool)CMSContext.CurrentDocument.DataRow["MyFieldName"])
Response.Redirect("PageNotFound.aspx");
%>
If you only want to be able to redirect a page to another page. Kentico already have something built in, just go to the Page->Properties->Menu. In the menu actions section you can specify a URL for redirection.
Otherwise for requirement described you can get the boolean value by:
if(ValidationHelper.GetBoolean(
CMSContext.CurrentDocument.GetValue("MyFieldName"), false)))
{
Response.Redirect("/404.aspx");
}