Generating a client URL in a Timer running in global.asax - asp.net-mvc

I have an MVC 2 app that has a System.Timers.Timer object starting up during Application_Start in global.asax. One of the things this timer needs to do is generate a daily e-mail to users when they have an item to review in their work queue. Inside the e-mail is a link back to the item to review.
In code I have running in various controller actions, I use code like this to generate URLs that can be e-mailed to users:
UriBuilder builder = new UriBuilder(Request.Url);
builder.Path = Url.RouteUrl("ReviewDetails", new { id = reviewId });
return builder.ToString();
I would like to do the same inside my Timer's elapsed method in global.asax, but there is no HttpContext at that point, so I'm not able to programatically determine the URL.
I think the only solution is to store the site's root (ex: http://intranet.domain.com/MyApp) in the web.config and then use that to build the URL, like this:
string url = string.Concat(ConfigurationManager.AppSettings["SiteRoot"], "/Review/", reviewId.ToString());
I'm putting this out in the cloud to see if there are any better solutions to programatically genererating a URL for the client when the HttpContext is not available.

You can capture the url in the Application_Start method and store it in the ApplicationState collection.

There is always an HttpContext. Try the HttpContext.Current static method.

Related

Detect QueryString and create Session on any Request URL

Currently, I'm detecting a QueryString and creating the session on the Action of the Controller. The query string can be coming in with the url to any deep page within the site. I don't want to code it in most of my Actions.
Therefore, I'm wondering, is there a generic way to detect QueryString in the URL in any request that comes to site's pages. I think I can create a 'BaseController' from the 'Controller' and change all my current Controller's to inherit from BaseController.
Is there any other ways? may be in RouteConfig.cs? Startup.Auth.cs?
(.Net Core) You can craete custom middleware to detect any request using Httpcontext.
read more about middleware
. example of custom middleware
Thanks KHAL, for your hint.
so in classic ASP.NET MVC, in Startup.Auth.cs file
app.Use(async (context, next) =>
{
var referral = context.Request.Query["referral"];
if(referral == "whatever")
{
//Do your thing ;)
}
// Call the next delegate/middleware in the pipeline
await next();
});
However, note that the position of this inside the Startup.Auth.cs file is critical, if you want to use Session. It cannot be at the top of the file

How do I call a specific Controller Action to test code within it, especially session variables?

I am using MVC5 on ASP.NET 4.5
I want to debug specific Controller Actions without running the whole application where I would need to know how to navigate to the specific Action, in addition to it taking much time, since I have much code to refactor and test.
I have considered Unit Tests, but using the debugger seems more appropriate for what I am doing.
Also I have session variables within the Action Code.
The closest I have come to a solution is to have a wrapper Action in Home ie:
public ActionResult Tx()
{
return new MyApp.Areas.Area1.Controllers.OrderController().Index();
}
I would then copy and paste the relevant Namespace, Controller and Action with any relevant parms into the "Tx" Action.
This does invoke the relevant Action without me needing to navigate to the specific Action. I just call Home/Tx. However I am getting null exception errors with my Session variable code:
Session["OrderID"]=null
Debugging the Action, with the Session variables works fine when I navigate directly from the application. When I use Home/Tx it is still on the Dev Web Server and within the Debug process, but perhaps I am in a new context as another Action is creating the Controller.
1) Any idea why my idea does not work with Session variables?
2) If anybody has a better idea on how to invoke specific Controller Actions then I would appreciate that.
Thanks.
You need to call the Initialize method on your controller before using it. Try something like:
var httpContext = new HttpContextWrapper(HttpContext.Current);
var routeData = RouteTable.Routes.GetRouteData(httpContext);
var controller = new MyApp.Areas.Area1.Controllers.OrderController();
controller.Initialize(new RequestContext(httpContext, routeData));
return controller.Index();

Dynamic url rewriting with MVC and ASP.Net Core

