I'm trying to implement api versioning following this tutorial. So in my startup I have:
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
configuration.MapHttpAttributeRoutes(constraintResolver);
configuration.AddApiVersioning()
and my controllers:
[Route("api/v{version:apiVersion}/my")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class MyV1Controller
[Route("api/v{version:apiVersion}/my")]
[ApiVersion("3.0")]
public class MyV3Controller
When I request for http://localhost/api/v1.0/my I get an error
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.\r\n\r\nThe request has found the following matching controller types: \r\nMyV1Controller\r\nMyV2Controller
Could you please advice how to make controller versioning to work?
I took a break and I remembered that in my project I have a custom IHttpControllerSelector implementation which extends DefaultHttpControllerSelector.
configuration.Services.Replace(typeof(IHttpControllerSelector), new ApiControllerSelector(config));
After I removed it versioning started to work.
Executing configuration.AddApiVersioning sets ApiVersionControllerSelector in ServicesContainer. It was accidently replaced with my custom implementation.
Related
I am trying to refactor an old setup, the end goal will be that I will have a functional REST API. I am getting rid of an old NuGet service and I sort of have to rebuild everything now. I am trying to set it up using ASP.NET Web API. This will include 60+ routes (like "www.website.com/cars/{id}/engine" "www.website.com/inventory/{id}" etc..)
I have tried attribute routing and conventional routing, and nothing seems to work. I get 404's no matter what I seem to try. I am probably just not doing either of them correctly.
Here is how I am setting up configuration (I am using a self hosting package btw but this is the main configuration point):
public class SomeHostClass
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//If I am trying attribute based routing, uncomment this
//config.MapHttpAttributeRoutes();
Register(config.Routes);
// I think I need this at all times?
app.UseWebApi(config);
}
public void Register(HttpRouteCollection routes)
{
// One of many other routes.MapHttpRoute() calls
routes.MapHttpRoute(
name: "Cars",
routeTemplate: "cars/{id}/engine",
defaults: new
{
controller = "CarController",
action = "Get"
},
constraints: null);
}
}
Then here is my Car Controller:
public class CarController : ApiController
{
[HttpGet]
public object Get([FromUri] CarDTO carObject)
{
// Some code calling a private worker method
return WorkerMethod();
}
private object WorkerMethod()
{
// Worker method do stuff, return
}
}
This Get() method is never called and a 404 is returned.
Another note: I have tried using reflection to register all the routes, which slims down the code. I'll debug it and it looks like all the routes have been configured correctly within the HttpConfiguration.routes but I will get 404's still. Even without reflection doing it the way shown above- if I debug and look at the values they all seem correct, but don't work.
Another Note: I should also mention that this is the error I usually get along with the 404.
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:8000/cars/1/engine'.","MessageDetail":"No type was found that matches the controller named 'CarController'."}
How do I get these routes recognized and stop returning 404's and start returning my data?
If this is a bad way of going about this- what's the best way?
TL;DR
I need a way to programtically choose which RoutePrefix is chosen when generating URLs based on the properties of a user in my MVC app
Not TL;DR
I have an MVC 4 app (with the AttributeRouting NuGet package)
Due to the requirements of the hosting environment I have to have two routes for a lot of my actions so that the hosting environment can have different permissions for access.
I am solving this by decorating my controller with with [RoutePrefix("full")] [RoutePrefix("lite)]. which allows each action method to be accessed via /full/{action} and /lite/{action}.
This works perfectly.
[RoutePrefix("full")]
[RoutePrefix("lite")]
public class ResultsController : BaseController
{
// Can be accessed via /full/results/your-results and /lite/results/your-results and
[Route("results/your-results")]
public async Task<ActionResult> All()
{
}
}
However, each user should only use either full or lite in their urls, which is determined by some properties of that user.
Obviously when I use RedirectToAction() or #Html.ActionLink() it will just choose the first available route and won't keep the "correct" prefix.
I figured I can override the RedirectToAction() method as well as add my own version of #Html.ActionLink() methods.
This will work, but it will involve some nasty code for me to generate the URLs because all I get is a string representing the action and controllers, but not the reflected types. Also there might be route attributes such as in my example, so I am going to have to replicated a lot of MVCs built in code.
Is there a better solution to the problem I am trying to solve?
How about something like:
[RoutePrefix("{version:regex(^full|lite$)}")]
Then, when you create your links:
#Url.RouteUrl("SomeRoute", new { version = "full" })
Or
#Url.RouteUrl("SomeRoute", new { version = "lite" })
You could even do the following to just keep whatever was already set:
#Url.RouteUrl("SomeRoute", new { version = Request["version"] })
I ended up finding a solution to this
I just overrided the default routes to include this. ASP.Net automatically keeps the usertype value and puts it back in when it regenerates the routes
const string userTypeRegex = "^(full|lite)$";
routes.Add("Default", new Route("{usertype}/{controller}/{action}/{id}",
new { controller = "Sessions", action = "Login", id = UrlParameter.Optional }, new { usertype = userTypeRegex }));
I found that this didn't work with the Route or RoutePrefix attributes, and so I had to remove all of them. Forcing me to add specific routes in these cases
routes.Add("Profile-Simple", new Route("{usertype}/profile/simple",
new { controller = "ProfileSimple", action = "Index" }, new { usertype = userTypeRegex }));
I thought that a half-dozen hard coded routes in my RouteConfig file was a better solution than having to manually add values to each place I generated a URL (as in Chris's solution).
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/
I have an existing .NET 4 console application which I want to start exposing a REST API. I'm using the Microsoft self host Web API library to have it expose that API, but I'm having trouble understanding how the routing path gets developed.
Here is an example of a controller, which should expose some database objects my console application already handles:
public class UserController : ApiController
{
UserInformation[] users;
public IEnumerable<UserInformation> GetAllUsers()
{
// snip.
}
public UserInformation GetUserById(int id)
{
// snip.
}
}
And I'm exposing my API in Program.cs like so:
var config = new HttpSelfHostConfiguration("http://localhost:8800");
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
using (var server = new HttpSelfHostServer(config)) { // code }
Given the above code, I'd expect that I could get resources by making http requests like http://localhost:8800/api/users and http://localhost:8800/api/users/1, but those don't seem to work. How does the controller part of the GET request get created? It doesn't seem like users is the correct routing path for the API, but I'm not sure what goes there.
Thanks for any help
That's because your controller is called UserController and not UsersController. Either rename your controller to UsersController or modify your request to go to http://localhost:8800/api/user.
This should solve the problem.
I created one solution with two projects: one is a class library with a Self Host Web API (created with the help of http://www.asp.net/web-api/overview/hosting-aspnet-web-api/self-host-a-web-api), the second is a windows service created with TopShelf. The purpose of this solution is to have a status report on the service with the use of Web API.
Everything works fine, but when I recreate my solution within a target solution the whole application does not work properly. The Windows Service seems to be working, but when I type localhost:8080/Test which is suppose to view OK (and it does in the separate test solution mentioned at the beginning) it throws an error (viewed as an xml):
Message: No HTTP resource was found that matches the request URI 'http://localhost:8080/Test'.
MessageDetail: No type was found that matches the controller named 'Report'.
There is a ReportController (inheriting from ApiController) in the project that contains the SelfHost but somehow it's "visible". I took a guess (a stupid guess, I believe) and moved it to the windows service project but it's also not working.
Can someone tell me what is the problem I'm facing? Why does it not see the controller if it has in a simple solution?
EDIT:
My routing looks like this:
var config = new HttpSelfHostConfiguration(String.Format("http://localhost:{0}", port));
config.Routes.MapHttpRoute("API Default", "{action}", new { controller = defaultControllerName });
where
defaultControllerName = "Report";
I am ashamed to admit it, but the reason why it didn't work lay in the controller class not having an access modifier. Making it public fixed the bug.
the Class and method must be public
public class PrintController: ApiController
{
//[HttpGet, Route("api/Print/Getp")]
public string Get()
{
var ob = new List<string>();
foreach (var item in File.ReadLines(#"c:\PrintService\pr.txt"))
{
string i = item;
ob.Add(i);
}
var json1 = JsonConvert.SerializeObject(ob);
return "ok";
}
}
and my route config is this:
_config.Routes.MapHttpRoute("DefaultHttpRoute", "api/{controller}");