GetVaryByCustomString method is not called - c#

Have a problem on production server. On local and test servers this problem is not reproduced.
.NET 4.5, Sitecore 7.2.
Several user controls have the following directive:
<%# OutputCache VaryByParam="*" Duration="300" VaryByCustom="VaryByUrl" %>
Note: corresponding sitecore sublayouts have caching is turned off.
Method in Global.asax:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
switch (custom.ToLower())
{
case "varybyurl":
return context.Request.Url.DnsSafeHost + context.Request.RawUrl + OutputCacheKey;
}
return base.GetVaryByCustomString(context, custom);
}
Previously caching was working normally. But since recent times, it was broken suddenly (but only on live server). Its behavior:
Page #1 with user control (that have mentioned directive) is loaded with some information (e.g. "Text A").
Then I am opening another page #2 with the same control but with another data (which should have "Text B"), but "Text A" is shown on this control on this page.
Only after 300 seconds, after refreshing the page #2, it shows "Text B".
I was trying to add the logging into GetVaryByCustomString and no logs was received on live server, so it means that method was not called.
Maybe someone has any idea why it works in such way?
Thank you!

Cause of problem is found!
Global.asax file was absent on live server.
It was not delivered during last build for some reason.
Current issue could be closed :)

Related

MVC Ajax.error render Partial View

While working with the Kendo UI Grid for ASP.NET MVC I am finding some frustrating behavior that I can't seem to understand or find any information on. I have a grid which makes a kendoGrid.saveChanges() request to the server which has some server side validation. If the server-side validation fails, it returns the following result:
Response.StatusCode = HTTP_BAD_CLIENT_REQUEST; // const int 400
return PartialView("Error/BadRequest");
The partial view just has some basic HTML for displaying the error information to the user, and the grid's datasource error callback does the following:
function errorCallback(e) {
// Handles explicit Ajax and Kendo DataSource error callbacks
var content = e.responseText || e.xhr.responseText;
// Wrapper around a kendo.ui.Window to display content
Core.Alert.raiseAlert(content);
}
When I run my project in debug mode, or publish the Release version of the project to my local machine the e.xhr.responseText value is populated correctly i.e. it contains the partial view's HTML message. However, as soon as I move this to the production server the e.xhr.responseText simply contains the value "Bad Request" which is also the HTTP status code I am currently using. I've tried using other status codes but the result is the same (error name is used as the responseText).
The reason I find this to be so odd is that I am doing something similar in another project for an internal application at our company and this works perfectly fine. They are running on the same versions and tools of both Kendo and ASP.NET.
Can anyone point me in the direct of tools or documentation, Kendo or AJAX, which explain why the response text is not using my partial view result or how I can map the partial view result I am sending to the xhr.responseText property?
Edit: With trying different error codes, I found that certain status codes such 405 (Not Allowed) resulted in the IIS server error html being returned. So now I'm really stumped, why is it that some status codes simply return a name of the request, while others return templated HTML for that error code when I am specifying the return value and view to return?
Credit for findings and solutions goes to this post.
After doing some digging, I figured out that the issue came down to IIS overriding the content (and therefore partial view) being sent when I used the error HTTP status codes.
The solution is to add an <httpErrors> tag to the system.webServer in the web.config. I found that the following was sufficient to get my partial views to reach the client.
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>

Instantiation of a controller in OnActionExecuting (...not throwing a 404) in MVC Azure

