I have a Product.Core MVC project. This project has some common API controllers.
I have another MVC project "Product" that references Product.Core.
The API controllers work as expected. I can call APIs defined in both solutions.
I now want to extended one of the API controllers in Product.Core. Say XXXApiController.
I have the same controller defined in the Product project.
namespace Product
{
public class XXXApiController : Product.Core.XXXApiController
{
public new Task<XXXResponse> MyApiAsync()
{
var response = await base.MyApiAsync();
// do extra code here
return response;
}
}
}
However, after this change it seems the XXXApiController is no longer available. I get a 'No HTTP resource was found' error.
How can I extend an API controller that lives in another MVC 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.
Project 1 is called DemoMVCProj.proj. Its main files are index.html and DemoController.
class DemoController : Controller {
// ----index action here----
[HttpPost] public ActionResult CallAction()
{
// --some logic here--
// --return action result--
}
}
There is another project called DemoMVCWebApiProj.proj, where more than one web API is created.
There is a third project called DemoUI.proj, which is an AngularJS project for creating views.
ApiControllers are called under this project using DataFactorys.
How can I call DemoController (from the DemoMVCProj project) from one of the view in the DemoUI project?
Since DemoUI sounds like it's angular based, you would use $http to do any call's
https://docs.angularjs.org/api/ng/service/$http
If your routing is normal, you would use the url of /controller/action
If you simply wanna go to a view in the MVC proj without ajax loading, simply write an tag with a href="/controller/action"
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 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
I have two Web API projects and I have a MarketController and I need to extend the Api controller so I did it.
I created a BaseController class and inherit from ApiController like this:
public class BaseController:ApiController { }
so far so good, it's working fine:
public class MarketController : BaseController
{
public MarketController() : base()
{
}
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
But I wanted to do it in another class library called BLL, so I moved the BaseController class to the BLL class library and I referenced it in the Web API project.
When I did this, the api stopped working.
The response is :
{
"Message": "No HTTP resource was found that matches the request URI someurl/api/market.",
"MessageDetail": "No type was found that matches the controller named 'market'."
}
No need to implement custom ControllerFactory or AssemblyResolver classes.
This scenario will "just work" provided you add the Microsoft.AspNet.WebApi.Core nuget package to the assembly containing the base class.
In my case I'd just added a reference to the System.Web.Http.dll which will compile, but the controllers will not load properly. Adding the Nuget package got everything working with no code changes.
By default MVC looks for all controllers in same assembly of mvc application. The default controller factory creates controller instance based on string 'ControllerName+Controller' like MarketController where market comes from url market/actionname.It will look for MarketController in the same assembly as mvc application.
To put controller in separate assembly you will have to create your own controller factory or you will have to specify assembly name is app start.
Once you've created your own custom ControllerFactory, you add the following line to Application_Start in global.asax to tell the framework where to find it:
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
Or for simple cases like yours you can do this :
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" },
new[] { "BLLAssembly.Controllers" }
);
Here BLLAssembly.Controllers is namespace for your BaseController in BLL assembly.
There is one more advanced way using custom assembly resolver ,i.e IAssembliesResolver
The below article tells how to do this with Web Api also,
http://www.strathweb.com/2012/06/using-controllers-from-an-external-assembly-in-asp-net-web-api/