asp.net mvc add parameter with each action view - asp.net-mvc-4

I have a site which have many action like article, blog, news, stories, myths, books, audio, video.
Now I want if i pass a query string in index action like
wwww.mysite.com/english
then every action must be have this parameter automatically like
wwww.mysite.com/article/englishwwww.mysite.com/blog/englishwwww.mysite.com/news/englishwwww.mysite.com/stories/englishwwww.mysite.com/myths/englishwwww.mysite.com/books/englishwwww.mysite.com/audio/englishwwww.mysite.com/video/english
Please help me and suggest a good way

Store the passed passed parameter in a session variable and then access on each action. Example:
public ActionResult Index(string lang)
{
Session["Language"]= lang;
return View();
}
And then fetch in other actions like:
public ActionResult News()
{
string lang= Session["Language"].ToString();
// Do something with the lang...
return View();
}

Related

Loding pages by posting parameters

The subject might not be clear since I couldn't find a better way to express it.
I am developing a web application using ASP.NET Core 6.0 with Razor Pages. Our previous application was an SPA using Ext JS where any call to server was returning only data and where I was also able to make any kind of call (GET/POST) to get the data.
For example, in the above picture from my old application, I make an ajax call with POST to get the list of periods when I open this page. I make a POST because I am sending the period type in my request payload. Sure I can pass these parameters in a GET request, however my other views have many criteria, so passing these criteria in the query string is not what I want. So, I decided to make it a standard to make my calls with POST method if there are any criteria payload, make GET request only when fething an entity with a simple key parameter (like Id) or GET any list that doesn't have any criteria.
Now, I am quite confused how to do same thing in my new ASP.NET Core Razor Pages web application. Normally, the menu items navigate to the page using link as below, which makes a GET request:
<a asp-area="System" asp-page="/ProfessionList">#AppLocalizer["Profession List"]</a>
<a asp-area="System" asp-page="/PeriodList">#AppLocalizer["Profession List"]</a>
In order to make a POST request, I replaced the menu item for period list as following which makes a POST request with a default periodType payload:
<a asp-area="System" asp-page="/ProfessionList">#AppLocalizer["Profession List"]</a>
<form asp-area="System" asp-page="/PeriodList" method="post">
<input type="hidden" name="periodType" value="1" hidden />
<button type="submit" >#AppLocalizer["Period List"]</button>
</form>
And the corresponding PeriodType.cshtml.cs file is as following:
[Authorize]
public class PeriodListModel: BaseEntityListPageModel<List<JsonPeriodEx>> {
public PeriodListModel(ILogger<BaseEntityListPageModel<List<JsonPeriodEx>>> logger, WebApi webApi) : base(logger, webApi) {
}
public IActionResult OnGet() {
PageData = JsonConvert.DeserializeObject<List<JsonPeriodEx>>(TempData["PageData"].ToString());
return Page();
}
public async Task<IActionResult> OnPostAsync(int periodType) {
var jsonResult = await _WebApi.DoPostAsync<List<JsonPeriodEx>>("/PeriodEx/GetList", new[] { new { Property = "periodType", Value = periodType } });
if (jsonResult.IsLoggedOut)
return RedirectToPage("/Login", new { area = "Account" });
if (jsonResult.Success) {
PageData = jsonResult.Data;
TempData["PageData"] = JsonConvert.SerializeObject(PageData);
return RedirectToPage("/PeriodList");
} else {
return RedirectToPage("/Error");
}
}
}
OnPostAsync successfully binds to the posted periodType parameter and gets the list of periods. Now, at the end of a successful call I want to follow the Post/Redirect/Get pattern and redirect to OnGet with the data from OnPostAsync, which is stored in TempData.
Now, according to the above scenario, is my approach, explained above, correct or should I implement it differently?
Thanks in advance
For these cases I would prefer TempData. Much easier and less code.
public async Task OnGet()
{
TempData["myParamToPass"] = 999;
...
}
public async Task OnPostReadData()
{
if (TempData.ContainsKey("myParamToPass"))
{
var myParamToPassValue = TempData.Peek("myParamToPass") as int?;
...
}
...
}

Route to allow a parameter from both query string and default {id} template

