ASP.NET MVC Route URL in Controller Constructor - asp.net

I have a problem when trying to generate url in the controller constructor
private BreadCrumbItem breadCrumbItem;
public SummaryController()
{
this.breadCrumbItem = new BreadCrumbItem { Title = "Sommaire", Url = Url.RouteUrl(new { controller = "Summary", action = "Index" }) };
}
The problem is in Url.RouteUrl
Why i can't access this in the controller? Any way to fix this ?
Beacause otherwise i have to add the same code in all actions in this controller.
Thanks for help

If i understand your question right you want something like this:
public class SummaryController
{
public SummaryController()
{
}
private BreadCrumbItem _breadCrumbItem = null;
private BreadCrumbItem CrumbItem
{
get
{
if(_breadCrumbItem == null)
{
_breadCrumbItem = new BreadCrumbItem { Title = "Sommaire", Url = Url.RouteUrl(new { controller = "Summary", action = "Index" }) };
}
return _breadCrumbItem;
}
}
}
Now in each method you can simply use the CrumbItem and the first time it'll create the new BreadCrumItem and after that it'll just return you the created item each time it's called.

You can override the Initialize() method. The Url property is set once the base controller is initialized.
protected override void Initialize(RequestContext requestContext)
{
// the controller's UrlHelper is still null
base.Initialize(requestContext);
// the controller's UrlHelper is now ready to use!
var url = Url.RouteUrl(...);
}

You can access it in the controller but you can't access it in the constructor. This value isn't set at the time the constructor is called, obviously, because it it populated after the controller is created by the controller builder. Using the #middelpat solution to lazy load the property (creating it on first use in the current action) is a reasonable solution to the problem. The UrlHelper instance should be available in the controller at that point.

I think you need something like this answer:
https://stackoverflow.com/a/700357/637425
Quote:
If you just want to get the path to a certain action, use UrlHelper:
UrlHelper u = new UrlHelper(this.ControllerContext.RequestContext);
string url = u.Action("About", "Home", null);
if you want to create a
hyperlink:
string link = HtmlHelper.GenerateLink(this.ControllerContext.RequestContext,
System.Web.Routing.RouteTable.Routes, "My link", "Root", "About",
"Home", null, null);
Intellisense will give you the meaning of each of
the parameters.

Related

Access Session by ActionFilterAttribute in .NET CORE

I tried to access the session-value by using the ActionFilterAttribute in .NET CORE. Therefore i use the following code:
A normal .NET Core Controller which gets the annotation [SessionCheck].
[SessionCheck]
public class HomeController : Controller
{
}
These class is extended by the ActionFilterAttribute Class and overrides the OnActionExecuted Method, where i would like to access the session value. Is there a possibility? I could only access the Key but not the session value.
public class SessionCheck:ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
var session = context.HttpContext.Session;
//var mySessionValue = session["user"];
}
}
My main problem is, that i have a session value which describes where i have to route my request. If there is a special value in, i would like to route my request to Location A, otherwise to Location B.
Is there another possibility to do that?
I have same issue, you can do something like below:
filterContext.HttpContext.Session.GetString("user");
public class SessionCheck : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionExecutingContext)
{
if (actionExecutingContext.HttpContext.Session.GetString("user") == "User One")
{
actionExecutingContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "LocationA Controller",
action = "LocationA Action",
returnurl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetEncodedUrl(actionExecutingContext.HttpContext.Request)
}));
}
else
{
actionExecutingContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "LocationB Controller",
action = "LocationB Action",
returnurl = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetEncodedUrl(actionExecutingContext.HttpContext.Request)
}));
}
}
}
Write SessionCheck class as above you can set your special value by typing HttpContext.Session.SetString("user", "User One"); in where you want to set also you can use multiple if-elseif like the same, This is working in .Net Core 3.1.

Is there any way to set ActionName in QueryString in ASP.NET MVC 5?

We all know url in ASP.NET MVC always like example.com/controller/action?param1=value1&param2=value2
I'd like to know is there a way to put action name in query string just like example.com/controller?aciton=index&param1=value1&param2=value2(notice action=index in query string), and make ASP.NET MVC route the url to the corresponding controller and action. The name of the query name can be action or something else.
--- update 28th Sep ---
The actual situation is I have a form, inside the form is a table with radio button each column and some button (create, edit, delete etc.) above the table. These buttons go to different action in same controller.
As a result of search, I've got some solutions:
handle onsubmit via JavaScript and change the action property of form. Answer link
write a "route" method in controller to re-route the request. Answer link ( I think this is not a graceful solution )
write a custom attribute let ASP.NET MVC route to corresponding action base on query. This anwser and this answer ( Very close to my original idea, I am currently using this solution )
I'm the questioner and just several hours after I update the question, I finally figure out the solution. That is write a custom Route.
public class QueryActionRoute : Route
{
public QueryActionRoute(string url, object defaults) : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler()) { }
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeData = base.GetRouteData(httpContext);
var action = httpContext.Request["action"];
if (routeData != null)
{
if (!string.IsNullOrEmpty(action))
{
routeData.Values["action"] = action;
}
}
else
{
routeData = new RouteData()
{
Values = { {"action", "Index"} }
};
}
return routeData;
}
}
and replace the default route
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(
"Default",
new QueryActionRoute(
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }));
}
}
after that you can access an action via
example.com/home/about?param=value(you can emit action name if the action is "index") like before
or
example.com/home?action=about&param=value.
I's really helpful when you need to specify different buttons to go different actions in form. You can make it in this way:
<form action="#Url.Action("Index")">
<button name="action" value="Create">Create</button>
<button name="action" value="Details">Detail</button>
<button name="action" value="Edit">Edit</button>
<button name="action" value="Delete">Delete</button>
</form>

