Reputation: 129
In ASP.NET Core 3.1, when I specify the route in controller, must be kind of
[Route("/[area]/[controller]/[action]/{id}")]
but when it's like
[Route("/[area]/[controller]/[action]?id={id}")]
it returns an error 500.
I want to show a TableData from JS in the route
Admin/Order/Index?status={status}
but, in the controller, the HttpGet is the action GetOrderList
so data is sent to
Admin/Order/GetOrderList?status={status}
How could I configure the route right?
Controller:
[Area("Admin")]
[ApiController]
[Route("Admin/Order/")]
[Authorize]
public class OrderController : Controller
{
#region Properties
private readonly IUnitOfWork _unitOfWork;
private Claim _claim
{
get
{
ClaimsIdentity claimsIdentity = (ClaimsIdentity)User.Identity;
return claimsIdentity.FindFirst(ClaimTypes.NameIdentifier);
}
set { }
}
#endregion
public OrderController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
[Route("Index")]
public IActionResult Index()
{
return View();
}
#region API CALLS
[Route("GetOrderList")]
[HttpGet]
public IActionResult GetOrderList(string status)
{
IEnumerable<OrderHeader> orderHeaderList;
// Repetimos codigo del switch, para que la recogida de datos sea más eficiente.
// Si hicieramos switch al final, y aplicamos filtro al IEnumerable<T>, estamos leyendo
// Todos los datos, y despues aplicamos filtro
// Mientras que si aplicamos el filtro en el GetAll() hacemos uso del IQueryable<T> que
// Aplica el filtro en la base de datos, y carga en memoria lo necesario
if(User.IsInRole(SD.Role_Admin) || User.IsInRole(SD.Role_Employee))
{
// Si es admin, puede gestionar todas las transacciones
switch (status)
{
case "pending":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.PaymentStatus == SD.PaymentStatusDelayedPayment,
includeProperties: "ApplicationUser");
break;
case "inprocess":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.OrderStatus == SD.StatusApproved ||
item.OrderStatus == SD.StatusInProcess ||
item.OrderStatus == SD.StatusPending,
includeProperties: "ApplicationUser");
break;
case "completed":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.OrderStatus == SD.StatusShipped,
includeProperties: "ApplicationUser");
break;
case "rejected":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.OrderStatus == SD.StatusCancelled ||
item.OrderStatus == SD.StatusRefunded,
includeProperties: "ApplicationUser");
break;
case "approved":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.OrderStatus == SD.PaymentStatusApproved ||
item.OrderStatus == SD.StatusApproved,
includeProperties: "ApplicationUser");
break;
default:
orderHeaderList = _unitOfWork.OrderHeader.GetAll(includeProperties: "ApplicationUser");
break;
};
}
else
{
// Si es trabajador, solo puede acceder a las suyas
switch (status)
{
case "pending":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.ApplicationUserId == _claim.Value &&
(item.PaymentStatus == SD.PaymentStatusDelayedPayment), includeProperties: "ApplicationUser");
break;
case "approved":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.ApplicationUserId == _claim.Value &&
(item.OrderStatus == SD.PaymentStatusApproved ||
item.OrderStatus == SD.StatusApproved), includeProperties: "ApplicationUser");
break;
case "inprocess":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.ApplicationUserId == _claim.Value &&
(item.OrderStatus == SD.StatusApproved ||
item.OrderStatus == SD.StatusInProcess ||
item.OrderStatus == SD.StatusPending), includeProperties: "ApplicationUser");
break;
case "completed":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.ApplicationUserId == _claim.Value &&
(item.OrderStatus == SD.StatusShipped), includeProperties: "ApplicationUser");
break;
case "rejected":
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.ApplicationUserId == _claim.Value &&
(item.OrderStatus == SD.StatusCancelled ||
item.OrderStatus == SD.StatusRefunded), includeProperties: "ApplicationUser");
break;
default:
orderHeaderList = _unitOfWork.OrderHeader.GetAll(item => item.ApplicationUserId == _claim.Value, includeProperties: "ApplicationUser");
break;
};
}
return Json(new { data = orderHeaderList });
}
#endregion
}
Index View:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
var status = Context.Request.Query["status"];
var approved = "text-primary";
var pending = "text-primary";
var all = "text-primary";
var inprocess = "text-primary";
var completed = "text-primary";
var rejected = "text-primary";
switch (status)
{
case "pending":
pending = "active text-white";
break;
case "approved":
approved = "active text-white";
break;
case "inprocess":
inprocess = "active text-white";
break;
case "completed":
completed = "active text-white";
break;
case "rejected":
rejected = "active text-white";
break;
default:
all = "active text-white";
break;
};
}
<br />
<div class="border p-3">
<div class=" d-flex justify-content-between mb-3">
<div class="p-2">
<h2 class="text-primary">Order's List </h2>
</div>
<div class="p-2">
<ul class="list-group list-group-horizontal-sm">
<a style="text-decoration:none;" asp-controller="Order" asp-action="Index" asp-route-status="inprocess">
<li class="list-group-item @inprocess"> In Process</li>
</a>
<a style="text-decoration:none;" asp-controller="Order" asp-action="Index" asp-route-status="pending">
<li class="list-group-item @pending">Pending</li>
</a>
<a style="text-decoration:none;" asp-controller="Order" asp-action="Index" asp-route-status="completed">
<li class="list-group-item @completed">Completed</li>
</a>
<a style="text-decoration:none;" asp-controller="Order" asp-action="Index" asp-route-status="approved">
<li class="list-group-item @approved">Approved</li>
</a>
<a style="text-decoration:none;" asp-controller="Order" asp-action="Index" asp-route-status="rejected">
<li class="list-group-item @rejected">Rejected</li>
</a>
<a style="text-decoration:none;" asp-controller="Order" asp-action="Index" asp-route-status="all">
<li class="list-group-item @all">All</li>
</a>
</ul>
</div>
</div>
<br /><br />
<table id="tblData" class="table table-striped table-bordered" style="width:100%">
<thead class="thead-dark">
<tr class="table-info">
<th>Id</th>
<th>Name</th>
<th>Phone number</th>
<th>Email</th>
<th>Order status</th>
<th>Amount</th>
</tr>
</thead>
</table>
</div>
@section Scripts {
<script src ="~/js/order.js"></script>
}
order.js
var dataTable;
$(document).ready(function () {
var url = window.location.search;
if (url.includes("inprocess")) {
loadDataTable("GetOrderList?status=inprocess");
} else {
if (url.includes("pending")) {
loadDataTable("GetOrderList/status=pending");
} else {
if (url.includes("completed")) {
loadDataTable("GetOrderList?status=completed");
} else {
if (url.includes("rejected")) {
loadDataTable("GetOrderList?status=rejected");
} else {
loadDataTable("GetOrderList?status=all")
}
}
}
}
});
function loadDataTable(url) {
dataTable = $('#tblData').DataTable({
"ajax": {
"url": "/Admin/Order/" + url
},
"columns": [
{ "data": "id", "width": "15%" },
{ "data": "name", "width": "15%" },
{ "data": "phoneNumber", "width": "15%" },
{ "data": "applicationUser.email", "width": "15%" },
{ "data": "orderStatus", "width": "15%" },
{ "data": "orderTotal", "width": "15%" },
{
"data": "id",
"render": function (data) {
return `
<div class="row text-center">
<a href="/Admin/Order/Details/${data}" class="btn btn-success text-white" style="cursor:pointer">
<i class="fas fa-edit"></i>
</a>
</div>
`;
}, "width": "10%"
}
]
});
}
Results
Thanks!
Upvotes: 6
Views: 21848
Reputation: 181
Can be done in this way as well.
To make target a custom HTML tag you have to make sure that the element is having Tag helper. If it is not having a tag helper it will not work.
Sample code.
namespace Routing_Helpers.Helpers
{
// You may need to install the Microsoft.AspNetCore.Razor.Runtime package into your project
[HtmlTargetElement(Attributes = "asp-active-route")]
public class ActiveRouteTagHelper : TagHelper
{
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var currentController = ViewContext.RouteData.Values["Controller"].ToString();
var currentAction = ViewContext.RouteData.Values["Action"].ToString();
var tagController = context.AllAttributes.FirstOrDefault(a => a.Name == "asp-controller").Value.ToString();
var tagAction = context.AllAttributes.FirstOrDefault(a => a.Name == "asp-action").Value.ToString();
if (currentController == tagController && currentAction == tagAction)
{
var cssClasses = context.AllAttributes.FirstOrDefault(a => a.Name == "class").Value.ToString();
var activeClass = context.AllAttributes.FirstOrDefault(a => a.Name == "asp-active-route").Value.ToString();
output.Attributes.SetAttribute("class", cssClasses + " " + activeClass);
}
}
}
}
To access the helper everywhere on the page you need to add the assembly in to the viewimports.cshtml file
@addTagHelper *, TagHelpers
HTML code
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index" asp-active-route="active">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login" asp-active-route="active">Login</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy" asp-active-route="active">Privacy</a>
</li>
</ul>
</div>
Hope this gives you a clarity. Happy Coding!!!
Upvotes: 0
Reputation: 19705
Route parameters are different from Query parameters.
when the url is like /some/action/and/property/id
that is route parameters and are parsed using the /
and the keys are specified based on parameter position. Route parameters can be either static (/some/action) or can include dynamic values (/some/action/{id}).
When you are dealing with ?key=value&key2=value2
format, you are dealing with Query parameters. Since those ain't route parameters because those doesn't depend on the current route you don't need to decorate your action with the routing and just use the parameters as an input to your function:
GET: /index?status=1
[Route("[action]")]
public IActionResult Index(string status){
// "status" is bound from query string ?status=1.
// "index" is bound from route [action]
}
Upvotes: 1
Reputation: 27793
The following is a working demo with testing data, please refer to it.
Javascript code
$(document).ready(function () {
var url = window.location.search;
//console.log(url);
var status = GetURLParameter("status");
switch (status) {
case "inprocess": loadDataTable("GetOrderList?status=inprocess"); break;
case "pending": loadDataTable("GetOrderList?status=pending"); break;
case "completed": loadDataTable("GetOrderList?status=completed"); break;
case "rejected": loadDataTable("GetOrderList?status=rejected"); break;
case "approved": loadDataTable("GetOrderList?status=approved"); break;
default: loadDataTable("GetOrderList?status=all");
}
});
function GetURLParameter(sParam) {
var sPageURL = window.location.search.substring(1);
var sURLVariables = sPageURL.split('&');
for (var i = 0; i < sURLVariables.length; i++) {
var sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] == sParam) {
return sParameterName[1];
}
}
}
function loadDataTable(url) {
//console.log(url);
$('#tblData').DataTable({
"ajax": {
"url": "/Admin/Order/" + url
},
"columns": [
{
"data": "id",
"render": function (data) {
console.log(data);
return `
<div class="row text-center">
<a href="/Admin/Order/Details/${data}" class="btn btn-success text-white" style="cursor:pointer">
<i class="fas fa-edit">${data}</i>
</a>
</div>
`;
},
"width": "15%"
},
{ "data": "applicationUser.Name", "width": "15%" },
{ "data": "applicationUser.phoneNumber", "width": "15%" },
{ "data": "applicationUser.email", "width": "15%" },
{ "data": "orderStatus", "width": "15%" },
{ "data": "orderTotal", "width": "15%" }
]
});
}
Controller actions
[Area("Admin")]
[Route("Admin/Order/")]
public class OrderController : Controller
{
private readonly List<AppUserOrder> appUserOrders;
public OrderController()
{
appUserOrders = new List<AppUserOrder>
{
new AppUserOrder
{
Id = 1,
ApplicationUserId = Guid.NewGuid().ToString(),
ApplicationUser =new ApplicationUser
{
UserName = "Jesus",
Email = "Jesus email here",
PhoneNumber = "673533658"
},
OrderStatus = "pending",
OrderTotal = 25
},
new AppUserOrder
{
Id = 2,
ApplicationUserId = Guid.NewGuid().ToString(),
ApplicationUser =new ApplicationUser
{
UserName = "Ray",
Email = "Ray email here",
PhoneNumber = "673533659"
},
OrderStatus = "completed",
OrderTotal = 39
},
new AppUserOrder
{
Id = 3,
ApplicationUserId = Guid.NewGuid().ToString(),
ApplicationUser =new ApplicationUser
{
UserName = "Wall",
Email = "Wall email here",
PhoneNumber = "673533656"
},
OrderStatus = "inprocess",
OrderTotal = 128
},
new AppUserOrder
{
Id = 4,
ApplicationUserId = Guid.NewGuid().ToString(),
ApplicationUser =new ApplicationUser
{
UserName = "Jack",
Email = "Jack email here",
PhoneNumber = "673533655"
},
OrderStatus = "rejected",
OrderTotal = 206
},
new AppUserOrder
{
Id = 5,
ApplicationUserId = Guid.NewGuid().ToString(),
ApplicationUser =new ApplicationUser
{
UserName = "Terry",
Email = "Terry email here",
PhoneNumber = "673533653"
},
OrderStatus = "approved",
OrderTotal = 102
}
};
}
public IActionResult Index(string status)
{
return View();
}
[Route("GetOrderList")]
[HttpGet]
public IActionResult GetOrderList(string status)
{
List<AppUserOrder> res = appUserOrders;
if (status!="all")
{
res = appUserOrders.AsQueryable().Where(a => a.OrderStatus == status).ToList();
}
return new JsonResult(new { data = res });
}
}
Model classes
public class AppUserOrder
{
public int Id { get; set; }
public string ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int OrderTotal { get; set; }
public string OrderStatus { get; set; }
}
public class ApplicationUser
{
[JsonPropertyName("Name")]
public string UserName { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
Testing data
{"data":[{"id":1,"applicationUserId":"e6aaa9ac-2bd7-4aa7-8f74-e6bce58236ad","applicationUser":{"Name":"Jesus","email":"Jesus email here","phoneNumber":"673533658"},"orderTotal":25,"orderStatus":"pending"},{"id":2,"applicationUserId":"85eb90a0-44e9-4266-b6d0-3f72fc122c24","applicationUser":{"Name":"Ray","email":"Ray email here","phoneNumber":"673533659"},"orderTotal":39,"orderStatus":"completed"},{"id":3,"applicationUserId":"c1031399-4ebe-4d64-addf-4852f19e30f7","applicationUser":{"Name":"Wall","email":"Wall email here","phoneNumber":"673533656"},"orderTotal":128,"orderStatus":"inprocess"},{"id":4,"applicationUserId":"02ff9515-5633-4981-ab06-2b1f9b01c375","applicationUser":{"Name":"Jack","email":"Jack email here","phoneNumber":"673533655"},"orderTotal":206,"orderStatus":"rejected"},{"id":5,"applicationUserId":"3e524698-f2b5-488b-bffb-4287d3447da5","applicationUser":{"Name":"Terry","email":"Terry email here","phoneNumber":"673533653"},"orderTotal":102,"orderStatus":"approved"}]}
Test Result
Upvotes: 2
Reputation: 26362
Check the Microsoft Routing Documentation
A route template looks similar to a URI path, but it can have placeholder values, indicated with curly braces
So the [Route("/[area]/[controller]/[action]?id={id}")]
is not a valid route, thus the error 500
If you want to pass something as a Query parameter, use the FromQuery
attribute.
You can also have multiple Get Methods in the same controller with different routes
[Route("Index")]
[HttpGet]
public IActionResult Index()
{
return View();
}
If you are sending a Query Parameter to the index, add it to the parameters too
[Route("Index")]
[HttpGet]
public IActionResult Index(string status)
{
return View();
}
By the way, you also have a typo:
loadDataTable("GetOrderList/status=pending");
Upvotes: 2