MVC Core Controller Declaration - c#

In the code line below below, what does the second Controller mean?
Is that a data variable declaration for HelloWorldController?
HelloWorldController : **Controller**
From MSDN Adding Controller
using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;
namespace MvcMovie.Controllers
{
public class HelloWorldController : Controller
{
//
// GET: /HelloWorld/
public string Index()
{
return "This is my default action...";
}
//
// GET: /HelloWorld/Welcome/
public string Welcome()
{
return "This is the Welcome action method...";
}
}
}

It means Controller is the base type of HelloWorldController, enabling you to access all of its protected methods, and allowing it to be stored anywhere a Controller can be stored.
This relates to the inheritance part of object-oriented programming, which is a topic much too broad for a complete explanation here.

Related

How To get Id from the url Path

My question may look simple but I'm stuck in this problem for a while.
Code Controller:
[Route("api/ReceiptOrders/{receiptOrderNo}/[controller]")]
[ApiController]
public class ReceiptPositionsController : ControllerBase
{
[HttpPost("{orderNo}")]
public async Task<IActionResult> PostReceiptOrderPositions(
[FromBody] IEnumerable<ReceiptPositionForCreationDto> receiptPositions)
{
var receiptOrder = await _repositoryManager.ReceiptOrder.GetReceiptOrderByOrderNoAsync(receiptOrderNo, false);
//More code here...
}
}
URL: https://localhost:7164/api/ReceiptOrders/RO-20220731-4/ReceiptPositions
my question is: how to get the ReceiptOrderNo(RO-20220731-4 in this case)?
I need it to fill "GetReceiptOrderByOrderNoAsync" method.
Bind to the template entry by adding another parameter in your method. In addition, remove the template from the method's attribute. A method's template is appended to the class's template, so what you currently have won't match.
[Route("api/ReceiptOrders/{receiptOrderNo}/[controller]")]
[ApiController]
public class ReceiptPositionsController : ControllerBase
{
[HttpPost] // <--- no template
public async Task<IActionResult> PostReceiptOrderPositions(
[FromRoute] string receiptOrderNo, // <---- new parameter
[FromBody] IEnumerable<ReceiptPositionForCreationDto> receiptPositions)
{
// ... use "receiptOrderNo"
}
You don't need [FromRoute], but I think it's a good idea as it makes it explicit where the value should be read from, instead of accidently read from a Query String or another source.

How does ASP.NET MVC associate page names with class names?

I'm new to ASP.NET MVC and I'm confused about how in a compiled language like C#, class names can have any meaning after the project has been built. Can someone explain to me how the building process takes
public class SomePageController : Controller {
public ActionResult Index() { return View(); }
}
and grabs the names SomePage and Index to create a URL map SomePage/Index that calls the function Index()? I only understand how this works if there is some other C# file that somehow able to look at all classes derived from the Controller class and get a string that is their name without the suffix "Controller". But that seems weird to me coming from a C++ background because I've never seen a way that a language can have of variables referencing their own name, nor have I ever seen a way to iterate through all classes derived from a certain class.
Maybe someone can show me how to write a C# procedure like
public class SomePageController : Controller {
public ActionResult Index() { return View(); }
}
public class SomeOtherPageController : Controller {
public ActionResult Index() { return View(); }
}
public void printPageNames ( void )
{
// ... Will print "SomePage, SomeOtherPage" to the console
}
What you need to read into is Reflection. It allows you to look at all of the classes, properties, etc within an assembly.
To directly answer your question, Jon Skeet has the starting point of how to accomplish this.
Your code would look something like this:
var assembly = Assembly.GetExecutingAssembly();
foreach (var controller in assembly.GetTypes().Where(a => a.Name.EndsWith("Controller"))
{
Console.WriteLine(controller.Name.TrimEnd("Controller"));
}

How to determine Route Prefix programmatically in asp.net mvc?

I wanted to provide some URL separation for my public/anonymous controllers and views from the admin/authenticated controllers and views. So I ended up using entirely Attribute Routing in order to take more control of my URLs. I wanted my public URLs to start with "~/Admin/etc." while my public URLs would not have any such prefix.
Public Controller (one of several)
[RoutePrefix("Home")]
public class HomeController : Controller
{
[Route("Index")]
public ActionResult Index()
{ //etc. }
}
Admin Controller (one of several)
[RoutePrefix("Admin/People")]
public class PeopleController : Controller
{
[Route("Index")]
public ActionResult Index()
{ //etc. }
}
This allows me to have public URLs such as:
http://myapp/home/someaction
...and admin/authenticated URLs such as:
http://myapp/admin/people/someaction
But now I want to do some dynamic stuff in the views based on whether the user is in the Admin section or the Public section of the site. How can I access this programmatically, properly?
I know I could do something like
if (Request.Url.LocalPath.StartsWith("/Admin"))
...but it feels "hacky." I know I can access the controller and action names via
HttpContext.Current.Request.RequestContext.RouteData.Values
...but the "admin" piece isn't reflected in there, because it's just a route prefix, not an actual controller name.
So, the basic question is, how do I programmatically determine whether the currently loaded view is under the "admin" section or not?
You just need to reflect the RoutePrefixAttribute from the Controller type, and then get its Prefix value. The Controller instance is available on the ViewContext.
This example creates a handy HTML helper that wraps all of the steps into a single call.
using System;
using System.Web.Mvc;
public static class RouteHtmlHelpers
{
public static string GetRoutePrefix(this HtmlHelper helper)
{
// Get the controller type
var controllerType = helper.ViewContext.Controller.GetType();
// Get the RoutePrefix Attribute
var routePrefixAttribute = (RoutePrefixAttribute)Attribute.GetCustomAttribute(
controllerType, typeof(RoutePrefixAttribute));
// Return the prefix that is defined
return routePrefixAttribute.Prefix;
}
}
Then in your view, you just need to call the extension method to get the value of the RoutePrefixAttribute.
#Html.GetRoutePrefix() // Returns "Admin/People"

How to set this Area up in my ASP.NET MVC Application

I'm trying to setup an Area Route in my ASP.NET MVC application.
I'm also using the nuget package AttributeRouting, not the normal MVC register area routes thingy.
From my understanding, area routes look like this : /area/controller/method
What I'm trying to do is :- /api/search/index
which means:
Area => Api
Controller => SearchController
ActionMethod => Index
.
[RouteArea("Api")]
public class SearchController : Controller
{
[POST("Index")]
public JsonResult Index(IndexInputModel indexInputModel) { .. }
}
But that doesn't create that route. This is what it creates: /api/index
The search controller is missing.
I've had a look the docs and noticed the RoutePrefix so I tried this..
[RouteArea("Api")]
[RoutePrefix("Search")]
public class SearchController : Controller
{
[POST("Index")]
public JsonResult Index(IndexInputModel indexInputModel) { .. }
}
and that actually creates the route /api/search/index.
But why do i need to put the RoutePrefix in there? Shouldn't it be smart enough to already figure out that this is a SearchController and create the 3-segment route?
You don't need to put a RoutePrefix anywhere. It's just there as a refactoring/DRY aid. Consider:
[RouteArea("Api")]
public class SearchController : Controller
{
[POST("Search/Index")]
public ActionResult Index() { }
}
If you had a number of actions, maybe you want them all with the "Search" prefix, so you'd do:
[RouteArea("Api")]
[RoutePrefix("Search")]
public class SearchController : Controller
{
[POST("Index")]
public ActionResult Index() { }
// Other actions to prefix....
}
Shouldn't it be smart enough?
Not to be cheeky, but no. AR was never intended to read all your code for you and magically generate routes. It was intended to keep your URLs top of mind, and to do that you should see your URLs. Not that this is the best or only way of doing things, just that was my intent from the get.
The real reason why it isn't smart enough is that the concept of "Area" has nothing to do with URL. An area is a logical unit. You could expose that logical unit without any route prefix (so it would be hanging off ~/) or you could expose it off "This/Is/A/Prefix".
However, if you want it to be smart enough.... I just released v3.4, which will let you do this (if you want to; don't have to):
namespace Krome.Web.Areas.Api
{
[RouteArea]
[RoutePrefix]
public class SearchController : Controller
{
[POST]
public ActionResult Index() { }
}
}
This will yield the following route: ~/Api/Search/Index. The area comes from the last section of the controller's namespace; the route prefix comes from the controller name; and the rest of the url comes from the action name.
One more thing
If you want to get out a route area url and route prefix rat's nest for individual actions in a controller, do this:
[RouteArea("Api")]
[RoutePrefix("Search")]
public class SearchController : Controller
{
[POST("Index")]
public ActionResult Index() { }
[GET("Something")] // yields ~/Api/Search/Something
[GET("NoPrefix", IgnoreRoutePrefix = true)] // yields ~/Api/NoPrefix
[GET("NoAreaUrl", IgnoreAreaUrl = true)] // yields ~/Search/NoAreaUrl
[GET("Absolutely-Pure", IsAbsoluteUrl = true)] // yields ~/Absolutely-Pure
public ActionResult Something() {}
}

MVC Calling a view from a different controller

My project structure is like:
Controllers/ArticlesController.cs
Controllers/CommentsController.cs
Views/Articles/Read.aspx
Read.aspx takes a parameter say "output", which is the details of the article by id and its comments, passed from ArticlesController.cs
Now I want to write then read the comment:: write() & Read() funct in CommentsController.cs
For reading the article with its comments, I want to call Views/Articles/Read.aspx from CommentsController.cs by passing output parameter from CommentsController.cs
How can I do this?
UPDATE
Code Here:
public class CommentsController : AppController
{
public ActionResult write()
{
//some code
commentRepository.Add(comment);
commentRepository.Save();
//works fine till here, Data saved in db
return RedirectToAction("Read", new { article = comment.article_id });
}
public ActionResult Read(int article)
{
ArticleRepository ar = new ArticleRepository();
var output = ar.Find(article);
//Now I want to redirect to Articles/Read.aspx with output parameter.
return View("Articles/Read", new { article = comment.article_id });
}
}
public class ArticlesController : AppController
{
public ActionResult Read(int article)
{
var output = articleRepository.Find(article);
//This Displays article data in Articles/Read.aspx
return View(output);
}
}
To directly answer your question if you want to return a view that belongs to another controller you simply have to specify the name of the view and its folder name.
public class CommentsController : Controller
{
public ActionResult Index()
{
return View("../Articles/Index", model );
}
}
and
public class ArticlesController : Controller
{
public ActionResult Index()
{
return View();
}
}
Also, you're talking about using a read and write method from one controller in another. I think you should directly access those methods through a model rather than calling into another controller as the other controller probably returns html.
You can move you read.aspx view to Shared folder. It is standard way in such circumstances
I'm not really sure if I got your question right. Maybe something like
public class CommentsController : Controller
{
[HttpPost]
public ActionResult WriteComment(CommentModel comment)
{
// Do the basic model validation and other stuff
try
{
if (ModelState.IsValid )
{
// Insert the model to database like:
db.Comments.Add(comment);
db.SaveChanges();
// Pass the comment's article id to the read action
return RedirectToAction("Read", "Articles", new {id = comment.ArticleID});
}
}
catch ( Exception e )
{
throw e;
}
// Something went wrong
return View(comment);
}
}
public class ArticlesController : Controller
{
// id is the id of the article
public ActionResult Read(int id)
{
// Get the article from database by id
var model = db.Articles.Find(id);
// Return the view
return View(model);
}
}
It is explained pretty well here: Display a view from another controller in ASP.NET MVC
To quote #Womp:
By default, ASP.NET MVC checks first in \Views\[Controller_Dir]\,
but after that, if it doesn't find the view, it checks in \Views\Shared.
ASP MVC's idea is "convention over configuration" which means moving the view to the shared folder is the way to go in such cases.

Categories

Resources