Extend MVC Api Controller Name - c#

Right now I have two controllers, one for MVC another one for API.
// API
public class UsersController : ApiController
{
// GET: api/Users <- works great
public IQueryable<User> GetUsers() {}
// **Not sure if this one is correct!**
public IHttpActionResult GetUsersChart([FromURI]int id)
{
return Ok(repo.GetUsersChart(id));
}
}
I am trying to understand on how to call my Charts generation api within C# code with HttpWebRequest?
Something like this:
HttpWebRequest createChartRequest =
WebRequest.Create(String.Format("{0}/api/Users/Chart/?id={1}", ServerUrl, 1));
would give me HTTP 500 error.
Update:
Here is routing configuration:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }

A response code of 500 means that you have some error in your API action. It has nothing to do with your route or how you're requesting it. If this is in development, inspect the response in your browser's developer tools and you should be able to see a description of the actual server error there. If this is in production, you'll need to log the error somehow such as with ELMAH so you can see what's causing the issue.

Related

Enabling OData Filtering on WebApi 2 ASP.NET 4.6.1

I have a legacy WebApi running on ASP.NET 4.6.1 where I need to be able to enable OData fitler support to an endpoint.
However, when debugging the controller through Postman, the ODataQueryOptions<Job> searchOptions on the controller is always null.
My query string example is;
https://localhost:44310/api/job/find/odata?Filter=[["Title"%2C"contains"%2C"T"]]&Skip=1&Top=5
I have added System.Web.Http.OData - V5.7.0.0 reference to the controller.
With the following in my WebApiConfig Register method;
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.EnableQuerySupport();
//config.AddODataQueryFilter();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
To note I have tried both EnableQuerySupport() which intellisense is saying is obselete and AddODataQueryFilter() however neither seems to work.
In my controller I then have the following code;
[HttpGet]
[EnableQuery]
[Route("find/odata")]
public PageResult<Job> Find(ODataQueryOptions<Job> searchOptions)
{
...
return new PageResult<Job>(response.Result, null, null);
}
I have tried with and without the [EnableQuery] filter with no change.
for reference the link I have been using is;
Microsoft OData tutorial
Any resolution on this? I'm seeing the same behavior. If I globally enable filter via config.Filter(), it works, but I'd rather enable it on a class by class or property by property basic, which doesn't appear to work.

API URL requires "/api/api/" after being published

I've got an API app up and running on my localhost. It works great at en endpoint such as:
http://localhost:26307/api/ModelName/12345
However, after deploying to the Test server, the same url needs to be:
http://v10test.mydomain.com/api/api/ModelName/12345
I'm trying to get rid of the double /api/.
I adjusted my routes FROM THIS (for example):
config.Routes.MapHttpRoute(
name: "route2",
routeTemplate: "api/{controller}/{organizationSys}"
);
TO THIS.....
config.Routes.MapHttpRoute(
name: "route2",
routeTemplate: "{controller}/{organizationSys}"
);
NOTE: I REMOVED "api/" FROM THE ROUTES.
But that seemed to have no effect. In IIS, the api's are set up as an Application within the domain:
I'm probbaly overlooking something obvious, but I'm just not sure what. How do I get rid of that double /api/?
There are several ways to specify routes to a controllers actions, and the order of precedence matters.
Firstly, it can be done from a general configuration, e.g. as you've done it...
config.Routes.MapHttpRoute(
name: "route2",
routeTemplate: "{controller}/{organizationSys}"
);
However this can be overridden by specifying a Route attribute on the controller or by specifying the route in the verb attribute. For example in the code below...
[Route("api/[controller]/[action]")]
public class TestController : Controller
{
[HttpGet]
[Route("/api")] // url... /api
[Route("/api/test")] // url... /api/test
[Route("testalso")] // url... /api/test/get/testalso
public string Get()
{
return "Alive";
}
[HttpGet("/api/echo/{id}")] // url... /api/echo/{id}
public string Echo(string id)
{
return $"Get Echo: {id}";
}
[HttpPost("{id}")] // url... /api/test/postit/{id}
public string PostIt(string id)
{
return $"Thanks for {id}";
}
}
The declaration on the controller specifies a default for all methods within the controller and any methods that specify attributes can either override the controller by starting the route with a '/' or append to the controller's route. See the comments next to each attribute above for examples.
Beyond that the routes will be relative to the base application hosted within iis in your case which starts at...
http://v10test.mydomain.com/api/
Hope that's enough information for you.
It is because of the way it is deployed in IIS. For IIS your application is at address
http://v10test.mydomain.com/api/
And after that comes all the controllers and etc. If you deploy it to (for example)
http://v10test.mydomain.com/myapp/
the calls will work like
http://v10test.mydomain.com/myapp/api/ModelName/12345
EDIT
Also - check the routes of your controllers or methods inside the controllers. This is the other place where the route may be modified/extended.

