I am trying to get Web API versioning working with an inherited class. I am working with two very simple variations of the stock Values controller.
[ApiVersion("1.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class ValuesController : ApiController
{
// GET api/values
[Route("")]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[Route("{id:int}")]
public virtual string Get(int id)
{
return "value from 1";
}
}
[ApiVersion("2.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class Values2Controller : ValuesController
{
//Want to use the method in the base class
//public IEnumerable<string> Get()
//{
// return new string[] { "value2-1", "value2-2" };
// }
[Route("{id:int}")]
// GET api/values/5
public new string Get(int id)
{
return "value from 2";
}
}
My start up configuration is also pretty straightforward.
public static void Register(HttpConfiguration config)
{
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap = {["apiVersion"] = typeof(ApiVersionRouteConstraint)}
};
config.MapHttpAttributeRoutes(constraintResolver);
config.AddApiVersioning(o => { o.AssumeDefaultVersionWhenUnspecified = true; });
}
The non-overridden routes work exactly as I would expect
http://localhost:32623/api/v1.0/Values/12 -> "value from 1"
http://localhost:32623/api/v2.0/Values/12 -> "value from 2"
Calling v1 of the default Get Route
http://localhost:32623/api/v1.0/Values -> Value1, Value2
However trying the same route on the child controller results in an error.
http://localhost:32623/api/v2.0/Values
<Message>
The HTTP resource that matches the request URI 'http://localhost:32623/api/v2.0/Values' does not support the API version '2.0'.
</Message>
<InnerError>
<Message>
No route providing a controller name with API version '2.0' was found to match request URI 'http://localhost:32623/api/v2.0/Values'.
</Message>
</InnerError>
The error message suggests that the overridden member expects a "1.0" route and I am able to work around this with a method like this in the child class.
[Route("")]
public override IEnumerable<string> Get()
{
return base.Get();
}
But this seems less than ideal across a larger application. Is there a way to make this work the way I would like, without these "empty" overrides?
What you need to do is overwrite the DefaultDirectRoutePrivider to allow route inheritance:
public class WebApiCustomDirectRouteProvider : DefaultDirectRouteProvider {
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor) {
// inherit route attributes decorated on base class controller's actions
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
}
}
And with that done, you will then need to configure it in your web api configuration along with the custom route constraint
public static void Register(HttpConfiguration config) {
var constraintResolver = new DefaultInlineConstraintResolver() {
ConstraintMap = {["apiVersion"] = typeof(ApiVersionRouteConstraint)}
};
var directRouteProvider = new WebApiCustomDirectRouteProvider();
// Attribute routing. (with inheritance)
config.MapHttpAttributeRoutes(constraintResolver, directRouteProvider);
config.AddApiVersioning(_ => { _.AssumeDefaultVersionWhenUnspecified = true; });
}
So now the inheriting value controller will now have the base routes available in derived controllers
For demonstrative purposes
[ApiVersion("1.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class ValuesController : ApiController {
[HttpGet]
[Route("")] // GET api/v1.0/values
public virtual IHttpActionResult Get() {
return Ok(new string[] { "value1", "value2" });
}
[HttpGet]
[Route("{id:int}")] // GET api/v1.0/values/5
public virtual IHttpActionResult Get(int id) {
return Ok("value from 1");
}
}
[ApiVersion("2.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class Values2Controller : ValuesController {
//Will have inherited GET "api/v2.0/Values" route
// GET api/v2.0/values/5 (Route also inherited from base controller)
public override IHttpActionResult Get(int id) {
return Ok("value from 2");
}
}
You will notice that the route in the child is not used on the overridden action as it too will also be inherited from the base controller.
Related
Here I have three controllers which inherited from the same controller.
All three controllers in the same configurations.
When I do the following queries;
https://localhost:7069/odata/Persons
or
https://localhost:7069/odata/Employees
the method with this signature which is called
public ActionResult<IQueryable<TModel>> Get(ODataQueryOptions<TModel> oDataQueryOptions)
This is the expected behavior
Problem
When I make this request https://localhost:7069/odata/Staffs
It is this method that is called
public async Task<ActionResult<TModel>> Get(int id)
If I remove the Get(int id) method.
The Get(ODataQueryOptions oDataQueryOptions) method which is called
And everything works
Questions
Where does the problem come from ?
Setup me
Here are the two methods which is in the base controller :
[HttpGet]
[EnableQuery(MaxExpansionDepth = 10, MaxNodeCount = 1000)]
public ActionResult<IQueryable<TModel>> Get(ODataQueryOptions<TModel> oDataQueryOptions)
{
modelService.ODataQueryOptions = oDataQueryOptions;
var result = modelService.GetModelsAsync().Result;
return Ok(result.Value);
}
[HttpGet("{id}")]
public async Task<ActionResult<TModel>> Get(int id)
{
var result = await modelService.GetModelAsync(id);
return OK(result);
}
The controllers inherit: they have the same configurations:
[Route("api/[controller]")]
[ApiController]
public class EmployeesController : PersonCollectionControllerBase<EmployeeDto, EmployeesService>
{
public EmployeesController(IEmployeesService employeesService): base(employeesService)
{
}
}
[Route("api/[controller]")]
[ApiController]
public class PersonsController : PersonCollectionControllerBase<PersonDto, PersonsService>
{
public PersonsController(IPersonsService persons): base(persons)
{
}
}
[Route("api/[controller]")]
[ApiController]
public class StaffsController : PersonCollectionControllerBase<StaffDto, StaffsService>
{
public StaffsController(IStaffService modelService) : base(modelService)
{
}
}
IServiceCollection configuration:
builder.Services.AddControllers().AddOData(options =>
{
options.AddRouteComponents(routePrefix: "odata", GetEdmModel())
.Select().Count().Filter().Expand().OrderBy().SetMaxTop(maxTopValue: 100);
});
IEdm configuration :
public static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new();
builder.EntitySet<PersonDto>(name: "Person")
.EntityType.HasKey(person => person.BusinessEntityId);
builder.EntitySet<PersonDtoBase>(name: "PersonsBase")
.EntityType.HasKey(person => person.BusinessEntityId);
builder.EntitySet<EmployeeDto>(name: "Employees")
.EntityType.HasKey(employee => employee.BusinessEntityId);
builder.EntitySet<EmployeeDtoBase>(name: "EmployeesBase")
.EntityType.HasKey(employee => employee.BusinessEntityId);
builder.EntitySet<StaffDto>(name: "Staffs")
.EntityType.HasKey(staff => staff.BusinessEntityId);
return builder.GetEdmModel();
}
The cause of problems is the name id in method Get(int id) and in its attribute [HttpGet("{id}")] they must be replaced by key.
The attribute [Route(“api/[Controller]”)], the controller must be explained example [Route(“odata/staffs”)] do it for any controllers that use Odata,
Another thing if you want to use Odata and Api endpoints in Route attribute replace odata with api example [Route(“api/staffs”)]
This solution is suggested by Juliano Leal Goncalves in the site https://github.com/OData/AspNetCoreOData/issues/664
I notice that there are a bunch of similar questions out there about this topic.
I'm getting this error when calling any of the methods below.
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.
I can't however sort out what is best practice in resolving the issue.
So far I haven't set up any specific routing middleware.
// api/menus/{menuId}/menuitems
[HttpGet("{menuId}/menuitems")]
public IActionResult GetAllMenuItemsByMenuId(int menuId)
{
....
}
// api/menus/{menuId}/menuitems?userId={userId}
[HttpGet("{menuId}/menuitems")]
public IActionResult GetMenuItemsByMenuAndUser(int menuId, int userId)
{
...
}
What you're trying to do is impossible because the actions are dynamically activated. The request data (such as a query string) cannot be bound until the framework knows the action signature. It can't know the action signature until it follows the route. Therefore, you can't make routing dependent on things the framework doesn't even know yet.
Long and short, you need to differentiate the routes in some way: either some other static path or making the userId a route param. However, you don't actually need separate actions here. All action params are optional by default. Therefore, you can just have:
[HttpGet("{menuId}/menuitems")]
public IActionResult GetMenuItemsByMenu(int menuId, int userId)
And then you can branch on whether userId == 0 (the default). That should be fine here, because there will never be a user with an id of 0, but you may also consider making the param nullable and then branching on userId.HasValue instead, which is a bit more explicit.
You can also continue to keep the logic separate, if you prefer, by utilizing private methods. For example:
[HttpGet("{menuId}/menuitems")]
public IActionResult GetMenuItems(int menuId, int userId) =>
userId == 0 ? GetMenuItemsByMenuId(menuId) : GetMenuItemsByUserId(menuId, userId);
private IActionResult GetMenuItemsByMenuId(int menuId)
{
...
}
private IActionResult GetMenuItemsByUserId(int menuId, int userId)
{
...
}
Action routes need to be unique to avoid route conflicts.
If willing to change the URL consider including the userId in the route
// api/menus/{menuId}/menuitems
[HttpGet("{menuId:int}/menuitems")]
public IActionResult GetAllMenuItemsByMenuId(int menuId)
//....
}
// api/menus/{menuId}/menuitems/{userId}
[HttpGet("{menuId:int}/menuitems/{userId:int}")]
public IActionResult GetMenuItemsByMenuAndUser(int menuId, int userId) {
//...
}
##Reference Routing to controller actions in ASP.NET Core
##Reference Routing in ASP.NET Core
You have the same route in your HttpGet attribute
Change to something like this :
// api/menus/{menuId}/menuitems
[HttpGet("{menuId}/getAllMenusItems")]
public IActionResult GetAllMenuItemsByMenuId(int menuId)
{
....
}
// api/menus/{menuId}/menuitems?userId={userId}
[HttpGet("{menuId}/getMenuItemsFiltered")]
public IActionResult GetMenuItemsByMenuAndUser(int menuId, int userId)
{
...
}
This is another solution that you can use for this kind of scenario:
Solution 1 and more complex, using IActionConstrain, and ModelBinders(this gives you the flexibility to bind your input to a specific DTO):
The problem you have is that your controller has the same routing for 2 different methods receiving different parameters.
Let me illustrate it with a similar example, you can have the 2 methods like this:
Get(string entityName, long id)
Get(string entityname, string timestamp)
So far this is valid, at least C# is not giving you an error because it is an overload of parameters. But with the controller, you have a problem, when aspnet receives the extra parameter it doesn't know where to redirect your request.
You can change the routing which is one solution.
Normally I prefer to keep the same names and wrap the parameters on a DtoClass, IntDto and StringDto for example
public class IntDto
{
public int i { get; set; }
}
public class StringDto
{
public string i { get; set; }
}
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IActionResult Get(IntDto a)
{
return new JsonResult(a);
}
[HttpGet]
public IActionResult Get(StringDto i)
{
return new JsonResult(i);
}
}
but still, you have the error. In order to bind your input to the specific type on your methods, I create a ModelBinder, for this scenario, it is below(see that I am trying to parse the parameter from the query string but I am using a discriminator header which is used normally for content negotiation between the client and the server(Content negotiation):
public class MyModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
dynamic model = null;
string contentType = bindingContext.HttpContext.Request.Headers.FirstOrDefault(x => x.Key == HeaderNames.Accept).Value;
var val = bindingContext.HttpContext.Request.QueryString.Value.Trim('?').Split('=')[1];
if (contentType == "application/myContentType.json")
{
model = new StringDto{i = val};
}
else model = new IntDto{ i = int.Parse(val)};
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
Then you need to create a ModelBinderProvider (see that if I am receiving trying to bind one of these types, then I use MyModelBinder)
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata.ModelType == typeof(IntDto) || context.Metadata.ModelType == typeof(StringDto))
return new MyModelBinder();
return null;
}
and register it into the container
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new MyModelBinderProvider());
});
}
So far you didn't resolve the issue you have but we are close. In order to hit the controller actions now, you need to pass a header type on the request: application/json or application/myContentType.json. But in order to support conditional logic to determine whether or not an associated action method is valid or not to be selected for a given request, you can create your own ActionConstraint. Basically the idea here is to decorate your ActionMethod with this attribute to restrict the user to hit that action if he doesn't pass the correct media type. See below the code and how to use it
[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class RequestHeaderMatchesMediaTypeAttribute : Attribute, IActionConstraint
{
private readonly string[] _mediaTypes;
private readonly string _requestHeaderToMatch;
public RequestHeaderMatchesMediaTypeAttribute(string requestHeaderToMatch,
string[] mediaTypes)
{
_requestHeaderToMatch = requestHeaderToMatch;
_mediaTypes = mediaTypes;
}
public RequestHeaderMatchesMediaTypeAttribute(string requestHeaderToMatch,
string[] mediaTypes, int order)
{
_requestHeaderToMatch = requestHeaderToMatch;
_mediaTypes = mediaTypes;
Order = order;
}
public int Order { get; set; }
public bool Accept(ActionConstraintContext context)
{
var requestHeaders = context.RouteContext.HttpContext.Request.Headers;
if (!requestHeaders.ContainsKey(_requestHeaderToMatch))
{
return false;
}
// if one of the media types matches, return true
foreach (var mediaType in _mediaTypes)
{
var mediaTypeMatches = string.Equals(requestHeaders[_requestHeaderToMatch].ToString(),
mediaType, StringComparison.OrdinalIgnoreCase);
if (mediaTypeMatches)
{
return true;
}
}
return false;
}
}
Here is your final change:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
[RequestHeaderMatchesMediaTypeAttribute("Accept", new[] { "application/json" })]
public IActionResult Get(IntDto a)
{
return new JsonResult(a);
}
[RequestHeaderMatchesMediaTypeAttribute("Accept", new[] { "application/myContentType.json" })]
[HttpGet]
public IActionResult Get(StringDto i)
{
return new JsonResult(i);
}
}
Now the error is gone if you run your app. But how you pass the parameters?:
This one is going to hit this method:
public IActionResult Get(StringDto i)
{
return new JsonResult(i);
}
And this one the other one:
public IActionResult Get(IntDto a)
{
return new JsonResult(a);
}
Solution 2: Routes constrains
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet("{i:int}")]
public IActionResult Get(int i)
{
return new JsonResult(i);
}
[HttpGet("{i}")]
public IActionResult Get(string i)
{
return new JsonResult(i);
}
}
This is a kind of test because I am using the default routing:
https://localhost:44374/weatherforecast/"test" should go to the one that receives the string parameter
https://localhost:44374/weatherforecast/1 should go to the one that receives an int parameter
In my case [HttpPost("[action]")] was written twice.
I got this error, and just needed to restart the service to get it working again. Probably because I was modifying the code, and it re-registered the same controller method somehow.
You can have a dispatcher endpoint that will get the calls from both endpoints and will call the right based on parameters.
(It will works fine if their are in same controller).
Example:
// api/menus/{menuId}/menuitems
[HttpGet("{menuId}/menuitems")]
public IActionResult GetAllMenuItemsByMenuId(int menuId, int? userId)
{
if(userId.HasValue)
return GetMenuItemsByMenuAndUser(menuId, userId)
.... original logic
}
public IActionResult GetMenuItemsByMenuAndUser(int menuId, int userId)
{
...
}
I am using a Repository pattern with Entity Framework, When i run my code instead of Json Data I am getting -> System.Collections.Generic.List`1[MovieInfo.Core.Models.Models.User]
Repository
public interface IRepository<T> where T : class
{
IEnumerable<T> Get();
T GetDetails(Guid Id);
void Insert(T data);
void Delete(T data);
void Update(T data);
void Save();
}
public GenericRepository()
{
context = new Entities();
dbEntity = context.Set<T>();
}
Services
public class TestService : ITestService
{
public TestService(
IRepository<User> userRepository
)
{
_userRepository = userRepository;
}
private readonly IRepository<User> _userRepository;
public IEnumerable<User> Get()
{
var result = _userRepository.Get();
return result;
}
}
Controller
public class HomeController : Controller
{
public HomeController(ITestService testService)
{
_testService = testService;
}
private readonly ITestService _testService;
public IEnumerable<User> Index()
{
var result = _testService.Get();
return result;
}
}
I found some solution on here (StackOverflow) that I need to add WebApiConfig, I added as below but it also not work. I am getting the output
System.Collections.Generic.List`1[MovieInfo.Core.Models.Models.User]
public static class WebAppConfig
{
public static void Register(HttpConfiguration config)
{
config.Formatters.Remove(new XmlMediaTypeFormatter());
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
config.Formatters.XmlFormatter.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data"));
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);
}
}
Here is my Git Repository Link
https://github.com/Harshk16/MovieInfo
Please help me out how to get output in Json Format
If you want to return JSON response form your MVC controller action method, you may use the Json method.
The Json method is defined inside System.Web.Mvc.Controller, from which you are inherting your HomeController. So you have access to this method.
The Json method returns JsonResult typw. So make sure your method's return type is either JsonResult or ActionResult. (The JsonResult class inhertis from ActionResult)
public JsonResult Index()
{
var result = _testService.Get();
return Json(result,JsonRequestBehavior.AllowGet);
}
Or
public ActionResult Index()
{
var result = _testService.Get();
return Json(result,JsonRequestBehavior.AllowGet);
}
Another option is to use a web api controller instead of MVC controller. Register your web api routes before your MVC action method routing in your global.asax.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
GlobalConfiguration.Configure(WebAppConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// Your other existing code
}
and create a webapi controller which inherits from System.Web.Http.ApiController. Here I removed the Dependency Injection part for simplicity. You can wire up DI in your web api controllers by following the AutoFac web api integration tutorial
public class UsersController : ApiController
{
public IEnumerable<User> Get()
{
var result = new List<User>
{
new User { Id = Guid.NewGuid() , DisplayName = "Shyju"},
new User { Id = Guid.NewGuid(), DisplayName = "Scott"}
};
// Hard coded list of users.
// Once you fix your DI setup, you can use your service to get data.
return result;
}
}
By default, web api uses content negotioation. It reads the "Accept" request header value and based on that execute the corresponding formatter. With your current web api routing template, you can access this endpoint with the below request URL
http://yourSiteBaseUrl/api/users/get
If you do not want the get in your API Urls, simply fix your route template when registering the web api routing
routeTemplate: "api/{controller}/{id}"
To use the web api, which you define in your WebApiConfig, your controllers need to inherit from the Web Api base controller ApiController instead of the MVC base controller controller.
When inheriting from the ApiController, the default Formatter will take care of turning your controller methods output to your desire result. In your case, as you removed the Xml-formatter, the default will be Json.
public class HomeController : ApiController
{
public HomeController(ITestService testService)
{
_testService = testService;
}
private readonly ITestService _testService;
public IEnumerable<User> Index()
{
var result = _testService.Get();
return result;
}
}
Typically you call an action of a controller like so http://hostname/MyController/MyAction
I have a requirement for my Web Api to have routes like this one:
http://hostname/MyController?action=MyAction, i.e., pass the action in url parameter.
My controller:
public class MyController : ApiController
{
[HttpGet]
[Route("WHAT SHOULD BE HERE??")]
public IHttpActionResult MyAction()
{
// some code
}
}
Any ideas how I can write such a routing?
You could try the following:
public class MyController : ApiController
{
[HttpGet]
[Route("MyController")]
public IHttpActionResult MyInnerController(String action)
{
switch(action)
{
case "MyAction":
return MyAction();
}
return BadRequest("Invalid action: " + action);
}
public IHttpActionResult MyAction()
{
return Ok();
}
}
Things will get more complicated if you require additional parameters.
After more than a year I can come back to this question and answer it myself.
The solution you can use here is to write your own ActionSelector - this is the class Web Api framework uses to select actions, by default it uses System.Web.Http.Controllers.ApiControllerActionSelector, which you can override.
So lets say your controller looks like this:
public class MyController : ApiController
{
[HttpGet]
public IHttpActionResult MyAction()
{
// some code
}
}
Then you can create your own action selector like this (the code might be improved I wrote it very quickly):
public class QueryParameterActionSelector : ApiControllerActionSelector
{
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
var mapping = GetActionMapping(controllerContext.ControllerDescriptor);
var parameters = controllerContext.Request.GetQueryNameValuePairs();
foreach (var parameter in parameters)
{
if (parameter.Key == "action")
{
if (mapping.Contains(parameter.Value))
{
// Provided that all of your actions have unique names.
// Otherwise mapping[parameter.Value] will return multiple actions and you will have to match by the method parameters.
return mapping[parameter.Value].First();
}
}
}
return null;
}
}
And then finally you have to register this action selector in WebApiConfig.Register method. It will look like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}"
);
config.Services.Replace(typeof(IHttpActionSelector), new QueryParameterActionSelector());
}
}
Now you can call your action like this http://hostname/controller?action=MyAction
I have a base controller:
public abstract class EntityController<T> : ApiController
{
[HttpPost]
[ValidateModel]
public abstract IHttpActionResult Create(T dto);
[HttpPut]
[ValidateModel]
public abstract IHttpActionResult Update(T dto);
[HttpDelete]
public abstract IHttpActionResult Delete(int id);
}
And a derived controller:
public class CustomerTypeController : EntityController<CustomerTypeDTO>
{
[ApiAuthorize(Right = Rights.CustomerType, Action = Action.Create)]
public override IHttpActionResult Create(CustomerTypeDTO customerType)
{
return Save(customerType);
}
[ApiAuthorize(Right = Rights.CustomerType, Action = Action.Update)]
public override IHttpActionResult Update(CustomerTypeDTO customerType)
{
return Save(customerType);
}
private IHttpActionResult Save(CustomerTypeDTO customerType)
{
//service layer save customer type
}
}
The base controller has a [ValidateModel] on the actions, whilst the derived controller sets it's own [ApiAuthorize] attribute. The attributes are fired in the right order (first ApiAuthorize and then ValidateModel). I have not set the order and I don't know if it's possible.
The ApiAuthorize looks like this:
[AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
public class ApiAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
//Do some checks...
if (!authorized)
actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, new CustomNotAuthorizedException());
}
}
However the problem is that even although the attribute creates an error response, the action still proceeds further by going into ValidateModel and then into the action itself.
How is it possible to stop the api from processing the request further at the Authorize and ValidateModel levels?
The problem was that the Authorize attribute code was not setting the response. It has to be like this:
if (!authorized)
{
var response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, new CustomNotAuthorizedException());
actionContent.Response = response;
}