Reputation: 95
I am working on educational platform for various subjects. With a help of Entity Framework Core, I have designed database schema such that:
Every subject belongs to a category and a category can have one or more sub-categories (see diagram attached).
With that in place, I would like to achieve routing something like:
/mathematics/integration/substitution
/programming/paradigms/oop
/economics/macro/unemployment
eg.
/{category}/{sub-category}/{subject}
where:
/mathematics (just like programming and economics) is a root category (eg. does not have a parent. It lists all the mathematics related sub-categories)
/integration is a sub-category (which also lists all the integration related subjects)
/substitution is a subject
I tried using both Attribute and Conventional routing as well as Areas but got lost in file structure / complexity for what seems like a simple task...
my routing so far:
endpoints.MapAreaControllerRoute(
name: "mathematics",
areaName: "mathematics",
pattern: "mathematics/{controller=Home}/{action=Index}/{slug?}"
);
How would you approach this problem? What controllers and actions do I need?
see https://isibalo.com for reference. It is in Czech language, but you get the idea. Just look at the URL routing. How did he accomplish that?
Upvotes: 1
Views: 7469
Reputation: 61
use this route for handling area and slug
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
endpoints.MapControllerRoute(
name: "slug",
pattern: "pages/{id}/{*Slug}",
defaults: new { controller = "Pages", action = "Detail"}
);
Upvotes: 0
Reputation: 1875
I used this slug code generator to create the slug which I saved in the database. Then I fetched this slug and sent it to the View. In the link to the action controller I have two asp-routes, one for the ID and one for the Slug.
Index.cshtml
<a asp-controller="Home" asp-action="Details" asp-route-id="@Model.Restaurants[i].Id" asp-route-slug="@Model.Restaurants[i].Slug">@Model.Restaurants[i].Name</a>
In the controller I have this action method (I don't care about the slug
value inside the method, I just use the id
value):
HomeController.cs
[HttpGet("Home/Details/{id}/{slug}")]
public async Task<IActionResult> Details(int id, string slug)
{
var restaurant = await _unitOfWork.RestaurantRepository.GetAsync(id);
var viewModel = new HomeDetailsVM
{
Address = restaurant.Address,
...
};
return View(viewModel);
}
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider)
{
if (env.EnvironmentName == "Development")
{
app.UseDeveloperExceptionPage();
app.UseShowAllServicesMiddleware();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles();
app.UseCookiePolicy();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"));
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
This is how it looks in the browser:
Upvotes: 0
Reputation: 95
I resolved this issue using both Areas and Attribute routing.
Startup.cs
endpoints.MapAreaControllerRoute(
"mathematics",
"mathematics",
"mathematics/{controller=Home}/{action=Index}/{slug?}"
);
MathematicsController.cs
[Area("Mathematics")]
[Route("[controller]")]
public class MathematicsController : Controller
{
private readonly ApplicationDbContext _context;
public MathematicsController(ApplicationDbContext context) => _context = context;
[HttpGet("{slug}")]
public async Task<IActionResult> Index(string slug)
{
var category = await _context.Categories
.FirstOrDefaultAsync(s => s.Slug == slug);
if (category == null)
return View("Errors/NotFound", Response.StatusCode = 404);
var subjects = await _context.Subjects
.Include(c => c.Category)
.Where(s => category.Slug == slug)
.ToListAsync();
return View("Index", new SubjectListViewModel { Subjects = subjects });
}
[HttpGet("{category}/{slug}")]
public async Task<IActionResult> Show(string category, string slug)
{
var subject = await _context.Subjects.FirstOrDefaultAsync(s => s.Slug == slug);
return subject == null
? View("Errors/NotFound", Response.StatusCode = 404)
: View("Show", subject);
}
}
Finally, supply both category (which can also be a sub-category) and a subject.
Mathematics\Index.cshtml
<a
asp-area="Mathematics"
asp-controller="Mathematics"
asp-action="Show"
asp-route-department="@subject.Category.Slug"
asp-route-slug="@subject.Slug"
>
@subject.Title
</a>
Upvotes: 2