The objective is to add a maintenance batch on the same url of the administration of an Azure MVC site. The url should be something like:
https://admin.mysite.com/Batch?pass=HKE671
I decided to override OnActionExecuting and to capture the information I need in the url to trigger the maintenance method. I am not familiar with MVC projects, and this may not sound very conventional...
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
EmailUtility.SendSupportEmail("DEBUG - On result executing", ""); //I receive this email
int y = 14;
if (Request.Url.Query.Length > y)
{
string url = Request.Url.Query.Remove(0, y); // I remove ?ReturnUrl=%2f
if (url.StartsWith("Batch"))
{
mySite.Admin.Web.Controllers.TenantController controller = new mySite.Admin.Web.Controllers.TenantController();
EmailUtility.SendSupportEmail("DEBUG - starts maintenance", ""); // I don't receive this one
controller.maintenance(HttpUtility.ParseQueryString(url).Get("pass"));
};
}
base.OnActionExecuting(filterContext);
}
This code works as well as I need on local, the maintenance method is called and does the job. But when deployed on Azure, this modification throws a 404 error. Interestingly, I send two debug emails : I don’t receive the second one "DEBUG - starts maintenance", therefore my 404 error comes from the instantiation of a controller in OnActionExecuting.
First, I would like to understand why is the behavior different between my development machine and Azure?
Then, how can I make it work? Thanks,
EDITED on Jan 4:
I made a little progress, but this issue is still unsolved.
- About the difference between my dev machine and Azure: there are a few redirections on this site: https, 404 and non-existent domain. I assumed it was due to a 404 error. Encapsulating the code with a try/catch didn't sent me any error, so I am guessing that I can suppress the 404 from the hypothesis.
- I tried the code above on OnAuthorization without having more success.
- I noticed that the first email DEBUG - On result executing is in fact sent at the first test only. It is not sent the second time I run my test. This doesn't make any sense to me, because the session should be checked every time.
Conclusion for today: it seems to be more a routing/redirection problem.
Why don't you do:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
try {
//your code here
} catch (Exception ex){
EmailUtility.SendSupportEmail("Execution failed: " + ex.Message , "");
}
}
If your code is throwing an exception, this should give you a better understanding on why it is failing.
But more important, a more robust way of checking for the correct URL would be:
if (filterContext.HttpContext.Request.Url.AbsolutePath.EndsWith("Batch")){
}
As far as i can tell your string variable "url" will not ever start with "Batch", because Request.Url.Query only conains the part after the "?" thus making your check always return false. Why this IS working on localhost is hard to tell, but in my opinion it shouldn't.
Actually, to work in all cases your check should be:
if (filterContext.HttpContext.Request.Url.AbsolutePath.ToLower().EndsWith("batch") || filterContext.HttpContext.Request.Url.AbsolutePath.ToLower().EndsWith("batch/")){
}
After your edit:
Ok, so what's happening is that you're requesting the action "Batch" in Home Controller, but because you're not authenticated you are redirected to LogOn in Account Controller. You should be seeing a login page, unless there's no associated LogOn view in your Views folder, or no AccountController at all, in both cases an exception is thrown.
Is the Batch method supposed to check for authentication? If not, remove the [Authorize] annotation from this method in HomeController and you should be fine.
You still have to adjust the if-check though, because it actually only evaluates to true on the LogOn page, and false if you actually get to the Batch page.
Shame on me, the problem was somewhere else!... The code above works well, but the routing of this site rejected an incomplete url. In this case, it had to contain the project name: https://admin.mysite.com/Admin/Batch?pass=HKE671 Then all is back to normal!
So the answer is: the behavior is different on my local computer because the local host of my development machine doesn't route projects of the solution the same way Azure does.
Many thanks for your help!

Server.Transfer causing Session exception