ASP.NET WebAPI returning Http 405

I have a WebApi controller
[RoutePrefix("api/invitations")]
public class InvitationsApiController : ApiController
And an action method:
[Route]
[HttpPost]
public IHttpActionResult Create([FromBody] CreateCommand command)
When i try to POST to http://host/api/invitations i get a "“405 Method Not Allowed”
But when I use another route like:
[Route("test")]
[HttpPost]
public IHttpActionResult Create([FromBody] CreateCommand command)
WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
SetupFormatters(config);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
And make a POST to http://host/api/invitations/test its working. I have controllers that works just perfect when requesting to "root".
More details on Http error:
HTTP Error 405.0 - Method Not Allowed The page you are looking for
cannot be displayed because an invalid method (HTTP verb) is being
used.
Detailed Error Information:
Module DefaultDocumentModule
Notification ExecuteRequestHandler
Handler StaticFile Error
Code 0x80070001
Requested URL http://localhost:8008/api/invitations
Physical Path <removed>
Logon Method Anonymous
Logon User Anonymous
Any suggestions what might be wrong?
The problem is that you're having a physical directory that is matching your route (/api/invitations). Because such physical directory exists, IIS native DirectoryListingModule and DefaultDocumentModule modules (that are not allowing the POST verb) are taking over the request before the TransferRequestHandler (that is used by MVC and WebAPI) is able to handle it.
There is no reason to put your InvitationsController.cs in the same directory stucture that is going to match the requested route. That's why we have routing in the first place, to be able to register routes dynamically regardless of the physical location of the controller.
The common convention (that is also the default WebAPI template structure) is to put your controllers in the ~/Controllers directory (or ~/Controllers/Api). But you can actually put them in any directory as long as they're getting compiled as part of the project.
Change your controller RoutePrefix declaration to Route at the controller level:
[Route("api/invitations")]
public class InvitationsApiController : ApiController
RoutePrefix doesn't add the route to the route table. This means, that if you want to use RoutePrefix, you'll have to prefix each action by itself, for example:
[RoutePrefix("api/invitations")]
public class InvitationsApiController : ApiController
[Route("")]
[HttpPost]
public IHttpActionResult Create([FromBody] CreateCommand command)

web api shows 403.14 error when localhost:port number is in browser address in iis express

This has to be something really dumb but I can't think what else to do.
Using Visual Studio 2013 - Update 1, I created an empty web api 2 project in an existing solution, added the cross origin support (cors) package and created a basic web api controller.
The WebApiConfig class seems to be fine:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var cors = new EnableCorsAttribute("*","*","*");
config.EnableCors(cors);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
And also the Global.asax
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
I then run the application, IIS express starts normally and the browser starts with the application's url but nothing seems to work.
If the URL is "localhost:port number" I get HTTP Error 403.14 - Forbidden The Web server is configured to not list the contents of this directory.
If I try "localhost:port number/api" I get HTTP Error 404.0 - Not Found The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
I have looked at several blogs, tutorials, examples and I haven't seen anywhere that anything special needs to be done. Could someone please shed some light in what I might be missing?
Web Api doesn't have a default viewable page (aspx, html, etc) that can be viewed by navigating to the root (localhost:port in this case). So that is the normal behavior. In order to access your Api through the controller you need to access it using the route template specified in your MapHttpRoute() method.
So to access the GET method in your Api you would open a browser and place localhost:port/api/{controllername} into the url. {controllername} would be set to the name of your controller class without Controller added to the end.
ex: If your controller looked like this:
public class TestController : ApiController {
public HttpResponseMessage Get() {
return something;
}
public HttpResponseMessage Get(int id) {
return something with id;
}
}
Then your url for the first Get() would look like this:
localhost:port/api/test
And the url for the second Get(int id) would look like this:
localhost:port/api/test/5
If yours route config is OK for sure, you can try add this in Web.config:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

