How to inject a route parameter into _Layout.cshtml - c#

I am trying to create a multitenant web app using ASP.Net Core v1.1.0. A single user may belong to multiple tenants (ie - an area manager who oversees multiple stores.) A user should also be able to go to the site and see some basic marketing views without logging in or having access rights to any tenant, but the functionality would be greatly limited.
Structurally, my project has not evolved much from the default ASP.Net Core MVC template so far...
In Startup.cs, I have defined my routes as follows to attempt to use subdomains to define which store the user is attempting to log into. "DEMO" would indicate that the user should only see the marketing views:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{tenant=DEMO}/{controller=Home}/{action=Index}/{id?}");
});
Since I have done that, the variable tenant can be magically used in a controller action definition like this, and it works fine:
public IActionResult Contact(string tenant)
{
ViewData["Message"] = "Your contact page for " + tenant + ".";
return View();
}
But I use _Layout.cshtml as a wrapper that contains most of the site navigation and some personalized elements, and I would like to do things like disable most of the navigation for the DEMO tenant and display a store-specific logo for all other tenants. Since to the best of my knowledge there is no controller associated with _Layout.cshtml, I need a different way of injecting this string. I have tried adding the following line to _ViewImports.cshtml:
#inject System.String tenant
and I get the error InvalidOperationException: No service for type 'System.String' has been registered. This leads me to believe that I need to add something to the ConfigureServices method in Startup.cs, but I don't know what I'm doing and have not been able to find any similar code samples online. I am visualizing something like this:
services.AddScoped<ITenantFactory, MySeeminglyUnnecessaryFactory>();
but even then, I don't know how to get the tenant route parameter to the factory, or how to go about implementing that. If I can just find a canned method of passing in the string, I'm sure I can work with that to do what I need to do as far as changing the look and feel of the layout, and I feel pretty comfortable restricting user access at the controller level. Thanks in advance for any advice or ideas!

You can use the View Context:
Hello user of #(ViewContext.RouteData.Values["tenant"])!
source:
https://github.com/aspnet/Mvc/blob/19331f95326b18bd6f0400526074da934bf65cf0/test/WebSites/RazorPagesWebSite/HelloWorldWithRoute.cshtml
https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Razor/RazorPageBase.cs

Related

MVC core routing not functioning as expected

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.

How do I get a list of all configured WebApi routes in asp.net MVC?

I'm trying to pass configuration values to bootstrap a single page AngularJs app hosted within an MVC app that utilises WebApi (see below from the Razor view, this is achieved by inheriting a BasePage and dependency injecting a Serializer/FinancialYears service).
<script>
var angularInitConfig = #Html.Raw(Serializier.Serialize(new {
financialYears = FinancialYearsService.GetFinancialYears()
}));
</script>
This works perfectly, however I would really like to be able to extend it to include the routes from my WebApi app to avoid having to configure the endpoints in both the WebApi app AND the AngularJs app individually.
Having poked around in the RouteTable.Routes class I can see that the information I require is available and accessible from within the view, however I've been unable to extract it.
So what I'd ideally like to do is generate a collection of objects as defined below from the RouteTable.Routes class, serialize them and spit them out in the bootstrap config for the AngularJS app to consume.
new {
name = "SomeService",
route = "api/{financialYearId}/someservice",
method = "POST"
}
Does anybody have an idea how to extract this information from RoutesTable.Routes? Is there an easier way to generate the data required?
NB. All WebApi routes are configured explicitly using the Routes attribute as such:
[HttpGet]
[Route("api/{financialYearId}/someservice")]
If you create default template asp.net Mvc or WebAPi using Visual Studio, you will get Help in Folder > Areas\HelpPage...and if you access your application in : Http://yourportapplication/api/Help if project webapi...
then, you can see the code how to get information...just for started what you looking for,....

Route Aliases in ASP.NET Web API 2

I have a Web API that allows users to access/manipulate resources that "belong" to other users. It contains many routes similar to this:
/users/{userId}/resource
I'd like a corresponding set of routes that do the same on behalf of the currently authenticated user:
/resource
Ideally I'd like to get the second set of routes working without doubling the number of actions in my controllers, but I'm struggling to find the right hook. Ideally, I'd like to add a MessageHandler (or something) that intercepts each request, checks if it matches a route, and if it doesn't, prepends "users/" + ID of the authenticated user to the route and checks again. What is the best way to accomplish this?
One constraint: I'm using attribute routing to implement the first set of routes and would ideally like to pull this off without sacraficing that.
The hook is the action selector, as you can see here: Routing and Action Selection in ASP.NET Web API.
You can implement your own action selector to achieve what you want. Here is a sample on how to do that: Magical Web API action selector. From the same page:
We will create a class that implements IActionSelector, as that would allow us to plug into the hook provided by the Web API under GlobalConfiguration.Configuration.Services.

Getting canonical url based on route data from outside MVC project

I have a MVC4 project and I have an external project that sends emails out that refer to areas of the site.
Rather than hardcoding urls into the emails I want to be able to make sure that I get the canonical url from the routing. I can reference the MVC project which means I believe I should be able to get any compile time information that it has (which includes routes and things).
I have managed to create a RouteCollection and fill it with my routes but I am now struggling with how I can query this collection. The only way I have found is the RouteUrl method on UrlHelper but this needs a UrlHelper with the appropriate routing data which I am having trouble creating. I also would hope that there would be better methods available to query a routecollection.
Essentially I have this route:
routes.MapRoute
(
"ShowBlog",
"blog/{shortcode}/{slug}",
new { controller = "Blog", action = "ShowBlog", shortcode = "", slug = "" }
);
And I want some code like GetUrl("ShowBlog", new {shortcode = "foo", slug="bar"}) that will return blog/foo/bar.
So how can I do this?
If you can wire up a UrlHelper from a test project, you can do it from any other external project. However you need to register the routes in the external project just like you do during Application_Start in the MVC project.
You will also need to mock up an HttpContextBase and a RequestContext, which means your external project will at least need to know the application path where your MVC project is installed. This would be easier if your external project could use a mocking library just like a test project would. Is that a possibility, or not?
If so, here is a link for how to do it with Moq. You don't need the controller, but you can use similar code for your HttpContext, RequestContext, and UrlHelper.

webapi error Multiple actions were found that match the request

I have an accountcontroller that has 2 actions here are the declarations:
HttpResponseMessage PostAccount([FromBody] Account account)
public HttpResponseMessage PostLogin([FromBody]string email,[FromBody] string pass)
Running in fiddler, I am receiving the error Multiple actions were found that match the request. Im a little confused on whats going on. Should I make two controllers just for Login and Register? is that standard practice.
You can only have one parameter that comes from the body in Web API. If you want multiple things in the body, you should wrap them in a container class.
The error you're getting is happening because you have two actions that start with "Post". You can either create separate controllers, which makes sense if you're posting different types of entities. Or you can use action-based routing and create a route that looks like this:
config.Routes.MapHttpRoute("ActionBased", "{controller}/{action}");
to distinguish between the two actions when you POST.

Categories