In my global I have the following code to handle when an error occurs
//[..] code goes here
Server.Transfer("~/Error.aspx?ErrorID=" + errorId);
It used to be a Response.Redirect which worked perfectly except that it changed the url (which is why I want to use Server.Transfer)
Unfortunately, now when it tries to load the Error page, it crashes on the Masterpage when it tries to refer to the Session
HttpException:
Session state can only be used when enableSessionState is set to true,
either in a configuration file or in the Page directive. Please also
make sure that System.Web.SessionStateModule or a custom session state
module is included in the \\
section in the application configuration.
I do have enableSessionState in both my config and my page.
I also found some links which suggest using Context.RewritePath - that just causes a blank page to load for me.
Using Response.Redirect works perfectly and as expected, so I assume Server.Transfer is the issue here. What is it?
EDIT Code:
protected void Application_Error(object sender, EventArgs e)
{
lock (_lockMe)
{
Exception ex = Server.GetLastError();
if (ex != null)
{
if (ex.InnerException != null)
ex = ex.InnerException;
ErrorLoggingManager.AddError(ex, new MembershipData(), ...); //etc
}
Server.ClearError();
//Some other database code for cleaning up some stuff when an error happens
}
try
{
if (Response != null)
{
//Get the last error logged
MyDataContext db = new MyDataContext();
int errorId = db.LoggedErrors.OrderByDescending(le => le.ErrorId).Select(le => le.ErrorId).FirstOrDefault();
Server.Transfer("~/Error.aspx?ErrorID=" + errorId);
}
}
catch (Exception)
{
}
}
As you have not posted much code. So without seeing the actual implementation you have done. I could suggest you below points.
Point 1. First of all, you need to check if SessionState is enabled for pages. You could set them globally in web.config file. Try the snippet given below in web.config
<configuration>
<system.web>
<pages enableSessionState="true" />
</system.web>
</configuration>
Point 2. And put your Redirection in Application_Error in Global.asax.
public void Application_Error(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
app.Server.Transfer("~/Error.aspx?ErrorID=" + errorId,true);
}
Point 3. Also check if your SessionStateis set properly in IIS too.
Details are on MSDN to enable sessionstate
Hope this helps..!!!
From what I understand, Server.Transfer sends the content of another page to the client rather than the requested content. If that is the case, then I am wondering if it does not have something to do with applying a master page to the error page? I had a similar error years ago with earlier technology and it turned out that the master page did not like what I was trying to do.
I hope this helps at least point to a solution.
Here's what the problem is:
If there is a page render exception (ex. "File Not Found") then Server.Transfer screws up the session. This has something to do with it being called during the page render.
As long as you are not appending headers before the error occurs, Response.Redirect will work just fine; if you are, however, using Response.AppendHeader then Response.Redirect will not work during a page render.
Try using HttpContext.Current.RewritePath instead. That should fix all these problems. For whatever reason, RewritePath() does not care that the page hasn't finished rendering.
why not just use customErrors in web.config to do the redirect?
<customErrors mode="Off" defaultRedirect="~/Common/Error.aspx">
<error statusCode="403" redirect="~/SM_AccessDenied.aspx" />
<error statusCode="404" redirect="~/Common/FileNotFound.aspx" />
</customErrors>
I had the same problem in a different context a while ago. I don't know if it is your case, but if you're using IIS7 on Windows 2008, in addition to setting enableSessionState=true in your web.config, you have to put your modules inside the <system.webServer> section, instead of <system.web>. Changing this little thing solved it for me.
Why don't you try like this:
The Server.Transfer method also has a second parameter—"preserveForm". If you set this to True, using a statement such as Server.Transfer("WebForm2.aspx", True), the existing query string and any form variables will still be available to the page you are transferring to.
So I think doing like this your session will not expire.
Server.Transfer("~/Error.aspx?ErrorID=" + errorId,True);
The error you are encountering is because you are using a query string parameter. Per the msdn docs
However, the path parameter must not contain a query string, or ASP returns an error.
http://msdn.microsoft.com/en-us/library/ms525800%28v=vs.90%29.aspx
Its about 3/4 of the way down the page just above Requirements.
Even though the docs here are mentioning asp. and not asp.net, keep in mind the session state is a feature of IIS and is handled before asp.net is ever called.
#user2110845 : I had faced similar problem few months ago. the problem was with having an underscore in the website name. We were deploying a website in IIS with two different host names(adding two entries through the 'Edit Bindings' option on the website). The host names provided were abc_ts, abc_is. When the underscore was removed then the session problem got resolved.
It seems there are certain characters not allowed in a website host name. Check if that is your problem.
I found the answer here : link (check 'update 2' in the article)
You don't mention what version of ASP.NET you are using, but there were some changes between 2.0 and 3.5 in how unhandled exceptions bubbled their way up through an ASP.NET web app and then IIS.
Among some other possibles, while you are clearing the error you are not setting Context.Response.TrySkipIisCustomErrors = true; While this particular flag could have nothing to do with your issue (and is only available for 3.5+), it also could help deal with what is potentially two error pages behind the scenes that are obscuring the real issue. Regardless, it'll save you a lot of grief (at least if you are running 3.5+) with other potential issues. Check out two posts I wrote several years back that may be helpful: while they don't cover session handling, they do cover the multiple song-and-dance routines I had to follow to get proper 500 and 404 handling in various versions of ASP.NET. It's possible you will run into something that will get you further ahead, if not all the way there.
http://www.andornot.com/blog/post/Errors-Sending-the-Right-Message-(Redux-Covering-ASPNET-3540).aspx
http://www.andornot.com/blog/post/Errors-Sending-the-Right-Message.aspx

