Web API 5.0.0-rc1 breaking default routes - asp.net-web-api

Before I updated from Web API 5.0.0-beta2 to 5.0.0-rc1 I could do something like this:
[RoutePrefix("api/v1/test")]
public class TestController : ApiController
{
[HttpGet]
public TestString Get()
{
return new TestString { str = "HELLO WORLD" };
}
}
so when I went to the URL /api/v1/test it would land on the Get() function.
After updating to Web API 5.0.0-rc1 I get a 404 when going to /api/v1/test
However, this works:
[RoutePrefix("api/v1")]
public class TestController : ApiController
{
[HttpGet("test")]
public TestString Get()
{
return new TestString { str = "HELLO WORLD" };
}
}
Can you explain why this doesn't work any longer?
** EDIT **
[HttpGet("")] works. Then it breaks on that Get() function.

I am not sure but I believe the Http[Get, Post, etc] type attributes have had their routing properties removed. This link hints at it:
http://blogs.microsoft.co.il/blogs/bnaya/archive/2013/08/28/asp-net-web-api-attribute-based-routing.aspx
be aware that most of the Attribute based Routing samples available
today on the web, is using old attribute like [PUT] or [HttpPut] which
is no longer supported on the latest bit (currently available from the
ASP.NET nightly build, http://www.myget.org/F/aspnetwebstacknightly/
), those attributes ware replaced with [Route] attribute.
Please see https://aspnetwebstack.codeplex.com/SourceControl/list/changesets and https://aspnetwebstack.codeplex.com/workitem/1206. Basically, the goal is to separate verb filters from attribute routing.

Related

Mvc6 versioned api actions with custom constraints

I'm making a versioned api with mvc6 and to do that I want to be able to specify for an action on which api version it should work.
My api route is: /api/{version}/... and so I want at a certain action to inspect the version route value and to see if this action is available for that version.
I want to be able to specify that as an attribute on the api action, so for example:
// This is the base api controller
[Route("api/{version:regex(^v[[0-9]].[[0-9]]$)}/[controller]")]
public abstract class ApiControllerBase { ... }
// This is an action in one of the sub classes
[HttpGet("foo")]
[ApiVersion("0.1", "0.2")] // Here! (this is params string[])
public object Foo()
{
// return
}
// This is an action in another sub class
[HttpGet("foo")]
[ApiVersion("1.0")]
public object Foo()
{
// return
}
My question is what should ApiVersion implement or extend for this to work? I don't believe action filters work as I want because I don't want to return a 404 when this doesn't match because other actions inside other controllers might be able to handle this (Later I might have HomeController with common actions and Home2Controller with extended actions that work only for 1.0).
Note that I'm not asking for an implementation of ApiVersionAttribute, I just need to know what mvc infrastructure I should hook into (action filters, route constraints, ...) that will let me create an attribute that can look into route values and say if this action is a match.
It took 4 hours analyzing the mvc6 source but it was worth it. I solved this using an action attribute implementing Microsoft.AspNet.Mvc.ActionConstraints.IActionConstraint.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ApiVersionAttribute : Attribute, IActionConstraint
{
public ApiVersionAttribute(string version)
{
Version = version;
}
public string Version { get; }
public int Order => 0;
public bool Accept(ActionConstraintContext context)
{
var routeData = context.RouteContext.RouteData;
// return ...
}
}
And then on a certain action:
[HttpGet("foo")]
[ApiVersion("0.1")]
public object Foo()
{
// return ...
}

ASP.NET Web API Action Routing

After a lengthy discussion in terms of how our API would need to behave due to limitations of the backend design, I'd like to have the following possibilities:
1. /students/251/employment <--- allow GET, PUT, POST
2. /students/251/employment/jobs <--- allow GET only
3. /students/251/employment/jobs/435 <--- allow all verbs
4. /students/251/employment/internships <--- allow GET only
5. /students/251/employment/internships/664 <--- allow all verbs
These cases are working for GET requests. I'm struggling when I try to do a PUT request for case #1 and #3:
Case #1 Error
No HTTP resource was found that matches the request URI '/students/251/employment/221'.,
No action was found on the controller 'Employment' that matches the name '221'.
Case #3 Error
The requested resource does not support http method 'PUT'.
Here's an abridged version of my controller methods:
public ApiEmploymentGetResult Get(long id) {
// code omitted
}
[HttpGet]
public IEnumerable<ApiJob> Jobs(long id) {
// code omitted
}
[HttpGet]
public IEnumerable<ApiOwnVenture> OwnVenture(long id) {
// code omitted
}
public void Put(long id, MyModel model) {
// breaks before getting here
}
My routing looks like this, but I'm not sure it's quite right, even though the GETs are working.
context.Routes.MapHttpRoute(
name: "V1/EmploymentApi",
routeTemplate: "api/v1/Employment/{action}/{jobId}",
defaults: new { controller = "Employment", jobId = RouteParameter.Optional, action = "Get" }
);
Case #1 seems to be conflicting due to the framework expecting an action rather than the 221. I'd like to be able to get all of these cases working.
You may want to look at Attribute Routing (Web API 1 and Web API 2).
public class StudentsController : ApiController
{
[HttpPut]
[Route("students/{studentId}/employment")]
public void UpdateStudentEmployment(int studentId) { ... }
[HttpPut]
[Route("students/{studentId}/employment/jobs/{jobId}")]
public void UpdateStudentEmploymentJob(int studentId, int jobId) { ... }
}