I have an action in my ASP.Net Core WebAPI Controller which takes one parameter. I'm trying to configure it to be able to call it in following forms:
api/{controller}/{action}/{id}
api/{controller}/{action}?id={id}
I can't seem to get the routing right, as I can only make one form to be recognized. The (simplified) action signature looks like this: public ActionResult<string> Get(Guid id). These are the routes I've tried:
[HttpGet("Get")] -- mapped to api/MyController/Get?id=...
[HttpGet("Get/{id}")] -- mapped to api/MyController/Get/...
both of them -- mapped to api/MyController/Get/...
How can I configure my action to be called using both URL forms?
if you want to use route templates
you can provide one in Startup.cs Configure Method Like This:
app.UseMvc(o =>
{
o.MapRoute("main", "{controller}/{action}/{id?}");
});
now you can use both of request addresses.
If you want to use the attribute routing you can use the same way:
[HttpGet("Get/{id?}")]
public async ValueTask<IActionResult> Get(
Guid id)
{
return Ok(id);
}
Make the parameter optional
[Route("api/MyController")]
public class MyController: Controller {
//GET api/MyController/Get
//GET api/MyController/Get/{285A477F-22A7-4691-AA51-08247FB93F7E}
//GET api/MyController/Get?id={285A477F-22A7-4691-AA51-08247FB93F7E}
[HttpGet("Get/{id:guid?}"
public ActionResult<string> Get(Guid? id) {
if(id == null)
return BadRequest();
//...
}
}
This however means that you would need to do some validation of the parameter in the action to account for the fact that it can be passed in as null because of the action being able to accept api/MyController/Get on its own.
Reference Routing to controller actions in ASP.NET Core

MVC6 attribute routing with two parameters

I've had a look around for this and nothing that pertains to the MVC6 taghelper anchor tag in relation to having an alternative [HttpGet] method that caters for multiple parameters.
Sure you can add multiple parameters to a MVC6 anchor taghelper but how do you process the second option with two parameters using attrubute routing...
I have two [HttpGet] IactionResult methods:
//GET: UserAdmin
public async Task<IActionResult> Index()
{
return View(await _userAdminService.GetAllUsers("name_desc", false));
}
// GET: UserAdmin/name_desc/True
[HttpGet("Index/{sortValue}&{showDeactivated}")]
public async Task<IActionResult> Index(string sortValue, bool showDeactivated)
{
return View(await _userAdminService.GetAllUsers(sortValue, showDeactivated));
}
I have in my view an attempt to go to the second method:
<a asp-action="Index" asp-route-sortValue="#Model.DisplayName" asp-route-showActivated="#Model.ShowDeActivated">Name: <span class="glyphicon glyphicon-chevron-down"></span></a>
which renders to:
Name: <span class="glyphicon glyphicon-chevron-down"></span>
or
localhost.../UserAdmin?sorValue=name&showActivated=True
IT never goes to the second method.
What do I need to do to use the second [HttpGet] method with two parameters using the MVC6 anchor taghelper?
EDIT
Also how do you handle the ampersand separating the two parameters in the route attribute...
There is no support for ampersand in route template. The idea is that ampersand is used for query string and it will always be applied to any route template. That's why your second action is never called.
For example you can change your route template to
[HttpGet("UserAdmin/Index/{sortValue}/{showDeactivated}")]
Official documentation link
Don't split up your actions in this case. You can just as easily do this in one action:
public async Task<IActionResult> Index(string sortValue, bool showDeactivated)
{
var sort = string.IsNullOrWhiteSpace(sortValue) ? "name_desc" : sortValue;
return View(await _userAdminService.GetAllUsers(sort, showDeactivated));
}
If the sortValue GET parameter is not supplied it will default to null, and if showDeactivated is not supplied it will default to false.
Latest version of ASP.NET Core can handle this:
[HttpGet("Index")]
public async Task<IActionResult> Index([FromQuery(Name ="sortValue")]string sortValue,[FromQuery(Name ="showDeactivated")] bool showDeactivated)

Using RedirectToAction to break out of a controller/action

In every action in every controller, I would like to have a check that, in certain cases, would return the app to another controller/action. I would like the check to be as simple as possible, something like TestForExit( );
Here's my problem: all my actions return ActionResult, and here is a sample:
public ActionResult Partial()
{
TestForExit( );
...
return PartialView( "ViewPartial", data );
}
If TextForExit returns RedirectToAction( "Index", "Home" ) I have to have something like this:
public ActionResult Partial()
{
var result = TestForExit( );
if( result == null )
{
...
result = PartialView( "ViewPartial", data );
}
return result;
}
But, as I am going to have this everywhere, I'd really like to have TestForExit( ) itself be able to send me to Home/Index rather than return an ActionResult that my Action has to return.
In other words, how can I have TestForExit ACTUALLY go to Home/Index, instead of just returning an ActionResult the the original Action must return?
You will want to use an custom ActionFilter. You can apply this action filter globally. Then in the OnActionExecuting, you can perform the TestForExit check, and redirect if needed.
For example.
public void TestForExitActionFilterAttribute : ActionFilterAttribute, IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(TextForExit())
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary {{ "Controller", "ExitController" },
{ "Action", "ExitAction" } });
}
base.OnActionExecuting(filterContext);
}
}
Now apply your [TestForExitActionFilter] attribute to your controllers, or individual actions. Or, to add it everywhere, add the following line to FilterConfig.RegisterGlobalFilters filters.Add(new TextForExitActionFilterAttribute()).
Here are some related links.
Redirecting to specified controller and action in asp.net mvc action filter
http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-custom-action-filters
Alternatively, you can just override the OnActionExecuting method directly in your controller class and add the logic there. This would make more sense than a custom attribute if you only need this exit logic for one particular controller.
Well your controller action method has to return eventually, so you still have to return an ActionResult no matter what if the action is executed.
If you want to avoid adding that code to every action, you could think about creating a custom Action Filter and then marking your actions with that filter, or applying a global filter if you need it for EVERY action.
Then in your action filter, you check your exit condition and redirect if necessary.