Forcing SSL (https) on a page by page basis

How can I set up my page load event in my code behind to redirect to an url that has an https prefix? I would need it to work with urls that have query strings attached too.
It's one thing to construct a link that goes straight to the https page, but I don't want the user to be able to manually change it to an http page.
Also I don't want to do it with javascript because it might be turned off.
I'm guessing a regular expression?
We mark our SSL Required pages with a special attribute ForceSslAttribute. Then we have a HttpModule that pulls down the current page's class and inspect it's attributes.
If the attribute is present on the page, it takes the exact url that was passed and changes the protocol from http to https then calls a redirect.
There's probably a bit simpler way of doing it, but that's how it's done for us.
Attribute:
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public sealed class ForceSslAttribute : Attribute
{
// Marker Attribute
}
Page Example (CodeBehind):
[ForceSsl]
public partial class User_Login : Page
{
//...
}
You can figure out the type of the page like this:
HttpContext.Current.CurrentHandler.GetType()
All Page's implement IHttpHandler and when you're visiting a page, it'll work.
The cool part about this method is you can mark anything that's an IHttpHandler and it'll force the redirect too :)
Add this at the top of your Page_Load
if (Request.ServerVariables["HTTPS"] != "ON")
{
Response.Redirect("https://" + Request["HTTP_HOST"] + Request.RawUrl);
}
I use the following in Global.asax Application_BeginRequest
If needsSSL <> Request.IsSecureConnection Then
If needsSSL Then
Response.Redirect(Uri.UriSchemeHttps + Uri.SchemeDelimiter + Request.Url.Host + Request.Url.PathAndQuery, True)
Else
Response.Redirect(Uri.UriSchemeHttp + Uri.SchemeDelimiter + Request.Url.Host + Request.Url.PathAndQuery, True)
End If
End If
There is an IIS7 module for URL rewriting. Very handy, but you need access to the IIS and it requires some time to learn how to write the rules. A simple http->https rule is a matter of seconds.
Just be careful because any rules you add will be stored in your web.config, so don't delete/override it or you will have to write them again.
Forcing SSL using ASP
To force SSL using ASP, follow these steps:
Click Start, click Run, type Notepad, and then click OK.
Paste the following code into a blank Notepad document. On the File menu, click Save As, and then save the following code in the root of your Web server as an include file named ForceSSL.inc:
<%
If Request.ServerVariables("SERVER_PORT")=80 Then
Dim strSecureURL
strSecureURL = "https://"
strSecureURL = strSecureURL & Request.ServerVariables("SERVER_NAME")
strSecureURL = strSecureURL & Request.ServerVariables("URL")
Response.Redirect strSecureURL
End If
%>
For each page that requires SSL, paste the following code at the top of the page to reference the include file from the previous step:
<%#Language="VBSCRIPT"%>
<!--#include virtual="/ForceSSL.inc"-->
When each page is browsed, the ASP code that is contained in the include file detects the port to determine if HTTP is used. If HTTP is used, the browser will be redirected to the same page by using HTTPS.

CryptographicException: Padding is invalid and cannot be removed and Validation of viewstate MAC failed

