I am using WebApi and Swagger/Swashbuckle.
I have one version of all my controllers but for one of the controllers, I want to introduce versioning but keep the old version as well.
So for ControllerA where there is just one version I want to carry on doing this.
http://mysite/api/ControllerA
For ControllerB where there are just two versions I want
http://mysite/api/v1/ControllerB
http://mysite/api/v2/ControllerB
I can always redirect the unversioned url to v1 if that is possible.
http://mysite/api/ControllerB
I think I fundamentally need to use a SingleApiVersion because most of my controllers only have one version. Inside SwaggerConfig I have the line:
c.SingleApiVersion("v1", "MySite.Api");
However, when I try this I cannot see MyController in the Swagger UI or call it in Postman
[Route("api/v{version:apiVersion}/[controller]")]
public class MyController : ApiController
{
[ApiVersion("1.0")]
[HttpPost]
public HttpResponseMessage MyMethod([FromBody] MyRequest myRequest)
{
return MyCode(myRequest);
}
[ApiVersion("2.0")]
[HttpPost]
public HttpResponseMessage MyMethod([FromBody] MyRequest myRequest)
{
return MyNewCode(myRequest);
}
}
I think I am probably mixing up the Single and Multiple attributes but I don't want to use multiple versions of the whole API but I only have one with just new versions of one controller. Can this be done?
Have you tried this?
[ApiVersion( "2.0" )]
[ApiVersion( "3.0" )]
[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorldController : ApiController
{
public string Get() => "Hello world v2!";
[MapToApiVersion( "3.0" )]
public string GetV3() => "Hello world v3!";
}
https://github.com/microsoft/aspnet-api-versioning/wiki/Versioning-via-the-URL-Path
Related
In the code below there are two methods annotated with #GetMapping annotation, one expects empty path, another one expects a path variable.
#Controller
#RequestMapping("/")
public class BasicController {
#GetMapping()
public String get(Model model) {
// doing something
}
#GetMapping("/{variable}")
public String getWithPathVar(#PathVariable("variable") String variable, Model model) {
// doing something different
}
}
Problem: When the app is running and I hit "www.myurl.com/" it enters both methods even though there is no path parameter. How can I fix this?
If so it sounds like a bug or some misconfiguration with filters. I can't reproduce this behaviour on the Spring 5.2.7. Here's an article that explains how Spring works under the hood.
If you can't upgrade the Spring version you can use only single endpoint as a workaround.
#GetMapping("/{variable}")
public String getWithPathVar(#PathVariable("variable") String variable, Model model) {
// doing something different
if(variable != null) {
// fulfill the normal workflow
} else {
// call ex get() workflow
}
}
I am using .NET Core 2.2 and I have the controller below
[Route("api/[controller]")]
[ApiController]
public class CarsController : ControllerBase
{
[HttpPost]
[Route("api/cars/search")]
[ActionName("search")]
public ActionResult<IEnumerable<string>> SearchForCar([FromBody] SearchCriteria searchCriteria)
{
return new string[] { "value1", "value2" };
}
}
I am new to pure web api controllers.
I am confused about why when I post json to
http://localhost:51285/api/cars/search
I get 405 method not allowed?
I would normally have a route of
[Route("api/[controller]/action")]
That does work (once I remove route from the method attributes), but this wasn't the default provided in the template
Could someone let me know what I am missing?
Am I breaking convention by changing to
[Route("api/[controller]/[action]")]
Cheers
Paul
Since you not using the root slash in your action "/", the MVC middleware will search concatenating the route for controller and the action, should work like this
[Route("/api/[controller]/[action]")] // Check the root slash as first character
public ActionResult<IEnumerable<string>> SearchForCar([FromBody]
SearchCriteria searchCriteria)
Or like this
[Route("api/[controller]/[action]")] [ApiController] public class
CarsController : ControllerBase
Or
In controller:
[Route("api/[controller]")] [ApiController] public class
CarsController : ControllerBase
In action:
[Route("search")] // [action] Takes the method name
public <ActionResult<IEnumerable<string>>
SearchForCar([FromBody] SearchCriteria searchCriteria)
Having a heck of a time here... (Web API 2.1, .NET 4.5.1)
I had one controller that worked perfectly:
[RoutePrefix("v1/members")]
public class MembersController : ApiController
{
[Route("{id}")]
public Member Get(string id)
{
DataGateway g = new DataGateway();
return g.GetMember(id);
}
}
Works as intended and desired like:
/v1/members/12345
But I added a new controller today and it doesn't seem to be registered or recognized at all. It doesn't get added to the Help pages and it returns a 404 Not Found when trying to access it:
[RoutePrefix("v1/test")]
public class Test : ApiController
{
[Route("{id}")]
public string Get(int id)
{
return "value";
}
}
Like I stated, the new controller doesn't show up in the Help pages and returns a 404:
/v1/test/12345
What am I doing wrong?
EDITED TO ADD:
I installed tracing and it doesn't even seem to be hitting that. The first controller works fine and shows a trace, the new Test controller doesn't show any trace info.
EDIT 2:
Updated example code to better match my actual code, which was the problem all along.
The problem was here:
[RoutePrefix("v1/test")]
public class Test : ApiController
Changed to:
[RoutePrefix("v1/test")]
public class TestController : ApiController
Seems the almighty important word "Controller" needs to be in the class name for the magic to happen.
I am playing around with the idea of having a base controller that uses a generic repository to provide the basic CRUD methods for my API controllers so that I don't have to duplicate the same basic code in each new controller. But am running into problems with the routing attribute being recognized when it's in the base controller. To show exactly what the problem I'm having I've created a really simple WebAPI controller.
When I have a Get method in the main Controller and it inherits from the ApiController directly I don't have any problems and this works as expected.
[RoutePrefix("admin/test")]
public class TestController : ApiController
{
[Route("{id:int:min(1)}")]
public string Get(int id)
{
return "Success";
}
}
When I move the Get method into a base controller it is returning the contents of the 404 page.
[RoutePrefix("admin/test")]
public class TestController : TestBaseController
{
}
public class TestBaseController : ApiController
{
[Route("{id:int:min(1)}")]
public string Get(int id)
{
return "Success";
}
}
Some more interesting notes:
I can access the action at GET /Test/1. So it is finding it based on the default route still.
When I try to access POST /admin/test, it returns the following JSON
{
"Message":"No HTTP resource was found that matches the request URI 'http://test.com/admin/test'.",
"MessageDetail":"No type was found that matches the controller named 'admin'."
}
Does anyone know of a way to get the routing to work with attributes from a base controller?
Attribute routes cannot be inherited. This was a deliberate design decision. We didn't feel right and didn't see valid scenarios where it would make sense to inherit them.
Could you give a more realistic scenario as to where you would want to use this?
[Update(3/24/2014)]
In the upcoming 5.2 release of MVC Web API, there is going to be an extensibility point called System.Web.Http.Routing.IDirectRouteProvider through which you can enable the inheritance scenario that you are looking for here. You could try this yourself using the latest night builds(documentation on how to use night builds is here)
[Update(7/31/2014)]
Example of how this can be done in Web API 2.2 release:
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
//---------
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
// inherit route attributes decorated on base class controller's actions
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
Using Web API 2.2, you can:
public class BaseController : ApiController
{
[Route("{id:int}")]
public string Get(int id)
{
return "Success:" + id;
}
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
as outlined here: http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22
Got it.
[Route("api/baseuploader/{action}")]
public abstract class BaseUploaderController : ApiController
{
[HttpGet]
public string UploadFile()
{
return "UploadFile";
}
}
[Route("api/values/{action}")]
public class ValuesController : BaseUploaderController
{
[HttpGet]
public string Get(int id)
{
return "value";
}
}
One caveat here is that the route action paramter must be the same as the action name. I could not find a way to get around that. (You cannot rename the route with a RouteAttribute)
Why does T4MVC uses virtual for controller methods? Changing a
public ActionResult Details (string Id)
to:
public virtual ActionResult Details (string Id)
I have already seen other questions about T4MVC but didn't understand why.
Usually if a framework/library needs virtual methods (see also Nhibernate) it means somewhere/sometime your methods will be overridden.
So T4MVC marks your action methods as virtual because it's overrides them.
Lets take a simple controller:
public partial class HomeController : Controller
{
public virtual ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
If you go to the generated HomeController.generated.cs under the T4MVC.tt you will find a generated class which inherits from your controller and overrides your action method:
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class T4MVC_HomeController: MvcApplication8.Controllers.HomeController {
public T4MVC_HomeController() : base(Dummy.Instance) { }
public override System.Web.Mvc.ActionResult Index() {
var callInfo = new T4MVC_ActionResult(Area, Name, ActionNames.Index);
return callInfo;
}
}
I haven't used T4MVC so I don't know why and for what purpose T4MVC creates this generated class.
The only benefit I can see for making them virtual is to allow the developer to 'Go to Implementation/Definition' where The T4MVC helpers are used. This works because the Controller's type on the static Helper 'MVC' is the base controller type.
public static partial class MVC
{
public static HomeController Home = new T4MVC_HomeController();
}
So in the following snippet, Go to Definition on the Action Name will go to Base Implementation:
#Url.Action(MVC.Home.Index())
+1 David Ebbo for such an intuitive feature. I was mind blown when I realized this!
PS: this does not work for the parameterless actions added via the partial function, instead they navigate to the generated code, unfortunately.