I am re-writing my FragSwapper.com website (currently in Asp 2.0!) with ASP.Net Core and MVC 6 and I'm trying to do something I would normally have to break out a URL Re-Write tool along with db code and some Redirects but I want to know if there is a "better" way to do it in ASP.Net Core possibly with MVC Routing and Redirecting.
Here are my scenarios...
URL Accessing the site: [root]
What to do: Go to the usual [Home] Controller and [Index] View (no [ID]). ...it does this now:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
URL Accessing the site: [root]/ControllerName/...yada yada...
What to do: Go to the Controller, etc...all THIS works too.
The tricky one: URL Accessing the site: [root]/SomeString
What to do: Access the database and do some logic to decide if I find an Event ID. If I do I go to [Event] Controller and [Index] View and an [ID] of whatever I found. If not I try to find a Host ID and go to [Home] Controller and [Organization] View with THAT [ID] I found. If I don't find and Event or Host go to the usual [Home] Controller and [Index] View (no [ID]).
The big gotcha here is that I want to Redirect to one of three completely different views in 2 different controllers.
So the bottom line is I want to do some logic when the user comes to my site's root and has a single "/Something" on it and that logic is database driven.
If you understand the question you can stop reading now...If you feel the need to understand why all this logic is needed you can read on for a more detailed context.
My site has basically two modes: Viewing an Event and Not Viewing an
Event! There are usually 4 or 5 events running at an one time but
most users are only interested in one event but it's a DIFFERENT event
every 4 months or so..I have a [Host] entity and each Host holds up to
4 events a year, one at a time. Most users only care about one Host's
events.
I'm trying to avoid making a user always go to an event map and find
the event and click on it since I have a limit to how many times I can
show a map (for free) and it's really not necessary. 99.99% of the
time a user is on my site they are on an Event screen, not my Home
screens, and are interested in only one event at a time. In the
future I want to code it so if they come to my website they go right
to their event or a new event from their favorite host so I can avoid
a LOT of clicks and focus my [Home] controller pages for newbs...but I
don't have auto-login working yet so that's on the back burner.
But for now I want hosts to always have the same url for their events:
FragSwapper.com/[Host Abbreviation] ...and know it will always go to
their current event which has a different ID every 4 months!!!
Crazy...I know...but technically very easy to do, I just don't know
how to do it properly in MVC with how things are done.
Update: ASP.Net Core 1.1
According to the release notes, a new RewriteMiddleware has been created.
This provides several different predefined rewrite options and utility extension methods, which might end up modifying the request path as it has been done in this answer. See for example the implementation of RewriteRule
Specifically to the OP question, you would need to implement your own IRule class (either from scratch or extending an existing one like RewriteRule, which is based on a regex). You would possibly complement it with a new AddMyRule() extension method for RewriteOptions.
You can create your own middleware and add it to the request pipeline before the MVC routing.
This allows you to inject your code into the pipeline before the MVC routes are evaluated. This way you will be able to:
Inspecting the path in the incoming request
Search in the database for an eventId or hostId with the same value
If event or host were found, update the incoming request path to Event/Index/{eventId} or Home/Organization/{hostId}
Let the next middleware (MVC routing) take care of the request. They would see any changes to the request path made by the previous middleware
For example, create your own EventIdUrlRewritingMiddleware middleware that will try to match the incoming request path against an eventId in the database. If matched, it will change the original request path to Event/Index/{eventId}:
public class EventIdUrlRewritingMiddleware
{
private readonly RequestDelegate _next;
//Your constructor will have the dependencies needed for database access
public EventIdUrlRewritingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var path = context.Request.Path.ToUriComponent();
if (PathIsEventId(path))
{
//If is an eventId, change the request path to be "Event/Index/{path}" so it is handled by the event controller, index action
context.Request.Path = "/Event/Index" + path;
}
//Let the next middleware (MVC routing) handle the request
//In case the path was updated, the MVC routing will see the updated path
await _next.Invoke(context);
}
private bool PathIsEventId(string path)
{
//The real midleware will try to find an event in the database that matches the current path
//In this example I am just using some hardcoded string
if (path == "/someEventId")
{
return true;
}
return false;
}
}
Then create another class HostIdUrlRewritingMiddleware following the same approach.
Finally add your new middlewares to the pipeline in the Startup.Configure method, making sure they are added before the Routing and MVC middleware:
app.UseMiddleware<EventIdUrlRewritingMiddleware>();
app.UseMiddleware<HostIdUrlRewritingMiddleware>();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
With this configuration:
/ goes to the HomeController.Index action
/Home/About goes to the HomeController.About action
/Event/Index/1 goes to the EventController.Index action id=1
/someEventId goes to the EventController.Index action, id=someEventId
Please note there are no http redirects involved. When opening /someEventId in the browser there is a single http request and the browser will show /someEventId in the addess bar. (Even if internally the original path was updated)

