MVC6 attribute routing with two parameters - asp.net-core

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)

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?;
...
}
...
}

Add link parameter to asp tag helpers with pound sign (#) anchor url in ASP.NET Core

I have the following url http://localhost:5000/Home/Index/#test and I need to pass the #test to the action. I used asp-route="#test"and asp-route-id="#test" but they does not work.
This is my action:
public ActionResult Index(string id)
{
return View();
}
Try to use attribute routing
[Route("Home/Index/{id}")]
public async Task<IActionResult> Index(string id)
Use tag helpers like
<a asp-action="Index" asp-controller="Home" asp-route-id="#test">Index</a>
The hash or fragment portion of the URL is not part of the route. It only has meaning client-side. I don't think there's any way to add it via the tag helper. Instead you'll need to use something like Url.Action:
My Link

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

Adding a WEB API method ruins my SWAGGER UI

This first method is fine. But when I add the second method the body of the SWAGGER UI is a bunch of html gibberish. And I creating the route the wrong way?
// GET api/checklist/1288
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
var model = _checkListService.Get(id);
return Ok(model);
}
// http://localhost:64783/api/checklist/GetDelinquentItems?id=1288
[Route("GetDelinquentItems")]
public async Task<IActionResult> GetDelinquentItems(int id)
{
var model = _checkListService.GetDelinquentItems(id);
return Ok(model);
}
That 'html gibberish' (indeed not the most elegant way to show an error) still contains some useful information. The first line says:
500 internal server error
and in the last three lines you can read:
Ambiguos HTTP method for action...CheckListController.GetDelinquentItems... Actions require explicit HttpMethod binding for Swagger
therefore another
[HttpGet("{id}")]
before the GetDelinquentItems() method should solve the problem.

asp.net mvc add parameter with each action view

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();
}