Monitoring my global exception logs this error seems to be impossible to remove no matter what I do, I thought I finally got rid of it but it's back again. You can see a strack trace of the error on a similar post here.
Notes about the environment:
IIS 6.0, .NET 3.5 SP1 single server ASP.NET application
Steps already taken:
<system.web>
<machineKey validationKey="big encryption key"
decryptionKey="big decryption key"
validation="SHA1" decryption="AES" />
In my Page Base for all of my pages
protected override void OnInit(EventArgs e)
{
const string viewStateKey = "big key value";
Page.ViewStateUserKey = viewStateKey;
}
Also in the source of the page I can see that all of the ASP.NET generated hidden fields are correctly at the top of the page.
First of all lets start from the fact, that this error of view state happens on PostBack.
Also I must say that I have done all the things that every one suggest to do to avoid this problem. And I have single machine, but 2 pools that run the same Pages.
So someone do an action, ether a man, ether some other search machine by 'clicking' on your pages, or some hacker try to check your system for problems...
I have similar problems (rare but existing ones), and I finally found that people try to hack-test my pages. (from the same IP I have and dos attacks)
I modify the function LoadPageStateFromPersistenceMedium() that translate the viewstate, and see by logging what exactly was the input, and from what IPs... then I started monitor these results and see that the view state was changed by hand - or was totally empty.
On error I just redirect him to the same page...
Here is what I did...
public abstract class BasePage : System.Web.UI.Page
{
protected override object LoadPageStateFromPersistenceMedium()
{
try
{
.. return the base, or make here your decompress, or what ever...
return base.LoadPageStateFromPersistenceMedium();
}
catch (Exception x)
{
string vsString = Request.Form[__VIEWSTATE];
string cThePage = Request.RawUrl;
...log the x.ToString() error...
...log the vsString...
...log the ip coming from...
...log the cThePage...
// check by your self for local errors
Debug.Fail("Fail to load view state ! Reason:" + x.ToString());
}
// if reach here, then have fail, so I reload the page - maybe here you
// can place somthing like ?rnd=RandomNumber&ErrorId=1 and show a message
Responce.Redirect(Request.RawUrl, true);
// the return is not used after the redirect
return string.Empty;
}
}
Second Reason
Now there is one more reason why this can happen, and the reason is because some one click on your page before the __EVENTVALIDATION is loaded.
This eventValidation is placed on the last button-even that asp.net found, and if you have some of them on many place on the page, or near the button, then this go to the end of the page.
So even if you see the viewstate on the top of the page, where is the Validation ??? maybe this never loaded - page corrupt ?, too fast user click on page ?
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" ... >
To avoid this kind of problem I made a simple javascript that I do not let it press the button unless this input have been loaded !!!.
One more comment, the __EVENTVALIDATION is not always presents ! so is maybe safer not to search for this field if you make a general solution, but to make a javascript trick to just check if the full page is loaded, or something else that you think.
Here is my final solution with jQuery: (note that I check on PageLoad if eventvalidation exist !). I have this placed on my MasterPages.
<script language="javascript" type="text/javascript">
function AllowFormToRun()
{
var MyEventValidation = $("#__EVENTVALIDATION");
if(MyEventValidation.length == 0 || MyEventValidation.val() == ""){
alert("Please wait for the page to fully loaded.");
return false;
}
return true;
}
</script>
protected void Page_Load(object sender, EventArgs e)
{
// I do not know if Page can be null - just in case I place it.
if (Page != null && Page.EnableEventValidation)
{
Form.Attributes["onsubmit"] = "return AllowFormToRun();";
}
}
You can test by placing near the button of your page a delay.
<% System.Threading.Thread.Sleep(5000); %>
Update
Today I see in log this message again for WebResource and what I discover is that a bot getting the pages and make all the character on the links in lower case, including the parameters, so this was one more reason to not getting the correct encoded string, and throw a message like Padding is invalid and cannot be removed.
Hope this help you more.
A survey of the web pages found with several of the keywords from the error message indicate that this type of error is relatively common, usually random (at least in appearance) and unfortunately rarely inclusive of an explicit work-around or explanation...
The existence of many similar-yet-different situations is probably tied to the very many different architectures and underlying configurations which can somehow lead to the inability of the crypto layer to assert the authenticity of the MAC (Message Authentication Codes) in the request pages:
Server farm setup
Cross domain / syndicated pages
third party widget libraries and such
Actual ASP program logic (of course)
One relatively frequent "marker" around these bug reports is the mention of resource requests (eg. WebResource.axd).
Note that such requests are often not logged (lest they inflate the log files size with event of relative little interest). This absence from the log file and the fact they are often cached (and hence the relative random and infrequent occurrence of the bug) may explain how this possible origin of the bug go "under the radar". This also suggests that in trying to recreate the bug, (while tracking in the logs, in real time etc) it may be useful to prevent the web browser from caching (or for the least to clear it cache initially).
In short, here are a few ideas and things to look for:
start logging the *.axd requests
try and co-relate such axd requests with the error events in the exception log
look for pages with resource references
if in a Farm setting, ensure that all instances use the same key (apparently the snippet provided in the question hint at multiple IIS servers)
Be suspicious of pages with 3rd party tie-ins (search services, affiliate programs...)
Hope this helps ;-)
Are you sure your problem is cryptography related, and not caused by oversized ViewState?
If ViewState is the problem, you can chunk it - change the value of pages / MaxPageStateFieldLength in web.config

Categories

Resources