Azure App Service Set Custom ConnectionTimeout in ApplicationHost.config - asp.net-mvc

On our ASP.NET MVC website hosted on Azure App Service we'd like to force a timeout for requests that take longer than 15 seconds. Here's a simple action that always takes longer than 15 seconds (i.e. infinite loop) that we've been testing with...
public ActionResult TimeoutTest()
{
var i = 1;
while (true)
{
i++;
}
return new HttpStatusCodeResult(200);
}
By default, if I do a GET on that action in a browser, I'll get a "500 - The request timed out" error after two minutes, which agrees with the "connectionTimeout" default setting in the webLimits section of ApplicationHost.config.
So...unless I'm mistaken, it should be enough to change this connectionTimeout value to 15 seconds. To do this, I understand one needs to use a transform-based approach for the ApplicationHost.config file (XDT), as explained here.
I did this with the following applicationHost.xdt file...
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.applicationHost>
<webLimits xdt:Transform="SetAttributes(connectionTimeout)" connectionTimeout="00:00:15"/>
</system.applicationHost>
</configuration>
...after which I added the file in the right place (d:/home/site/applicationHost.xdt). I restarted my site and saw in the logs that the transform was successfully applied:
2016-04-20T08:40:44 Start 'site' site extension transform
2016-04-20T08:40:44 StartSection Executing SetAttributes (transform line 4, 18)
2016-04-20T08:40:44 on /configuration/system.applicationHost/webLimits
2016-04-20T08:40:44 Applying to 'webLimits' element (no source line info)
2016-04-20T08:40:44 Set 'connectionTimeout' attribute
2016-04-20T08:40:44 Set 1 attributes
2016-04-20T08:40:44 EndSection Done executing SetAttributes
2016-04-20T08:40:44 Successful 'D:\home\site\applicationHost.xdt' site extension transform
2016-04-20T08:40:44 sandboxproc.exe complete successfully. Ellapsed = 316.00 ms
[Edit]: I have also checked the applicationhost.config directly after the transform, and the new value is there:
...
</sites>
<webLimits connectionTimeout="00:00:15" />
</system.applicationHost>
<system.webServer>
<asp>
...
Despite all this, if I hit my action method above again, it still times out after two minutes instead of 15 seconds.
My question: Does anybody know why this timeout setting isn't being respected?
I'm aware of this post, which looks like it took exactly the same approach (and seemed to work?).

What about using executionTimeout instead, through the Web.Config?
<system.web>
<httpRuntime executionTimeout="30" />
<compilation debug="false" />
</system.web>
For a MVC project, you should add the following code to force the value to be applied to the request:
System.Web.HttpContext.Current.GetType().GetField("_timeoutState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(System.Web.HttpContext.Current, 1);
If you want every request to respect this setting, you can create an action filter:
public class Timeoutter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
System.Web.HttpContext.Current.GetType().GetField("_timeoutState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(System.Web.HttpContext.Current, 1);
base.OnActionExecuting(filterContext);
}
}
Add register it in the RegisterGlobalFilters method called in the Global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new Timeoutter());
filters.Add(new HandleErrorAttribute());
}
Please see:
https://msdn.microsoft.com/en-us/library/e1f13641(v=vs.85).aspx
http://forums.asp.net/p/1715081/4723882.aspx?Re+web+config+executionTimeout+not+working+in+ASP+NET+MVC
.NET Execution Timeout Not Taking Effect in an MVC Web Project

Related

Can't create Session in Asp MVC

I have an ASP.NET MVC Application which is published on 2 IIS web servers (one for debug and one for release).
In this application, I use a Session variable to keep the user Login, but in the release server this Session definition didn't work, returning the error:
object reference not set to an instance of an object
I believe that it can only be IIS configurations, because the same code works fine on the debug server and in localhost, that is why I can not debug the application.
I have never made a IIS configuration before, but in my search about it, I found that there is a config for the Sessions State (see this link here), which, for default, is set as true, and if I try to set false, nothing different happens.
Here's the code:
// POST: Auth/Login/{login}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(DTOViewModel login)
{
try
{
this.Autenticar(login.Usuario, login.Password);
Session["user"] = login.Usuario; // <---- Here is the problem
return Json(new { url = Url.Action("Index", "User/Home") });
}
catch (Exception ex)
{
return Json(new { erro = ex.Message });
}
}
Solution 1:
Check the maximum worker process associated with the application pool. It should be 1.
Steps to check the maximum worker process:
Go to the application pool from left panel in IIS
Select application pool which is associated with your website.
Go to the advance setting of that application pool
Set the Process Model --> "maximum worker process" to 1
Solution 2:
Change the below setting in web.config file:
<configuration>
<system.webServer>
<modules>
<remove name="Session" />
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
</modules>
</system.webServer>
</configuration>