TempData not working for second request in MVC4

I have never seen this before and am stumped. I have the following controller sequence:
/// <summary>
/// Helper method to store offerId to TempData
/// </summary>
/// <param name="offerId"></param>
private void StoreOfferInTempData(string offerId)
{
if (TempData.ContainsKey(SelectedOfferKey))
TempData.Remove(SelectedOfferKey);
TempData.Add(SelectedOfferKey, offerId);
}
[HttpPost]
[AllowAnonymous]
public virtual ActionResult Step1(MyViewModel model)
{
if (ModelState.IsValid)
{
StoreOfferInTempData(model.SelectedOfferId);
return RedirectToAction(MVC.Subscription.Register());
}
MySecondViewModel model2 = new MySecondViewModel { OfferId = model.SelectedOfferId };
return View(model2);
}
[HttpGet]
[AllowAnonymous]
public virtual ActionResult Register()
{
string offerId = TempData[SelectedOfferKey] as string; //we get a valid value here
... error handling content elided ...
RegisterViewModel model = new RegisterViewModel { OfferId = offerId };
return View(model);
}
[HttpPost]
[AllowAnonymous]
public virtual ActionResult Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
CreateCustomerResult result = CustomerService.CreateAccount(model.Email, model.NewPassword);
if (result.Success)
{
... content elided; storing customer to Session ...
MyMembershipProvider.PersistUserCookie(result.Principal, true);
//need to store our selected OfferId again for use by the next step
StoreOfferInTempData(model.OfferId);
return RedirectToAction(MVC.Subscription.Payment());
}
model.ErrorMessage = result.ErrorMessage;
}
return View(model);
}
[HttpGet]
public ActionResult Payment()
{
string offerId = TempData[SelectedOfferKey] as string; //this is null???
... content elided ...
return View(model);
}
The first round of storage to TempData behaves as expected. The value is present in the subsequent HttpGet method and is marked for deletion such that it is no longer there when I go to add it again. However, on the third HttpGet method, it returns null.
I have tried using different Keys for each round with no change. I can assure you that at no time other than those displayed am I checking TempData, so I see no way the value would be marked for deletion somehow. Also, it fails in the Payment method whether it has an [AllowAnonymous] attribute or not (so not due to any http to https switch or anything like that.
Seems like it must be something very simple, but my searches have turned up nothing. Any help greatly appreciated.
UPDATE: On further inspection, it seems my whole context is hosed on this step, for some reason. We're using IoC in the controllers, but none of the IoC-instantiated items are there. The mystery deepens.
The lifespan of TempData is only until it's read back out or the next request has processed (which ever comes first). You shouldn't be relying on TempData if you're going in to two (or three) requests. Instead, use the session or a database.
The purpose of TempData is to hand-off information between requests not to perpetuate until you clear it (that's what sessions are for).
Aha! Well, this is obscure enough that I hope it helps someone else. Turns out, I had forgotten to run my T4MVC.tt file after creating the Payment() actions, so the RedirectToAction taking an MVC.Subscription.Payment() action was not instantiating the controller properly. I'm not clear on all the underlying magic here, but if you run into this and are using T4MVC.tt, make sure you ran it!
Comments on why this would be are welcome.
Use TempData.Keep("key") to retain values between multiple post-backs