Receiving 403 in ASP.NET WebAPI application when including an extra segment in attribute route - asp.net-web-api

In my ASP.NET WebAPI controller, the following routing setup works correctly:
[Route("api/products")]
public class ProductsController : ApiController
{
[HttpGet]
public IHttpActionResult Get()
{
return Ok();
}
}
However, when I change the route to api/catalog/products I start getting 403 errors when accessing the route. Why would that extra segment cause a 403 error and how can I fix it?
Here is my config:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
}
}

First I would suggest you update the attribute routes to follow the suggested format
[RoutePrefix("api/products")]
public class ProductsController : ApiController {
//GET api/products
[HttpGet]
[Route("")] //(Default route)
public IHttpActionResult Get() {
return Ok();
}
}
that uses the [RoutePrefix] attribute on the ApiController and adding the [Route] attribute on the action.
Next, 403 Forbidden typically occurs when you try to browse to a directory on a site where the Web site does not have the Directory Browsing feature enabled, and the default document is not configured.
In this case you may have an actual folder which is conflicting with the default route of the ProductsController.
when you update the route and try to call api/catalog/products it will try to return the actual content of that folder which will fail if the feature is not enabled.
Either remove or rename the folder to something that does not conflict with any of your controller routes.
Reference Attribute Routing in ASP.NET Web API 2

Related

how to set up url routing with sub-paths

i am new to webapi and MVC in general. If I wanted to group my service URLs like this
/api/account/create
/api/account/login
/api/account/resetpass
Am I able to put all 3 method calls in the same controller file and somehow map a particular request to the right method?
Create a Controller named Account and Create 3 [GET, POST, PUT, DELETE] method and name them create , login ,resetpass.
By Default, this is the routing for MVC / API(Id can be optional)
route Template: "api/{controller}/{id}",
Example :
public class AccountController : ApiController
{
[HttpPost]
public string Create()
{
// CODE
}
[HttpPost] // or [HttpGet]
public string Login ()
{
// CODE
}
[HttpPost]
public string Resetpass()
{
// CODE
}
}
if you had trouble calling them, try to give them a specific route :
[HttpGet("GetSubject/{subject}")]
public int GetSubjectId(String subject)
{
//CODE
}
Please if you get any error or misunderstanding, don't hesitate to post a comment

Ajax not working after deploying to Azure

I am developing a web application using ASP.NET MVC Core. Everything works perfect on my local machine but whenever I deploy to Azure the Ajax calls always get a 404 Not Found.
Here's a snippet of one controller method:
[HttpGet]
public JsonResult GetPublicHolidays()
{
var events = adminService.GetPublicHolidays();
return new JsonResult(events);
}
And here's the Ajax call:
$.getJSON('#Url.Action("GetPublicHolidays","Admin")',
By default, the URL to an action in an ASP.NET Controller is not the name of the method. There's a lot going on by convention in ASP.NET.
As an example, this is a default ASP.NET Core API controller:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
}
As you can see in the comment, the route is <baseUrl>/api/values. This route is comprised of the base URL, the api prefix and the name of the controller. Because you add a HttpGetAttribute, ASP.NET knows that is the Get method.
So, considering this controller:
public class RandomController : ControllerBase
{
[HttpGet]
public ActionResult<IEnumerable<string>> WhateverWeirdMethodName()
{
return new string[] { "value1", "value2" };
}
}
The URL for the GET request would be <baseUrl>/api/random

How do I add API Endpoints in ASP.NET?

I would like to register API Endpoints in ASP.NET by just adding few methods in ApiController. A new method there means a new API.
In a random example below:
public class ProductController : ApiController
needs to serve the following Endpoints:
/
/price
/price/discount
Problem here is all endpoints are having a GET request to /, and result in same output as /.
Reference URL and Service Contract
You can place Route annotation at method for which you want to use custom route.
public class CustomersController : ApiController
{
// this will be called on GET /Customers or api/Customers can't remember what default
//config is
public List<Customer> GetCustomers()
{
...
}
// this will be called on GET /My/Route/Customers
[HttpGet, Route("My/Route/Customers)]
public List<Customer> GetCustomersFromMyRoute()
{
...
}
}

Attribute routing and inheritance

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)

ASP.Net MVC 3 IF Statement outside Controller Action

I am developing an ASP.Net MVC 3 Web Application. I need to have my website secured with an SSL certificate, however, I only want this used when the application is on my live server, NOT on my test server.
Therefore, I setup an AppSetting in my Web Config like so
<appSettings>
<add key="SSL" value="false" />
</appSettings>
Then in my Account Controller I get this value (either True or False) and using the value, decide whether or not to set the RequiresHttps attribute on my LogOn Action. I would like to do something like so
public class AccountController : Controller
{
public string SSL = System.Configuration.ConfigurationManager.AppSettings["SSL"];
if (SSL.Equals("true"))
{
[RequireHttps]
}
public ActionResult LogOn()
{
return View();
}
}
But I know I can't put my IF statement where it currently is, however, hopefully you get the idea of what I am trying to achieve.
Does anyone have any suggestions as to how I can implement my idea?
Thanks.
Subclass the RequireHttpAttribute (note this code is changed from my original answer - this new version will be more efficient):
public class RequireHttpsIfEnabledAttribute : RequireHttpsAttribute
{
//this setting can't be changed without a recycle, so get it once and cache it.
private static readonly Lazy<bool> HttpsRequired = new Lazy<bool>(() => {
//if the AppSettings["SSL"] returns null you raise an exception if you do a
//.Equals on it - so do it on the constant instead. And make sure it's case
//insensitive!
return "true".Equals(System.Configuration.ConfigurationManager.AppSettings["SSL"],
StringComparison.OrdinalIgnoreCase);
});
public override void OnAuthorization(AuthorizationContext filterContext)
{
//calling the base will fire the HTTPS check. Not calling it will allow
//non-SSL requests through
if (HttpsRequired.Value)
base.OnAuthorization(filterContext);
}
}
Now you just decorate your controllers/actions as before - but with your new attribute:
[RequireHttpsIfEnabled]
public class AccountController : Controller
{
//....
}

Resources