Creating an endpoint for keeping the session alive IIS 7.5

At my work we have a standard timeout script that waits for 15 mins and prompts a user asking if they want to continue the session. Is there anyway within IIS to create a dummy endpoint that the script can be changed to call to refresh the session. For all our applications we use the same script so I want it to be the same end point for each application. I was leaning toward creating an HTTP handler to do this, however I wasn't sure if this was enough. I'm looking to emulate the approach taken here.
Keeping ASP.NET Session Open / Alive
public class SessionKeepAliveHttpHandler : IHttpHandler, IRequiresSessionState
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
//Give it some long name it's no one is likely to have used
context.Session["Global_Session_KeepAlive"] = System.DateTime.Now;
}
and registering the handler like this
<httpHandlers>
<add verb="POST" path="keep-alive.html" validate="false" type="SessionKeepAliveHttpHandler"/>
</httpHandlers>
I'm also not 100% sure about where to register the handler, reading online here it looks like I'd put it in the Machine.config. However I wasn't sure as I don't have direct access to the ISS server.
By default our session timeout script is setup to make a post request to just "keep-alive.html"

Why doesn't my action method time out?

I have the following controllers:
[TimeoutFilter]
public abstract class BaseController: Controller
{
}
public class IntegrationTestController : BaseController
{
[HttpGet]
public ActionResult TimeoutSeconds()
{
return Content(HttpContext.Server.ScriptTimeout.ToString(CultureInfo.InvariantCulture));
}
[HttpGet]
public ActionResult ForceTimeout()
{
var timeoutWindow = TimeoutFilter.TimeoutSeconds;
Thread.Sleep((timeoutWindow + 5) * 1000);
return Content("This should never get returned, mwahahaaa!");
}
}
For my test scenario I use a config setting of 5 seconds in the TimeoutFilter, and I know this is working because when my test calls TimeoutSeconds, I get the correct value of 5, but when the test calls ForceTimeout, I get an HTTP response of 200 and my 'never returned' text.
And the filter:
public class TimeoutFilter : ActionFilterAttribute
{
internal const string TimeoutSecondsSettingsKey = "MvcActionTimeoutSeconds";
internal static int TimeoutSeconds;
public TimeoutFilter()
{
TimeoutSeconds = int.Parse(ConfigurationManager.AppSettings[TimeoutSecondsSettingsKey]);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ControllerContext.HttpContext.Server.ScriptTimeout = TimeoutSeconds;
base.OnActionExecuting(filterContext);
}
}
I could not get this to work by setting the ScriptTimeout property either, even when setting the debug="false" in the web.config as suggested by user Erik Funkenbusch:
<configuration>
<system.web>
<compilation debug="false" targetFramework="4.5"/>
...
It continued to return the text "This should never get returned" rather than timing out during the Thread.Sleep.
It is also worth noting that I also extended the Thread.Sleep to well beyond the Server.ScriptTimeout default of 110 seconds but it still eventually returned the same text rather than timing out.
I then instead tried to set executionTimeout in the web.config:
<configuration>
<system.web>
<httpRuntime targetFramework="4.5" executionTimeout="5"/>
...
If you were to now add a breakpoint to the TimeoutFilter you will observe that the ScriptTimeout value has been set to the executionTimeout value from web.config. Alas, this still did not work for me (regardless of whether debug="false").
I then came across this link about ScriptTimeout and executionTimeout not working with a similar setup to what you describe. The first reply in the post describes using the debug="false" and also mentions that the timeout will have a delay of 5 to 15 seconds. I still had no luck even when using a large Thread.Sleep value. The second reply in this article suggests that the executionTimeout config setting is a replacement of the ScriptTimeout property (which is apparently a COM interface used in classic ASP). This reply suggests that is not possible to set a specific timeout without using your own time-out logic.
Further, I then came across the following (more recent) link where the first reply suggests that the timeout is switched off in MVC. A further reply suggests that this is because an MVCHandler (which selects the controller that will handle the HTTPRequest) is an IHttpAsyncHandler and as it may therefore be executing another request (which is the point of using an async request) it therefore internally switches the time-out state off (this is what I gather from reading this link). It must work with straight asp.net though as using ScriptTimeout seems to be the accepted way in this answer.
The link suggests that adding the following line will allow it to work (but not in a medium trust application):
System.Web.HttpContext.Current.GetType().GetField("_timeoutState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(System.Web.HttpContext.Current, 1);
Therefore, changing the TimeoutFilter OnActionExecuting() to:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
System.Web.HttpContext.Current.GetType().GetField("_timeoutState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(System.Web.HttpContext.Current, 1);
base.OnActionExecuting(filterContext);
}
and setting the web.config:
<configuration>
<system.web>
<compilation debug="false" targetFramework="4.5"/>
<httpRuntime targetFramework="4.5" executionTimeout="5"/>
...
allows the time-out to work but has the slight 5 second delay that is mentioned in the first post.
Note: Using this method does not allow you to set the ScriptTimeout property in the filter. Trying to set ScriptTimeout and override the value set in web.config does not appear to work.
Are you using a debug build?
From the ScriptTimeout documentation:
http://msdn.microsoft.com/en-us/library/system.web.httpserverutility.scripttimeout(v=vs.110).aspx
If you set the debug attribute of the compilation element to true in the Web.config file, the value of ScriptTimeout will be ignored.
Also, since this value is the same as that set on the httpRuntime element, I really don't understand the point of this, since you can just configure that setting in in your web.config instead.
Edit:
Dangerous has done a good job of finding out the details, and indeed, ScriptTimeout is unsupported in asynchronous pipelines (which MVC has been since at least MVC4 I think, as well as WebApi.. even if not using async methods)
The "workaround" as suggested by this connect report:
https://connect.microsoft.com/VisualStudio/feedback/details/781171/asp-net-mvc-executiontimeout-does-not-work
Use the [AsyncTimeout] attribute, and take a cancelation token as a parameter, then call CanclationToken.ThrowIfCancelationRequested periodically, or use the cancellation token in an async method.
Here's an example:
[AsyncTimeout(5000)]
public async Task<ContentResult> Index(CancellationToken ct)
{
await Task.Delay(10 * 1000, ct);
return Content("This should never get returned, mwahahaaa!");
}
This throws an OperationCanceled exception with a YSOD after 5 seconds. The bonus for this is that it works even in Debug mode ;)