ASP.NET MVC Model binding from Querystring to Object and Request-response pattern

The model binding from HTTP POST params works great with the pattern Request-response, so i recieve in my controller the ViewModel that i need and i call the service layer with that object.
So everithing is encapsulated in the DTO ViewModel. If i want to add some other params i modify the object and not the method declaration.
I need to do the same thing if possible automatically with HTTP GET request (from QueryString)
so for example :
/Index/CountryName/PageNumber/1 binding to controller Index(string CountryName, int PageNumber)
i want it to bind to this controller to : Index(CountryQueryStringModel countryQueryStringModel)
class CountryQueryStringModel
{
public string CountryName,
public int PageNumber
}
With this approch, if i want to add for example a filter criteria i encapsulate it in the object CountryQueryStringModel
Thanks for help.
you are right Mark, the routing default model binder do it.
Here the solution i found
context.MapRoute(
null,
"hotel/{countryName}/Page/{pageNumber}",
new { controller = "ResultsCity", action = "Index"},
new[] { "California_Front.Areas.Hotel_FR.Controllers" }
context.MapRoute(
null,
"hotel/{countryName}/",
new { controller = "ResultsCity", action = "Index", PageNumber = 1 },
new[] { "California_Front.Areas.Hotel_FR.Controllers" }
the controller is like this
public ActionResult Index(CountryQueryStringModel CountryQueryStringModel)
{}
Thanks for help

How to gracefully handle DefaultControlFactory's CreateController

I have some links on my page for which the controllers are yet to be created. I have my custom controller factory like this:
public class MyControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
try
{
IController controller = base.CreateController(requestContext, controllerName);
HttpContext.Current.Items["Controller"] = controller;
return controller;
}
catch { return null; }
}
}
This works fine as long as it can find controller. However I throws yellow screen of death if I return null from my method. How can I gracefully handle this? I want to redirect to some page along with 404 nicely formatted message or maybe show some message such as "In Progress".
Did you try adding this?
[HandleError(ExceptionType = typeof(NullReferenceException), Order = 1, View = "NotFound")]
you will have to add a NotFound view to the shared views folder. Annotate your controller factory with this attribute.

Asp.net MVC 3 global querystring?

I'm building a generic web application for two business groups. The logo/banner needs to be changed based on the querystring. For example, if the url is http://foo.com/test?bg=a it shows the logo for business group a and if the url is http://foo.com/test?bg=b it shows the logo for business group b. This is not a problem if I only had one action. But I have many actions.
I could check the query string on all actions but there must be a nice way to do it. I have an perception that I need to do something with the routing stuff but just don't know how. Can anyone please let me know how to do it?
You can write a Custom Routing Handler and use routing to extract the querystring as a parameter, and pass into RouteData where it can be accessed anywhere.
public class RouteWithQueryStringValueHandler : MvcRouteHandler
{
private readonly string key;
public RouteWithQueryStringValueHandler(string key)
: base()
{
this.key = key;
}
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var request = requestContext.HttpContext.Request;
var qsValue = requestContext.HttpContext.Request[key];
var router = base.GetHttpHandler(requestContext);
requestContext.RouteData.DataTokens[key] = qsValue;
return router;
}
}
Register as follows:
routes.Add(new Route("{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional
}),
new RouteWithQueryStringValueHandler("bg")));
Get the logo for Routing data:
var logo = RouteData.DataTokens["bg"];
You could write a custom helper method which based on the query string parameter will append a given class name to some div. Then of course you would have different class definitions in your CSS file applying a background-image.
For example:
public static class HtmlExtensions
{
public static string BannerClass(this HtmlHelper html)
{
var bg = html.ViewContext.Controller.ValueProvider.GetValue("bg");
if (bg == null || string.IsNullOrEmpty(bg.AttemptedValue))
{
// no bg parameter => return a default class
return "default_banner";
}
if (string.Equals("a", bg.AttemptedValue))
{
return "banner_a";
}
else if (string.Equals("b", bg.AttemptedValue))
{
return "banner_b";
}
// unknown value for the bg parameter => return a default class
return "default_banner";
}
}
and then in your _Layout you could apply this class to some placeholder like a div or even the body:
<div class="#Html.BannerClass()">OK</div>
This way it will always be applied for all view in your application.
Now all that's left is to define your CSS rules for the different banners:
.default_banner {
background-image: url('../images/default_banner.png')
}
.banner_a {
background-image: url('../images/banner_a.png')
}
.banner_b {
background-image: url('../images/banner_b.png')
}
If you are using Razor (and I believe this does break the separation of responsibilities guideline) change the _ViewStart.cshtml to do it.
#{
if (/* Context.QueryString Params, not at my development box*/)
{
Layout = "~/Views/Shared/Layout-Group1.cshtml";
}
else
{
Layout = "~/Views/Shared/Layout-Group2.cshtml";
}
}
I prefer this route because it makes any future requests (layout + css + javascript) fairly easy because they can all be updated within the Layout.
Place some code in your master page(s) to make the decision as to which banner to display based on the query string. Ideally the code wouldn't be completely inline i.e. it'd be in a helper class.

Resources