Asp.Net WebAPI and AttributeRouting - no overloaded attribute constructor?

I have just downloaded AttributeRouting NuGet package for WebAPI and having a problem within my controller.
I thought the way to use it was to have something like:
public class InboxController : ApiController
{
private IInboxService _inboxService;
public InboxController(IInboxService inboxService)
{
_inboxService = inboxService;
}
public IEnumerable<MessageModel> GetAll()
{
return _inboxService.GetAllMessages();
}
[HttpGet("Inbox/Count")]
public int GetInboxCount()
{
return _inboxService.GetMessageCount();
}
}
However I get the following error:
Error 2 'System.Web.Http.HttpGetAttribute' does not contain a constructor that takes 1 arguments
I need to get this up and running fairly quickly. Is there any reason why the HttpGet attribute doesn't have an overloaded constructor?
UPDATE
[GET("Inbox/EnquiryCount")]
public EnquiryCountModel GetEnquiryCounts()
{
var model = new EnquiryCountModel();
model.EnquiryCount = _inboxService.GetCustomerEnquiriesCount();
model.EnquiryResponseCount = _inboxService.GetCustomerEnquiryResponseCount();
return model;
}
In routes:
routes.MapHttpRoute("InboxEnquiryApi", "api/inbox/{action}", new { Controller = "Inbox" }, null, new WebApiAuthenticationHandler(GlobalConfiguration.Configuration));
When I hit the URL at 'api/inbox/EnquiryCount' I get the this error:
**No HTTP resource was found that matches the request URI 'http://localhost:49597/api/inbox/enquirycount'**
Attribute routing is supported in Web api 2
Here is the details: http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
This syntax has been changed in newer versions of the webapi. The [HTTPPOST] is now standalone and there is a new attribute for the route aptly name ROUTE which takes the route url
eg.
[Route("GetRes/{month}")]

What is wrong with this ASP.Net and Fiddler example?

I am using Visual Studio 2012 RC. I am using the default routes and have the following Web API controller:
public class FooController : ApiController
{
// GET api/foo
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/foo/5
public string Get(int id)
{
return "value";
}
// POST api/foo
public string Post(string abc)
{
Console.WriteLine("value: {0}", abc);
return "foo" + abc;
}
// PUT api/foo/5
public void Put(int id, string value)
{
}
// DELETE api/foo/5
public void Delete(int id)
{
}
}
I wanted to do a simple test of POST in Fiddler, so I have
Request Headers
User-Agent: Fiddler
Content-Type: application/json
Request Body
{"abc": "def"}
When I debug the request, the parameter abc comes in as null, not "def". Is there something wrong with my Fiddler syntax?
(1) By default, simple types are taken from the URI. To read a simple type from the request body, add the [FromBody] attribute to the parameter.
public string Post([FromBody] string abc)
(2) '{"abc": "def"}' defines an object with a property named "abc" - to send a JSON string, the request body should just be "def"
This answer comes from a link on the ASP.Net Web API site sending-html-form-data , which turns out to be Mike's blog post (I didn't realize that at first). The Web API team has made a few decisions with parameter binding that are quite different from normal MVC controllers.
The correct syntax for sending "simple types" is
public HttpResponseMessage PostSimple([FromBody] string value)
{
// code goes here
And in Fiddler, you put
=def //THIS CANNOT HAVE QUOTES AND = IS MANDATORY
OK, so here are the parts that work very differently from MVC.
You must use [FromBody], as Mike says.
You can only have 1 parameter. If you want more than 1 parameter, you have 2 choices: i) use url query parameters, instead of request body or ii) use a complex object (i.e. your own class).
The request body should be a simple =def and cannot use named parameters.

WebApi in MVC3, cannot hit my API Controller (500 internal error)

I have the below route mapped in my AreaRegistration:
public override void RegisterArea(AreaRegistrationContext context)
{
if (context != null)
{
context.Routes.MapHttpRoute(
name: "API_Default",
routeTemplate: "Areas/Test/AIO/api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
});
When I look at the Global.asax file, I can see the HttpRoute is being Registered, and is listed in the RouteTable.Routes as a {System.Web.Http.WebHost.Routing.HttpWebRoute}.
Problem is, when I go to the url... https://myRoot/Areas/Test/AIO/api/AioApi/test or https://myRoot/Areas/Test/AIO/api/AioApi, it's giving me a 500 internal server error.
I'm not sure how to view the actual error, when stepping thru the code I cannot see anything after it leaves Application_BeginRequest.
My controller code:
public class AioApiController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Any insight as to why I cannot hit the API controller? I can hit my regular MVC controller in the same context.
Any help is greatly appreciated!
One thing I found out with WebApi is that there are two Route collections: System.Web.Routing.RouteCollection and System.Web.Http.HttpRouteCollection. I believe (but can't remember) that you need to use the latter in order for your ApiController derivations to work properly (luckily the syntax is the same).

Resources