ASP.NET MVC: How to create a usable UrlHelper instance?

I am using quartz.net to schedule regular events within asp.net mvc application.
The scheduled job should call a service layer script that requires a UrlHelper instance (for creating Urls based on correct routes (via urlHelper.Action(..)) contained in emails that will be sent by the service).
I do not want to hardcode the links into the emails - they should be resolved using the urlhelper.
The job:
public class EvaluateRequestsJob : Quartz.IJob
{
public void Execute(JobExecutionContext context)
{
// where to get a usable urlHelper instance?
ServiceFactory.GetRequestService(urlHelper).RunEvaluation();
}
}
Please note that this is not run within the MVC pipeline. There is no current request being served, the code is run by the Quartz scheduler at defined times.
How do I get a UrlHelper instance usable on the indicated place?
If it is not possible to construct a UrlHelper, the other option I see is to make the job "self-call" a controller action by doing a HTTP request - while executing the action I will of course have a UrlHelper instance available - but this seems a little bit hacky to me.
How about just creating a new HttpContext for the UrlHelpler as in this answer:
Edit: Sorry I totally mis-read the question I guess.
It sounds like your scheduler (which I have no idea how it works) is a seperate process and you want the UrlHelper to help generate valid URLs in your MVC app?
You could try writing a handler in your MVC app that will be running under your applications context that will build the URL for you and return it. You could then call the handler from your scheduler to get any URL you need based on the params you pass in. This way your scheduler just needs to know about where the query URL of your MVC app is and then can ask it to do the Url mapping for you.
Hope this is a bit better of an answer. If I am totally off let me know... was going to delete my response but thought I would give it one more shot.
Remember to specify the protocol parameter when using UrlHelper.Action method, this will generate absolute urls. Example:
url.Action("Action", "Controller", null, "http")
or
url.Action("Action", "Controller", null, request.Url.Scheme)
You need a RequestContext to create a UrlHelper. In one of my HtmlHelper extension methods, I do it like this:
public static string ScriptUrl(this HtmlHelper html, string script)
{
var url = new UrlHelper(html.ViewContext.RequestContext);
...
}
How you get the RequestContext is dependent on your application.

ASP.NET MVC Action Method Parameters from Querystring don't change after first request

I have an action method in a controller that needs to do paging. I am passing a page number and pagesize parameter in the querystring. The problem I am having is that the first request I make sets the parameters for all subsequent calls.
public ActionResult GetStuff(string key, int? page, int? pageSize)
{
// do something magical
}
My route looks like this:
routes.MapRoute("GetStuff", "Stuff/{key}", new {controller = "Stuff", action = "GetStuff"});
When I start debugging my app, I go to the url /Stuff/My_Stuff and the key parameter is correct, and both page and pagesize are null as I would expect. If I make a second call with the url /Stuff/My_Stuff?page=2&pageSize=3 then the values of page and pageSize are still null. If I restart the app, and make my first call include page and pagesize parameters, everything works as I would expect, but then changing those values on subsequent calls retains the values from the first call. In fact, even the key parameter, which is part of my route will keep the same value, even if I change my Url. What am I missing?
I am using IIS 6.1 on Windows Server 2003. I am using extensionless routes. Also, the actual code is in VB.Net, but I don't think that should matter. But for full disclosure, the above code is only representative of my actual code, and not the actual code.
I had the same problem because I used a DI Container (Castle Windsor) to create my Controllers. The problem arose because of the lifetime settings of Controller classes, because the default lifetime policy in Castle is Singleton (a weird default if you ask me).
It seems that because the Controller instance is only created once in the application's lifetime, the parameters get stuck on their first values.
Setting the lifetime to Transient solved the problem in my case.

Resources