I'm trying to create a webapi solution consisting of the main project with other projects as additional endpoints. If the additional projects could not be dependency of the main, that would be great too.
Structure would be something like:
MainWebApiProject accessed as example.com/api/Values
SubProject1 accessed as example.com/sub1/Values
SubProject2 accessed as example.com/sub2/Values
I've created a solution with the 3 projects. I tried setting up the routing in the startup.cs of the MainWebApiProject using Areas and without.
example with the Areas
app.UseMvc(routes =>
{
routes.MapAreaRoute("subProject1_route", "subProject1", "sub1/{controller}/{action}/{id?}");
routes.MapAreaRoute("subProject2_route", "subProject2", "sub2/{controller}/{action}/{id?}");
routes.MapRoute("default_route", "{controller}/{action}/{id?}");
});
then the controller inside SubProject1
[Area("subProject1")]
[Route("sub1/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "sub 1 value1", "sub 1 value2" };
}
}
Just not quite sure what I'm missing or doing wrong, or if this can really be done.
I don't see how it is possible if the sub-projects aren't configured as dependency for the main project.
Areas in the new ASP.NET Core, is a feature to logically separate your namespace and views (where views are defined in the area). You would still need to have controllers in the main project to listen for that route.
I wonder, how do you run the sub projects? separately and totally independent of the main one? In that case you will definitely need a different protocol/port for them to be executed and routes will simply not work for them (within the main project).
Related
Is there any way to configure Swagger so that it generates UI & documentation only for a certain API controller within solution, or for a group of API controllers that belong to specific module (project withing solution)?
My solution consist of 50+ projects, several of them contains many API controllers, but I need to enable Swagger only for one of them, located in specific project.
I know about [ApiExplorerSettings(IgnoreApi = true)] attribute, but this way I would need to set this attribute to all API controllers which I don't need, and I would like just to mark the specific API controller which I want to use swagger on.
Is there any way to do that?
You can use conventions for this when registering your controllers.
If you create a new IActionModelConvention, something like this:
public class WhitelistControllersConvention : IActionModelConvention
{
public void Apply(ActionModel action)
{
if (action.Controller.ControllerName == "Item")
{
action.ApiExplorer.IsVisible = true;
}
else
{
action.ApiExplorer.IsVisible = false;
}
}
}
Then use it when configuring swagger in Startup:
services.AddControllers(c =>
{
c.Conventions.Add(new WhitelistControllersConvention());
});
Then you can control which controllers get included. In my example I'm just doing it off the name (only including ItemController), but you can change that to identify the controllers that you want however you want to do it.
I'm implementing Swagger in an integrations api and in that case it was necessary to separate these services by area for project organization issues, but I have the following problem, some controllers have common names in different areas and when swagger generates the documentation, even separated by area, if it has the same controller name together, I would like to know if there is any way to separate the controllers by area as well? Below are two integrations: Mercado Livre and Skyhub, both are in different areas, but if they notice the categories they joined because they have the same controller name.
! https://tribofoxcombr-my.sharepoint.com/personal/leonardo_silva_tribofox_com_br/_layouts/15/onedrive.aspx?id=%2Fpersonal%2Fleonardo_silva_tribofox_com_br%2FDocuments%2FCompartilhado%2FCapturar%2EPNG&parent=%2Fpersonal%2Fleonardo_silva_tribofox_com_br%2FDocuments%2FCompartilhado&slrid=3e37b79e-f020-7000-b380-a8a0e7785d97
Thanks.
You can use:
[ApiExplorerSettings(GroupName = "Group")]
public class SomethingController : Controller
{
And in declaration
services.AddSwaggerGen(options =>
{
options.SwaggerDoc(version,
new Info
{
Title = name,
Version = version
}
);
options.DocInclusionPredicate((_, api) => !string.IsNullOrWhiteSpace(api.GroupName));
options.TagActionsBy(api => api.GroupName);
});
I have created a .Net Core Web API program. I want to add a single view to it. Under the same project I add a "Views" folder. In the HomeController, where I am routing all my API requests, I created the following:
[HttpGet("view")]
public IActionResult Index()
{
return View();
}
In my Views folder I created a folder "Home" and added "Index.cshtml" to it.
When I launch the API, and navigate to "../view" I get to the return View(); line, but then it returns a 500 Internal Server Error.
This is what I don't like about the "automagical" approach of MVC convention. I have no idea where to link a view to a controller if the convention didn't work.
Update, this probably should have been my first course of action.
I added a new class to Controllers folder, and used the MVC Controller template in VS2015. I then added a view to match, and it still doesn't work automagically.
So for clarity, my project is: ASP.NET Core Web Application(.NET Core) with a Web API template. I have a "Jobs" controller class that was added at the start as 'Values' and I renamed. Then I added an MVC Controller Class named "HomeController" with the only method being "Index". I added a folder named "Views" and a subfolder named "Home", and added an MVC View Page named "Index.cshtml".
I tried to use "return View();" in the Index method, didn't work. I then tried to add [Route("Home/Index")] above the Index method. Either way, the URL will get me to my break point at "return View();" but it will never return the view.
Note : It's a little strange that you want to return a view in a Web API project, a Web API project is supposed to return some data structure, like json using return new JsonResult(your_json_here) for example.
Note 2 : you need the Microsoft.AspNetCore.Mvc framework (which is installed with the Web API template)
Anyway, you have different ways to configure routing in a asp.net core application :
Creating and extending default routes
Example of routing configuration in the Configure method :
app.UseMvc(routes =>
{
// You can add all the routes you need here
// And the default route :
routes.MapRoute(
name: "default_route",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" }
);
});
Using attributes
If you configure routing with attributes, don't forget the controller's one :
Example for the route /index :
[Route("")]
public class HomeController : Controller
{
[HttpGet]
[Route("[action]")]
public IActionResult Index()
{
return View();
}
}
Example for the route /home/index :
[Route("[controller]")]
public class HomeController : Controller
{
[HttpGet]
[Route("[action]")]
public IActionResult Index()
{
return View();
}
}
Example for the route /iputwhatiwant/actionnameiwant :
[Route("iputwhatiwant")]
public class HomeController : Controller
{
[HttpGet]
[Route("actionnameiwant")]
public IActionResult Index()
{
return View();
}
}
My screen of a .NET Core Web API project returning a view :
For more information, the official documentation is well-documented : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing
How are you running this webapp, from the Windows commandline?... can you give us the detailed HTTP500 error. It will probably reveal something different than routing errors because that usually gives 404.
[Route("[controller]")]
public class HomeController : Controller
Note the automagical "[controller]" in the Route definition, I think its necessary now
It took me a frustratingly long while to learn the routing convention as it was being developed, but it seems to have normalized out for a few versions. Check out this tutorial documentation on the subject in MVC: Attribute Routing in ASP.NET MVC 5, which is MVC not WebCoreAPI where it is likely based from. If you have a better documentation specific to Web Core API, use that.
This ASP.NET Web Core Build a web API tutorial documentation has some good points about what you seem to be trying to do. Specifically, the section title "Getting to-do items" has this code:
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(long id)
{
var item = _todoRepository.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
Looking at that with benefit of some measure of MVC routing experience, it looks particularly different from your approach in that the HTTP verb annotation member property value used is a query parameter.
Seeing I am guessing using known inexpertise, still, I think you need to get the attribute routing fixed, and maybe return an ObjectResult instead of a view, as NightOwl888 suggests. The server error might also have much more useful information along with the exception message.
EDIT: Sorry, I may have misunderstood your question. If you are trying to build an app that serves dynamic web pages instead of a WebAPI that serves data object results, this Build an MVC Web App tutorial, similar to the "Build a web API" tutorial I mentioned before might have your app structure problem answer. If you are trying to do both, you should probably start with the MVC Web App structure or use two separate projects.
The (only) way I have got this working is to declare the path as an attribute on the action - in the same way you have done but with the format as below (Controller/Action):
[HttpGet("Home/Index")]
public IActionResult Index()
{
return View();
}
I was missing:
"preserveCompilationContext": true
in the build options of my project.json
I have a Solution structure like this:
MyApp.Core
--Properties
--References
--bin
--Events
|EventHandlers.cs
--Directory
--Controllers
|DirectoryController.cs
--Helpers
|ContextHelpers.cs
--Models
|DirectoryModel.cs
--AnotherSite
--Controllers
--Helpers
--Models
--Services
--Shared
--Controllers
|HomePageController.cs
--Helpers
|Extensions.cs
|app.config
|packages.config
MyApp.Umbraco
--Properties
--References
--bin
etc........
--Views
--Directory
--Partials
|DirectoryFilters.cshtml
|DirectoryBase.cshtml
|DirectoryHome.cshtml
|FDirectory.cshtml
|SDirectory.cshtml
--Partials
--Shared
|Base.cshtml
|Web.config
etc........
My Umbraco instance uses the models and controllers from my "Core" project. There is nested directory structure, because of multiple websites in one installation, in the "Core", and also in the "Views" directory in the Umbraco instance.
I am still fairly noob to .NET MVC, and I understand route hijacking, but the documentation for Umbraco's routing is slim. I have the following:
EventHandlers.cs
namespace MyApp.Core.Events
{
/// <summary>
/// Registers site specific Umbraco application event handlers
/// </summary>
public class MyAppStartupHandler : IApplicationEventHandler
{
public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
}
public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
RegisterCustomRoutes();
}
public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
}
private static void RegisterCustomRoutes()
{
// Custom Routes
RouteTable.Routes.MapUmbracoRoute(
"FDirectory",
"fdirectory/{id}",
new
{
controller = "Directory",
action = "FDirectory",
id = UrlParameter.Optional
},
new PublishedPageRouteHandler(1000));
RouteTable.Routes.MapUmbracoRoute(
"SDirectory",
"sdirectory/{id}",
new
{
controller = "Directory",
action = "SDirectory",
id = UrlParameter.Optional
},
new PublishedPageRouteHandler(1001));
RouteTable.Routes.MapUmbracoRoute(
"HomePage",
"",
new
{
controller = "HomePage",
action = "Index",
id = UrlParameter.Optional
},
new PublishedPageRouteHandler(1002));
}
}
public class PublishedPageRouteHandler : UmbracoVirtualNodeRouteHandler
{
private readonly int _pageId;
public PublishedPageRouteHandler(int pageId)
{
_pageId = pageId;
}
protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
{
if (umbracoContext != null)
{
umbracoContext = ContextHelpers.EnsureUmbracoContext();
}
var helper = new UmbracoHelper(UmbracoContext.Current);
return helper.TypedContent(_pageId);
}
}
}
DirectoryController.cs
namespace MyApp.Core.Directory.Controllers
{
public class DirectoryController : RenderMvcController
{
public DirectoryController() : this(UmbracoContext.Current) { }
public DirectoryController(UmbracoContext umbracoContext) : base(umbracoContext) { }
public ActionResult FDirectory(RenderModel model)
{
return CurrentTemplate(new DirectoryModel(model.Content));
}
public ActionResult SDirectory(RenderModel model)
{
return CurrentTemplate(new DirectoryModel(model.Content));
}
}
}
So Umbraco does not install with an App_Start folder. I would like to know what the best approach is for a multi-site installation of Umbraco for registering the routes to the controllers. My implementation works, but it seems like I shouldn't have to create actions for every single page I am going to have in a site, in every controller. I know Umbraco has its own routing, so using Umbraco concepts, ASP.NET MVC concepts, and whatever else is available, what is the best way to implement this type of solution structure? Should I even worry about using a RouteConfig.cs and create a App_Start directory? Or is what I am doing the best approach? Should I use IApplicationEventHandler or ApplicationEventHandler?
Also, I have to hard code the node ID's. I've read that there is a way to Dynamically? And example of this would be great.
Examples of the best way to implement a structured multi-site Umbraco MVC solution is what I am asking for I guess, in regards to routing the controllers, with some detail, or links to strong examples. I have searched and researched, and there are bits and pieces out there, but not really a good example like what I am working with. I am going to have to create a RouteMap for every single page I create at this point, and I don't know if this is the most efficient way of doing this. I even tried implementing a DefaultController, but didn't see the point of that when your solution is going to have multiple controllers.
I'm not entirely sure what you are trying to achieve with this, but I'll try to explain how it works and maybe you can clarify afterwards.
I assume you have the basics of Umbraco figured out (creating document types + documents based on the document types). This is how Umbraco is normally used and it will automatically do routing for you for each of these "content nodes" (documents) you create in a site.
So create a document named document1 and it will be automatically routed in your site at URL: http://localhost/document1. By default this document will be served through a default MVC controller and it will all take place behind the scenes without you having to do anything.
Route hijacking allows you to override this default behavior and "shove in" a controller that lets you interfere with how the request is handled. To use hijacking you create a RenderMvcController with the alias of your document type. That could be HomePageController : RenderMvcController.
This controller should have an action with the following signature:
public override ActionResult Index(RenderModel model)
In this action you are able to modify the model being sent to the view in any way you like. That could be - getting some external data to add on to the model or triggering some logic or whatever you need to do.
This is all automatically hooked up by naming convention and you will not have to register any routes manually for this to work.
The other type of Umbraco MVC controller you can create is a SurfaceController. This one is usually used for handling rendering of child actions and form submissions (HttpPost). The SurfaceController is also automatically routed by Umbraco and will be located on a "not so pretty" URL. However since it is usually really not used for anything but rendering child actions and taking form submits, it doesn't really matter what URL it is located at.
Besides these auto-routed controllers you are of course able to register your own MVC controllers like in any standard MVC website. The one difference though is that unlike a normal ASP.NET MVC website, an Umbraco site does not have the automagical default registration of controllers allowing the routing to "just work" when creating a new controller.
So if you want to have a plain old MVC controller render in an Umbraco site without it being related to a document/node in Umbraco, you would have to register a route for it like you would do in any other MVC site. The best way of doing that is to hook in and add it to the Routes using an ApplicationEventHandler class. That will automatically be triggered during application startup - essentially allowing you to do what you would normally do in App_Start.
Just to be clear though - if you plan on using data from Umbraco, you should not be using normal MVC controllers and should not require any manual route registration to be done. You usually want to render a template/view in context of a document/node created in Umbraco (where you can modify data/properties of the document) and then the route hijacking is the way to go.
From what it looks like, it could seem that the correct way to do what you are trying to do is to simply create two document types:
FDirectory and SDirectory
You click to allow both of these to be created in root and then you create documents called FDirectory and SDirectory and they will be automatically routed on these URLs. Creating a RenderMvcController's called FDirectoryController : RenderMvcController will then make sure it is used to hijack the routing whenever that page is requested.
If you're simply trying to set up a multi-site solution I would suggest you create a Website document type and create a node for each site you want, in the root of your Umbraco content tree. Right click each of these nodes and edit the hostname to be whatever you need it to be. This can also be some "child url" like /fdirectory or /sdirectory in case you need to test this on localhost without using multiple hostnames.
Hope this gives you the pointers needed, otherwise try to explain what you are trying to do and I'll see if I can refine my answer a bit!
I have a webapi2 C# projects on which I have all the controllers in a controllers folder.
I now have some functionality that I want to add but I want to put it on a different visual studio project that will have its own controllers (and models and views) folder. Is this possible so that I can create some kind of module that will be loaded by webapi2?
Web Api relies on the IHttpControllerSelector for choosing the api controller for handling a request, of which has a default implementation which relies on IAssembliesResolver for resolving the assemblies to search for the api controllers.
In the least minimum change, you can replace this assembly resolver with a custom implementation which will load other libraries for you.
A very naive example might look like the following:
public class CustomAssemblyResolver : IAssembliesResolver
{
public List<string> PluginNames { get; set; }
public CustomAssemblyResolver()
{
PluginNames = new List<string>();
//Add the custom libraries here
PluginNames.Add("Your_Second_Library");
}
public ICollection<Assembly> GetAssemblies()
{
var asms = AppDomain.CurrentDomain.GetAssemblies().ToList();
foreach (var name in PluginNames)
{
var asmPath = System.IO.Path.Combine(HostingEnvironment.MapPath("~/"), name + ".dll");
try
{
var asm= System.Reflection.Assembly.LoadFrom(asmPath);
if(!asms.Contains(asm))
asms.Add(asm);
}
catch (Exception)
{
}
}
return asms;
}
}
You can then replace the default resolver with this code
config.Services.Replace(typeof(IAssembliesResolver), new CustomAssemblyResolver());
inside your Register method of WebApiConfig class.
Then, copy all your additional libraries with controller classes to the bin directory and you are done.
If you need even further customization for controller selection, you can go for custom implementation of IHttpControllerSelector and replace the existing implementation in a similar fashion.
You can create the functionality in a library project, and then reference that project to the your webapi2 and your other visual studio project. Basically you will have three solutions; two webapi solutions and one library solution. The library solution will contain the required logic needed for the two webapi solutions.
You shouldn't have a need for controllers in two projects.
If the controllers are for COMPLETELY different business domains, make two api's in IIS w/ two solutions.
If they are similar, create all of your controllers in the web project. Then have these controllers call out to separate application services.
public CustomerAccountsController : ApiController
{
private CustomerAccountService _customerAccountService; // lives in application layer project
public CustomerAccountsController()
{
// di stuff
}
public HttpResponseMessage PutCancelAccount( int accountId )
{
// exception handling + logging
_customerAccountService.CancelAccount(accountId);
// return status code if success, or if an exception
}
}
public OrderController : ApiController
{
private OrderService _orderService; // lives in application layer project
public OrderController()
{ //di stuff
}
public HttpResponseMessage PostCreateOrder(CreateOrderRequest createOrderRequest)
{
// exception handling + logging
_orderService.TakeOrder(createOrderRequest);
// return status code if success, or if an exception
}
}
So, most of your logic should hide behind application layer services, and these services should have methods that map 1-1 to use cases. If your business domain for these two applications are completely different, just create two separate solutions and two separate IIS applications/api's
No, it is not possible. The maximum you can do is create a class library that will compile as a DLL, then reference that DLL in your WebApi. Otherwise, you will be obliged to either put everything in the same application (WebApi) or create two different WebApi applications.
Depending on your needs...
My advise, just put the 2 controllers on a single project and create a helper/service folder/class in your other project and call those services whenever you need.
This is not really the answer to your question but I believe this will help. Normally we create a solution using this folder structure, I hope this helps:
MyTeamSolution
- MyTeam.Core = Class libraries
> Models
> Model1.cs
> Model2.cs
> Model3.cs
> Interface
> ISomeInterface.cs
> Helpers
> HelperClass.cs
- MyTeam.Api = webapi project
> Model1Controller.cs
> Model2Controller.cs
> Model3Controller.cs
- MyTeam.Web = mvc project
> Controllers
> Models
> Views
> etc.
- MyTeam.Sql = Class libraries
> MyTeamDbContext.cs