ASP.NET MVC: How to create a usable UrlHelper instance? - asp.net-mvc

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.

Related

MVC 5 UrlHelper without HTTPContext?

I decorate my MVC 5 actions with route attributes:
[Route("this-test")]
public ActionResult ThisTest()
and with a HTTPContext I can access the route name like so:
UrlHelper helper = new UrlHelper(System.Web.HttpContext.Current.Request.RequestContext);
string actionUrl = helper.Action("ThisTest", "Home");
However I'm using Hangfire chron jobs to fire off some emails. Since I don't have a HTTPContext I cannot use the code above to obtain my route of "home/this-test/".
Is this possible to achieve? Thank you.
Look at this topic:
Passing site URL to hangfire recurrent jobs.
Relying on the answer there, there's no way to receive the domain inside a Hangfire job. It seems like passing the URL of your site as a parameter to a job is the most clear solution.

RouteUrl in class

How can I generate a URL by RouteName outside of a view or controller?
I want generate a URL by RouteName in a seperate class for an e-mail, but if I try to use UrlHelper.GenerateUrl. Then I don't see where I can get rest of the parameters for this function.
How i can generate URL by RouteName outside view or conroler ?
You shouldn't be needing/doing this. Urls should be generated only in the front layers where you have access to an HTTP context and passed to backed layers as arguments.
Of course you could always perform some horrible grotesque hack in your backend layer and hardcode some static HttpContext.Current in order to instantiate an UrlHelper and thus render your backed layers strongly coupled to the HTTP stack, making it impossible to be reused in isolation and unit tested.
Oh and by the way checkout MvcMailer if you need to send emails in your application. This way you can define the email body as templates where you will have access to helpers and stuff and won't need to perform the aformentioned grotesque hack.

Is it possible to get the controller and the action (NOT THEIR NAME!!) based on the url?

I have found a dozens of threads about getting the name of the controller and method based on the url, I managed that just as well. Can I get the MethodInfo of the method based on their name automatically from the MVC engine, or do I have to do Type.GetType("Namespace.Controllers."+cname+"Controller").GetMethod(mname)? Which is not so nice, since how do I know the namespace in a framework class? How do I know if the default naming patterns are being observed, or is there a different config in use?
I want to get a "What Would MVC execute?" kind of result....
Is it possible?
EDIT: further info:
I have a framework which uses translatable, data-driven urls, and has a custom url rewriting in place. Now it works perfectly when I want to show the url of a news object, I just write #Url.Content("~/"+#Model.Link), and it displays "SomeNewsCategory/SomeNews" instead of "News/29" in the url, without the need to change the RouteTable.Routes dynamically. However in turn there is a problem when I try to write RedirectToAction("SomeStaticPage","Contact"); into a controller. For that, I need to register a static link in db, have it target "/SomeStaticPage/Contact", and then write
Redirect("~/"+DB.Load(linkid).Link); and that's just not nice, when I have 30 of these IDs. The web programmer guy in the team started registering "fake urls", which looked like this:
public class FakeURL
{
public string Controller;
public string Action;
public int LinkID;
}
and then he used it like Redirect(GetFakeUrl("controller","action")); which did the trick, but still was not nice. Now it got me thinking, if I apply a [Link(linkid)] attribute to each statically linked method, then override the RedirectToAction method in the base controller, and when he writes ReturnToAction("action","controller"), I'll actually look up the attribute, load the url, etc. But I'm yet to find a way to get the methodInfo based on the names of the controller and the action, or their url.
EDIT: I've written the reflection by myself, the only thing missing is getting my application's assembly from inside a razor helper, because the CallingAssembly is the dinamically compiled assembly of the .cshtml, not my WebApplication. Can I somehow get that?
To answer your edit, you can write typeof(SomeType).Assembly, where SomeType is any type defined in code in the project (eg, MvcApplication, or any model or controller)
Also, you can write ControllerContext.Controller.GetType() (or ViewContext) to get the controller type of the current request EDIT That's not what you're trying to do.
I found out that it was totally wrong approach. I tried to find the type of the controller based on the name, when instead I had the type all along.
So instead of #Url.Action("SomeAction","SomeController") I'll use #Url.MyAction((SomeController c)=>c.SomeAction()), so I won't even have to find the controller.

ASP.NET MVC Get Route values from a URL

I want to work out what the route values for the UrlReferrer in the controller action would be.
I can't figure out at what part in the MVC pipeline the incoming URL is converted into RouteValues, what I'm trying to achieve is close to that.
You need call RouteTable.Routes.GetRouteData with a mocked HttpContextBase which returns your URL in its Request.
The routes are matched internally using the request's AppRelativeCurrentExecutionFilePath.
However, this functionality is not exposed, so you need to pass an HttpContextBase.
You need to create an HttpContextBase class which returns an HttpRequestBase instance in its request property.
The HttpRequestBase class needs to return your path, beginning with ~/, in its AppRelativeCurrentExecutionFilePath property.
You don't need to implement any other properties, unless they're used by IRouteConstraints.
Someone already wrote this: Creating a RouteData instance from a URL

Generating a client URL in a Timer running in global.asax

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.

Resources