All ASP.NET Web API controllers return 404

I'm trying to get an API Controller to work inside an ASP.NET MVC 4 web app. However, every request results in a 404 and I'm stumped. :/
I have the standard API controller route from the project template defined like:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
The registration is invoked in Global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// Register API routes
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
I have a basic API controller like this:
namespace Website.Controllers
{
public class FavoritesController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new [] { "first", "second" };
}
// PUT api/<controller>/5
public void Put(int id)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
Now, when I browse to localhost:59900/api/Favorites I expect the Get method to be invoked, but instead I get a 404 status code and the following response:
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost:59900/api/Favorites'.
</Message>
<MessageDetail>
No type was found that matches the controller named 'Favorites'.
</MessageDetail>
</Error>
Any help would be greatly appreciated, I'm losing my mind a little bit over here. :) Thanks!
One thing I ran into was having my configurations registered in the wrong order in my GLobal.asax file for instance:
Right Order:
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
Wrong Order:
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
WebApiConfig.Register(GlobalConfiguration.Configuration);
Just saying, this was my problem and changing the order is obvious, but sometimes overlooked and can cause much frustration.
Had essentially the same problem, solved in my case by adding:
<modules runAllManagedModulesForAllRequests="true" />
to the
<system.webServer>
</system.webServer>
section of web.config
I have been working on a problem similar to this and it took me ages to find the problem. It is not the solution for this particular post, but hopefully adding this will save someone some time trying to find the issue when they are searching for why they might be getting a 404 error for their controller.
Basically, I had spelt "Controller" wrong at the end of my class name. Simple as that!
Add following line
GlobalConfiguration.Configure(WebApiConfig.Register);
in Application_Start() function in Global.ascx.cs file.
I had the same problem, then I found out that I had duplicate api controller class names in other project and despite the fact that the "routePrefix" and namespace and project name were different but still they returned 404, I changed the class names and it worked.
Similar problem with an embarrassingly simple solution - make sure your API methods are public. Leaving off any method access modifier will return an HTTP 404 too.
Will return 404:
List<CustomerInvitation> GetInvitations(){
Will execute as expected:
public List<CustomerInvitation> GetInvitations(){
For reasons that aren't clear to me I had declared all of my Methods / Actions as static - apparently if you do this it doesn't work. So just drop the static off
[AllowAnonymous]
[Route()]
public static HttpResponseMessage Get()
{
return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
}
Became:-
[AllowAnonymous]
[Route()]
public HttpResponseMessage Get()
{
return new HttpResponseMessage(System.Net.HttpStatusCode.OK);
}
Create a Route attribute for your method.
example
[Route("api/Get")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
You can call like these http://localhost/api/Get
Had this problem. Had to uncheck Precompile during publishing.
I'm a bit stumped, not sure if this was due to an HTTP output caching issue.
Anyways, "all of a sudden it started working properly". :/ So, the example above worked without me adding or changing anything.
Guess the code just had to sit and cook overnight... :)
Thanks for helping, guys!
I'm going to add my solution here because I personally hate the ones which edit the web.config without explaining what is going on.
For me it was how the default Handler Mappings are set in IIS. To check this...
Open IIS Manager
Click on the root node of your server (usually the name of the server)
Open "Handler Mappings"
Under Actions in the right pane, click "View ordered list"
This is the order of handlers that process a request. If yours are like mine, the "ExtensionlessUrlHandler-*" handlers are all below the StaticFile handler. Well that isn't going to work because the StaticFile handler has a wildcard of * and will return a 404 before even getting to an extensionless controller.
So rearranging this and moving the "ExtensionlessUrlHandler-*" above the wildcard handlers of TRACE, OPTIONS and StaticFile will then have the Extensionless handler activated first and should allow your controllers, in any website running in the system, to respond correctly.
Note:
This is basically what happens when you remove and add the modules in the web.config but a single place to solve it for everything. And it doesn't require extra code!
Check that if your controller class has the [RoutePrefix("somepath")] attribute, that all controller methods also have a [Route()] attribute set.
I've run into this issue as well and was scratching my head for some time.
WebApiConfig.Register(GlobalConfiguration.Configuration);
Should be first in App_start event. I have tried it at last position in APP_start event, but that did not work.
I found this in a comment here: https://andrewlock.net/when-asp-net-core-cant-find-your-controller-debugging-application-parts/
Add the following to your Program.cs:
app.UseRouting();
app.UseEndpoints(endpoints =>
{
// DP: I don't know the purpose of this, but without it, all controllers report 404
endpoints.MapControllerRoute("default", "WTF is this");
});
For me this is all it took to make it work after hours of trying to find a solution. If this doesnt work for you, try adding
builder.Services.AddControllers();
Add this to <system.webServer> in your web.config:
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="TRACEVerbHandler"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>
Adding <modules runAllManagedModulesForAllRequests="true" /> also works but is not recommended due performance issues.
I have solved similar problem by attaching with debugger to application init. Just start webserver (for example, access localhost), attach to w3wp and see, if app initialization finished correctly. In my case there was exception, and controllers was not registered.
I had the same 404 issue and none of the up-voted solutions here worked. In my case I have a sub application with its own web.config and I had a clear tag inside the parent's httpModules web.config section. In IIS all of the parent's web.config settings applies to sub application.
<system.web>
<httpModules>
<clear/>
</httpModules>
</system.web>
The solution is to remove the 'clear' tag and possibly add inheritInChildApplications="false" in the parent's web.config. The inheritInChildApplications is for IIS to not apply the config settings to the sub application.
<location path="." inheritInChildApplications="false">
<system.web>
....
<system.web>
</location>
I have dozens of installations of my app with different clients which all worked fine and then this one just always returned 404 on all api calls. It turns out that when I created the application pool in IIS for this client it defaulted to .net framework 2.0 instead of 4.0 and I missed it. This caused the 404 error. Seems to me this should have been a 500 error. Very misleading Microsoft!
I had this problem: My Web API 2 project on .NET 4.7.2 was working as expected, then I changed the project properties to use a Specific Page path under the Web tab. When I ran it every time since, it was giving me a 404 error - it didn't even hit the controller.
Solution: I found the .vs hidden folder in my parent directory of my VS solution file (sometimes the same directory), and deleted it. When I opened my VS solution once more, cleaned it, and rebuilt it with the Rebuild option, it ran again. There was a problem with the cached files created by Visual Studio. When these were deleted, and the solution was rebuilt, the files were recreated.
If you manage the IIS and you are the one who have to create new site then check the "Application Pool" and be sure the CLR version must be selected. In my situation, it had been selected "No Managed Code". After changed to v4.0 it started to work.
i try above but not sucess all
add ms webhost and GlobalConfiguration.Configure(WebApiConfig.Register);

ASP.NET MVC 4 - Exception Handling Not Working

When an error occurs in my ASP.NET MVC 4 application, I would like to customize a view for the user depending on the type of error. For example, page not found or an exception has occurred (with some user-friendly details about the exception). I've checked other examples of how to do this on StackOverflow and other online sources, but none of the answers are working for me.
The basic [HandleError] attribute does not seem to be working in VS2012 with an MVC 4 application targeting .NET 4.5. Here is the code I have in my home controller:
[HandleError]
public ActionResult Index()
{
Response.TrySkipIisCustomErrors = true; //doesn't work with or without this
throw new NullReferenceException("Uh oh, something broke.");
}
It is just throwing an exception, and I would expect the default ~/Shared/Error.cshtml view to be returned because of the [HandleError] attribute, but all I get is an HTTP 500 Internal Server Error indicating that the page could not be displayed. I checked my web.config, and different configurations seem to be behaving weird. In the section, it currently contains:
<customErrors mode="On" />
(I've tried adding defaultRedirect and with customErrors mode="Off" as well but that didn't have any effect... neither the shared Error view or the CustomError view I have is being rendered. If I change customErrors mode to off, then I can see the exception details as expected, so it is throwing the "Uh oh, something broke" exception properly.
I've also tried adding an OnException handler to the HomeController, and although I can debug through and see that the OnException event is being raised, it doesn't make any difference:
protected override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext);
filterContext.ExceptionHandled = true;
if (filterContext == null)
{
filterContext.Result = View("CustomError");
return;
}
Exception e = filterContext.Exception;
// TODO: Log the exception here
ViewData["Exception"] = e; // pass the exception to the view
filterContext.Result = View("CustomError");
}
I have also tried changing [HandleError] to specify a view, but that doesn't seem to do anything either:
[HandleError(View="CustomError")]
Any help would be much appreciated. Please let me know if you need any more details.
I also went on a seamingly endless journey of reading SO answers and assorted blog postings trying to get custom error pages to work. Below is what finally worked for me.
The first step is to use IIS Express for debugging instead of the built-in Cassini web server to "guarantee" that the debug experience will mirror the live environment.
Create a controller to handle application errors and add an action for each custom error you will handle. The action should do any logging, set the status code, and return the view.
public class ErrorsController : Controller
{
// 404
[HttpGet]
public ActionResult NotFound()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View();
}
// I also have test actions so that I can verify it's working in production.
[HttpGet]
public ActionResult Throw404()
{
throw new HttpException((int)HttpStatusCode.NotFound, "demo");
}
}
Configure the customErrors section in web.config to redirect to your custom error actions.
<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="Errors/InternalServerError">
<error statusCode="400" redirect="Errors/BadRequest" />
<error statusCode="403" redirect="Errors/Forbidden" />
<error statusCode="404" redirect="Errors/NotFound" />
<error statusCode="500" redirect="Errors/InternalServerError" />
</customErrors>
Add the httpErrors section to system.webServer and set the errorMode to Detailed in web.config. Why this works is a mystery to me, but this was the key.
<system.webServer>
<httpErrors errorMode="Detailed" />
Add a catchall route last to the defined routes to direct 404s to the custom page.
// catchall for 404s
routes.MapRoute(
"Error",
"{*url}",
new {controller = "Errors", action = "NotFound"});
You may also want to create a custom HandleErrorAttribute and register it as a global filter to log 500 errors.
These steps worked for me in both development (IIS Express) and production (IIS7) environments. You need to change customErrors mode="On" to see the effect in development.
I seem to recall that you had to call the page from a non-localhost IP address (typically another computer). And it has to be an IIS based server, not the built-in development server (so IIS or IIS Express, but then you have to configure IIS Express for external access, which is a pain).
You can in fact debug it, you have to configure your local server on your debug computer to accept external requests, then call your local server from the remote server.
I faced a similar problem and lost sometime trying to work out what was going on. So just in case any others face a similar problem here is what my problem was.
The error page was trying to use my _Layout page. Just ensure that you Error.cshtml page has
#{
Layout = null;
}
Try adding the following attribute to the customErrors tag:
defaultRedirect="Error"
This explicitly defines to which view MVC should redirect the user when the error is thrown and no view is specified by default in the attribute. This will of course only work if Error.cshtml exists in the Shared views folder.

Resources