MVC core routing not functioning as expected - c#

So it's my first time setting up an netcore MVC based application. I've used MVC 4 in the past on plain old asp.net.
So i'm having issues with my routing. My application is an single page application (spa) that is accessible from the home controller on the index action. I can access this controller method fine, and my defaults are set so that this is navigated to at route: /.
I also have a second controller for authentication called AccountController. This controller's methods take and return JSON, rather then views. I can also access the methods on this controller from my application.
The issue i'm having lies in my next controller, which is the start of my API.
As such, i've put it in a folder called api inside my controllers folder. However, no matter what i try, i cannot seem to get the methods on the controller accessible. I have also tried moving it out of the api folder and just having in the route of the controllers folder.
The routing deffinition
app.UseMvc(routes =>
{
routes.MapRoute(
name: "api",
template: "api/{controller=Core}/{id?}");
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
I've tried adding and removing the api definition, removing the api part, and adding a template for actions aswel, all to no effect.
The troublesome controller
public class CoreController : Controller
{
[HttpGet]
public JsonResult Get()
{
return Json("Dev");
}
}
I've tried adding [Route(~routing here~)] annotations to this controller and its methods with no success either.
Folder structure
I should also mention that i've tried plenty of URL's to access this controller on:
/api/Core/
/Core/
/api/Core/Get
I've been wracking my brain for the best part of a day trying to get this sorted and i know i'm missing something obvious, i just can't for the life of me work out what it is.
Edit:
I've added a cut-down sample of my project to github at: https://github.com/lexwebb/aspnet-test if anyone would like a complete example
Edit 2
It appears that my example works, i'm going to add things in to see what breaks it

AFAIK, default route requires the {action} using as well.
Instead of "api" default routing, you may to use the following configuration for such type of controllers (RESTFul controller):
[Route("api/[controller]")]
public class CoreController : Controller
{
[HttpGet]
public JsonResult Get()
{
return Json("Dev");
}
}
I found this Routing is ASP.NET Core article useful in the past.

So as it turns out, i had made a mistake in a totally unrelated place. I had renamed my project half way through the beginning stage of development, after i had build scripts in place. This led to the the wrong dll being referenced on the server when the code was ran, a version that had all of my routing EXCEPT the new one, of course.

Related

Rewrite URl according to Attribute

We have an ASP.NET Core Web API running on .NET 5. It has got many controllers and routes that are sometimes protected via the Authorize attribute and sometimes they are public.
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase {
[HttpGet("me")]
public IActionResult GetMyPublicInformation()
{
// code...
}
[HttpGet("me")]
[Authorize]
public IActionResult GetMyPrivateInformation()
{
// code...
}
}
Well now I would like to publish these REST routes through different HTTP Routes, depending on the Authorization requirement. For example, the route GetPublicInformation does not require authorization. That route should be available under public/user/me. Whereas the method GetMyPrivateInformation does require authorization and should be published under the route secure/user/me.
Of coure, I am aware that I can define the route manually in the HttpGet attribute (i.e. [HttpGet("public/user/me")), but - besides that I'm lazy - it would be error prone because we have the information available twice: Once with in the manual route definition (HttpGet) and once in the Authorize attribute. So if someone forgets to change one of the two attributes, we get an inconsistency.
Question: What are my options to automate this URL rewriting (I'm thinking of middleware)? Are there any drawbacks you see using this approach? I have been fighting this idea because I don't like extra magic sauce in my codebase. I prefer explicity, that's why I'm going for the manual route naming right now...
By the way: I have to take this on me because of limitations in Microsoft's MSAL library. I believe I shouldn't have to do this because we host an OpenAPI definition on which routes are and which routes aren't authorized. That should be enough in most cases.

ASP.NET Core MVC Web API deployment issues

I'm trying to deploy my ASP.NET Core MVC Web App with Web API, i.e. I have both MVC and API controllers in the same folder.
It works fine on localhost but on IIS when I create a Virtual Directory, the path gets added to the domain.
I can find it using window.location.pathname
I can append the 'api/Get' and it works like (questions is my virtual directory)
http://example.com/questions/api/Question/GetAll
But when I navigate to other pages then then controller name also gets appended and then it causes issues.
e.g. if I navigate to the 'Question' page (QuestionController), the URL becomes
http://example.com/questions/newquestion/api/Question/Create
instead of
http://example.com/questions/api/Question/Create
How can I fix it?
Here is my Asp.Net core api.
[ApiController]
public class ScheduleController : ControllerBase
{
[HttpGet]
public List<PathologistSchedule> GetPathologistScheduleByDate(DateTime taskDate)
{
return pathologistRepository.GetPathologistScheduleByDate(taskDate).ToList();
}
}
I call this api from PathologistScheduleController's view using jquery.
Here's the error I get:
GET http://localhost:51434/PathologistSchedule/api/Schedule/?sort=&group=&filter=&taskDate=2020-11-13T21%3A16%3A47.507Z 404 (Not Found)
TIA.
A
If you have API and MVC projects in one solution you have to config your solution to run multiple projects.
You can use route attribute like this for each of your APIs
[Route("~/api/Question/GetAll")]
will give you Url http://example.com/api/Question/GetAll.
Or
[Route("~/api/Question/Create")]
will give Url http://example.com/api/Question/Create.
And it will not depend on the controller name or folder.
UPDATE because of the question update:
Use this code please:
public class ScheduleController : ControllerBase
{
[Route("~/api/Schedule/GetPathologistScheduleByDate/{taskDate?}")]
public List<PathologistSchedule> GetPathologistScheduleByDate(DateTime taskDate)
{
return pathologistRepository.GetPathologistScheduleByDate(taskDate).ToList();
}
}
for testing try this route in your browser:
http://localhost:51434/api/Schedule/GetPathologistScheduleByDate/2020-11-13T21%3A16%3A47.507Z
But basically for APIs you don't need to use any controller or action name. You can use any names you like, for example:
[Route("~/api/Pathologist/GetSchedule/{taskDate?}")]
or
[Route("~/api/GetPathologistSchedule/{taskDate?}")]
or even
[Route("~/api/{taskDate?}")]
The route just should be unique.
I added a variable in the 'appsettings.json' and 'appsettings.Development.json' called baseURL and had 'appsettings.json' set to '/VirtualDirectoryName/' and kept the one in 'appsettings.Development.json' as '/'.
Appended this variable when calling APIs.

Unable to use MVC Controller with ASP.Net Web API 2

What I have so far (that works)
I have an ASP.Net Web API 2 project. Well, at least that's what I remember creating when I set up the project, I am not sure how to confirm that.
I am using Visual Studio 2017, and .Net Framework version 4.6.
In terms of the API side of things, this is all working great. The API controllers are fine, I can get data, post data, etc.
Just a bit of additional information in case it matters, I have added SignalR to the project which has been configured.
As it may be important, here are my various configuration files:
Global.asax.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional }
);
}
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
What I am trying to do (that doesn't work)
However, I want to add a HTML page so the user can view some information (just static HTML stuff, nothing special). So I have created a standard MVC controller with an action like so (and the Index.cshtml view is in the correct Views folder):
public class NotificationsController : Controller
{
public ActionResult Index()
{
return View();
}
}
The problem is that this action never gets run (I have a breakpoint).
What I have tried to identify the problem
Now I get at this point, it could be loads of different things, so here is what I have tried so far to debug the issue:
When I access the URL in a browser (e.g. http://localhost:59461/Notifications), I get:
localhost is currently unable to handle this request.
HTTP ERROR 500
At first I thought maybe this is a routing issue, however in VS 2017 you can see that a request has failed for this action:
So surely the routing must be working correctly? Unfortunately, clicking the requests only confirms the 500 error and doesn't give any more information about the problem.
The only additional information I can find is in the Windows Event Viewer, in which I get the following error:
Application 'MACHINE/WEBROOT/APPHOST/PROJECTNAME' with physical root 'C:\PATH TO PROJECT FOLDER\' failed to start process with commandline '%LAUNCHER_PATH% %LAUNCHER_ARGS%', ErrorCode = '0x80070002 : 0.
But I have researched that error a lot and am yet to find a suitable solution or explanation for my problem.
I have also tried adding Application_Error but that isn't throwing any exceptions either.
At this point I don't know how to work out the cause of the problem. The only thing I can think of is that I need to configure something specifically to allow Web API projects to work with MVC controllers, but I can't find anything on that either.
What can I do to debug this problem correctly, and find the cause?
Urgh... so I solved the problem...
After stumbling across this post, there is a suggestion to delete the .vs folder in the Visual Studio solution folder. After doing this, and rebuilding the solution, it started working.
No idea what is in that folder that causes this problem exactly though, maybe something got corrupted or some sort of caching conflict, who knows...

How do I create pages in a web api project?

I want to display a default status page for my web api project (where instead some IIS message is displayed when I start the project). However it seems like I cannot create views in web api (there is no support for ActionResult).
In addition to a status page I will also use this information to create an api documentation page.
How can I achieve displaying html pages in this situation ?
If your default status page is static html, you don't have to use MVC. Just tell WebApi in your Startup.cs that you want to support static resources:
app.UseFileServer();
For creating an API documentation, maybe you could write that file on startup dynamically?
You can create regular controllers and views in a webAPI project the same as any MVC project. Just create a normal controller that does not inherit from ApiController. In your startup.cs make sure to configure at least a default route.
configuration.Routes.MapHttpRoute(
name: "someName",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Right click and select Add, then you should see controller at the top. Select one of the mvc controllers.

Custom url for different ActionResults?

I am in the process of moving old website to a new ASP .Net MVC website. The old site has pretty bad url naming scheme. I would love to ignore the old ways and just create new urls, however, A LOT of links point to the old links for SEO. Therefore, I have to maintain the older url.
So let's say this is the old url:
web.com/items/products/Hello-World-Hyphens
How do I input that on MVC?
I got ProductsController:
ActionResult HelloWorldHyphens() { return View(); }
Which will output to web.com/products/HelloWorldHyphens
However, I need it written in the old ways. Starting with /items/ and having hyphens in controller name.
Is there a way I can do something like this?
[OutputUrl="/items/products/Hello-World-Hyphens"]
ActionResult HelloWorldHyphens()
As you are moving to a new ASP MVC website then you can take advantage of attribute routing in MVC 5.
If you add attribute routing when you register your routes:
routes.MapMvcAttributeRoutes();
Then you can add routes on methods:
[Route("/items/products/Hello-World-Hyphens")]
ActionResult HelloWorldHyphens()

Categories