ASP.NET Web API get child list (hierarchical resources)

I have the following rest schema that I'd like to implement using the ASP.NET Web Api:
http://mydomain/api/students
http://mydomain/api/students/s123
http://mydomain/api/students/s123/classes
http://mydomain/api/students/s123/classes/c456
I've got the first two links working properly using the ApiController and the following two methods:
public class StudentsController : ApiController {
// GET api/students
public IEnumerable<Student> GetStudents() {
}
// GET api/students/5
public IEnumerable<Student> GetStudent(string id) {
}
}
In this same controller, (or do I need a different controller called ClassesController?), how would I implement the last two links? Also, what would the routing for the 'classes' part look like (if necessary)?
Here's my WebApiConfig (which I'd like to keep as dynamic, rather than hard-coding the route to the /classes if possible:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// EDIT - I'm getting 404's when trying to use this
context.Routes.MapHttpRoute(
name: "JobsApi",
routeTemplate: this.AreaName + "/Students/{id}/Classes/{classId}",
defaults: new { classId = RouteParameter.Optional }
);
EDIT
Here's my newly created ClassesController:
public class ClassesController : ApiController {
// GET api/classes
public IEnumerable<TheClass> Get(string id) {
return null;
}
}
I'm getting 404 Errors when attempting to go to this URL:
http://mydomain/api/students/s123/classes
Routing in ASP.NET can express these more complex rules but needed to be explicitly set up. For example in this case you would have to define 2 routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/students/{studentId}/{controller}/{classId}",
defaults: new { classId = RouteParameter.Optional }
);
And you would have a controller for it:
public class ClassesController
{
public TheClass Get(int studentId, int classId)
{
....
}
}
This is perhaps not ideal but the main option.
I was working on a hierarchical routing which was not possible due to an implementation issue in Web API but this issue has been fixed now so I might start working on it again.
With this nice hierarchical approach, you have more concerns that routing internally. There is a good sample application which adopts the hierarchical resource structure: PingYourPackage. Check that out.
Note: I have a blog post about this issue which explains the below concerns and gives solutions to those with a few code samples. You can
check it out for more details:
Hierarchical Resource Structure in ASP.NET Web API
Let me explain the concerns here briefly by setting up a sample scenario. This may not be the desired approach for these type of situations but lays out the concerns very well. Let's say you have the below two affiliates inside your database for a shipment company:
Affiliate1 (Id: 100)
Affiliate2 (Id: 101)
And then assume that these affiliates has some shipments attached to them:
Affiliate1 (Key: 100)
Shipment1 (Key: 100)
Shipment2 (Key: 102)
Shipment4 (Key: 104)
Affiliate2 (Key: 101)
Shipment3 (Key: 103)
Shipment5 (Key: 105)
Finally, we want to have the following resource structure:
GET api/affiliates/{key}/shipments
GET api/affiliates/{key}/shipments/{shipmentKey}
POST api/affiliates/{key}/shipments
PUT api/affiliates/{key}/shipments/{shipmentKey}
DELETE api/affiliates/{key}/shipments/{shipmentKey}
Routing Concerns
#Ali already explained it but I've a different approach here. Assume that we are sending a GET request against /api/affiliates/105/shipments/102. Notice that the affiliate key is 105 here which doesn't exist. So, we would want to terminate the request here ASAP. We can achieve this with a per-route message handler.
Authorization Concerns
If you have some type of authentication in place, you would want to make sure (in our scenario here) that the authenticated user and the requested affiliate resource is related. For example, assume that Affiliate1 is authenticated under the Affiliate role and you have the AuthorizeAttribute registered to check the "Affiliate" role authorization. In this case, you will fail miserably because this means that Affiliate1 can get to the following resource: /api/affiliates/101/shipments which belongs to Affiliate2. We can eliminate this problem with a custom AuthorizeAttribute.
Ownership Concerns
Now, the following URI should get me the correct data:
GET /api/affiliates/100/shipments/102
However, what would happen for the below URI:
GET /api/affiliates/100/shipments/103
This should get you "404 Not Found" HTTP response because affiliate whose Id is 100 doesn't own the shipment whose id is 103.

Categories