OData v4 Routing Prefix? - c#

I have a side-by-side Web API 2.2 APIController and OData v4 ODataController. My APIController uses routing attributes internally like this (there are no predefined routing defaults):
[RoutePrefix("api")]
public class MyController : ApiController
{
[HttpGet]
[Route("My")]
public IHttpActionResult Get()
{
//Code Here
}
[HttpGet]
[Route("My")]
public IHttpActionResult Get([FromUri] String mykey)
{
//Code Here
}
}
and as such are routed to through ./api/My and ./api/My/?mykey=value
and I've tried setting up my ODataController to follow a similar suit with:
[ODataRoutePrefix("My")]
public class oMyController : ODataController {
[HttpGet]
public IHttpActionResult Get(ODataQueryOptions<FileModel> queryOptions) {
//Code Here
}
[HttpGet]
[ODataRoute("({mykey})")]
public IHttpActionResult Get([FromODataUri] String mykey) {
//Code Here
}
}
defining odata route ahead of time like this:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MyModel>("My");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "odata",
model: builder.GetEdmModel()
);
but attempts to access ./odata/My and ./odata/My(value) end up going into my APIController instead of the ODataController.
How can I route these using the different prefixes, but the same name, and have them go to the appropriate controllers. I don't want to have a different name for each route if I can prevent it, the prefixes should take care of everything, but for some reason they're not.

You need to specify the ODataRoute attribute, even if it's empty:
[ODataRoutePrefix("My")]
public class oMyController : ODataController {
[HttpGet]
[ODataRoute()] // <---<< This was the key to proper OData routing
public IHttpActionResult Get(ODataQueryOptions<FileModel> queryOptions) {
//Code Here
}
[HttpGet]
[ODataRoute("({mykey})")]
public IHttpActionResult Get([FromODataUri] String mykey) {
//Code Here
}
}

Related

Controller route attribute only relevant for root function?

I have a question about .NET Core controller routing. Recently I discovered that the controller route attribute (which you place just above the controller) only works for the root method, or at least it seems that way.
My code:
using KrabbelMicroservice.Models;
using KrabbelMicroservice.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
namespace KrabbelMicroservice.Controllers;
[ApiController]
[Route("/profile")] // <-- This is the controller routing attribute I am talking about
public class ProfileKrabbelController : Controller
{
private readonly IProfileKrabbelService _krabbelService;
public ProfileKrabbelController(IProfileKrabbelService krabbelService)
{
_krabbelService = krabbelService;
}
[HttpGet]
public IActionResult Index()
{
// not relevant
}
[HttpGet]
[Route("/id/{krabbelId}")]
public IActionResult GetKrabbelById(long krabbelId)
{
// not relevant
}
[HttpGet]
[Route("/pid/to/{profileId}")]
public IActionResult GetKrabbelsToProfileId(long profileId)
{
// not relevant
}
[HttpGet]
[Route("/pid/from/{profileId}")]
public IActionResult GetKrabbelsFromProfileId(long profileId)
{
// not relevant
}
[HttpGet]
[Route("/pid/with/{profileId}")]
public IActionResult GetKrabbelsWithProfileId(long profileId)
{
// not relevant
}
[HttpPost]
[Route("/new")]
public IActionResult AddKrabbel(ProfileKrabbel krabbel)
{
// not relevant
}
[HttpPut]
[Route("/update")]
public IActionResult UpdateKrabbel(ProfileKrabbel krabbel)
{
// not relevant
}
[HttpDelete]
[Route("/delete")]
public IActionResult DeleteKrabbel(ProfileKrabbel krabbel)
{
// not relevant
}
}
In my swagger launch the requests look like this:
I expected that all paths would be prefixed by /profile/ but it seems like only the root function (which did not have its own route attribute) implemented the prefix.
I am not only trying to get a fix for this, but also looking for an explanation as to why my controller route attribute is ignored for the other requests. The only possibility I can think of is the specific route attributes for each endpoint overriding the controller route attribute but I would like to hear it from an expert.
Secondly I would of course also like to find a solution to this problem, preferrably not adding /profile before every seperate route but if that is the only solution so be it.
Thanks in advance!
you should be remove "/" if you have root route
ex:
[Route("test")]
[ApiController]
public class TestController3 : Controller
{
[HttpGet]
[Route("testobj")]
public TestObj Test()
{
return "test";
}
}
the even shorter in httpget
[HttpGet("testobj")]
the both output:
test/testobj

How to bind WebAPI controller action parameter either by URL part or querystring

I have an API method defined as such:
[Route("api/[controller]")]
[ApiController]
public class ItemsController : ControllerBase
{
[HttpGet]
[HttpGet("{itemType}/{itemId}")]
public IActionResult Get(string itemType, string itemId)
{
return Ok($"{itemType}/{itemId}");
}
}
Calling this works:
/api/items/person/0123
Calling the action by querystring results in UnprocessableEntity
/api/items?itemType=person&itemId=0123
How can I define my action method so that it accepts both types of input?
you can try like this, so that the first route will allow you to use querystring and the second one will allow values to be bound to the variables through attribute routing.
[Route("api/[controller]")]
[ApiController]
public class ItemsController : ControllerBase
{
[HttpGet("")]
[HttpGet("{itemType}/{itemId}")]
public IActionResult Get(string itemType, string itemId)
{
return Ok($"{itemType}/{itemId}");
}
}

cannot find C# netcore controller

I have added a netcore controller in my existing IdentityServer4 project. Here is my code
namespace IdentityServer4.Quickstart.UI
{
public class VersionController : Controller
{
IVersionService _repository;
public VersionController(IVersionService repository)
{
_repository = repository;
}
[HttpGet(nameof(GetBackgroundId))]
public IActionResult GetBackgroundId()
{
return new OkObjectResult(_repository.GetBackgroundId());
}
[HttpPut(nameof(SetBackgroundId))]
public IActionResult SetBackgroundId([FromQuery]int id)
{
_repository.SetBackgroundId(id);
return new NoContentResult();
}
}
}
I also have the following line of code in startup.cs
app.UseMvcWithDefaultRoute();
I can access the account controller by the following url
http://localhost:5001/account/login
However, I cannot access the version controller by the following url:
http://localhost:5001/version/GetBackgroundId
The error code is 404.
What is wrong?
You are missing a route prefix for controller. You are using attribute routing so you need to include the entire desired route.
The current GetBackgroundId controller action would map to
http://localhost:5001/GetBackgroundId
Add a route to the controller
[Route("[controller]")]
public class VersionController : Controller {
IVersionService _repository;
public VersionController(IVersionService repository) {
_repository = repository;
}
//Match GET version/GetBackgroundId
[HttpGet("[action]")]
public IActionResult GetBackgroundId() {
return Ok(_repository.GetBackgroundId());
}
//Match PUT version/SetBackgroundId?id=5
[HttpPut("[action]")]
public IActionResult SetBackgroundId([FromQuery]int id) {
_repository.SetBackgroundId(id);
return NoContent();
}
}
Also note the use of route tokens and that instead of newing up the responses, Controller already has helper methods that provide those results.
Reference Routing to controller actions in ASP.NET Core

Asp.net core 2 Prefix Routing

How to create prefixed routing for MVC CRUD operation. I am working on an application that requires admin and front-end. For the admin I want all route to point to localhost:5000/admin/....
I have different Controllers
public class RoomsController : Controller
{
// GET: Rooms
public async Task<IActionResult> Index()
{
return View(await _context.Rooms.ToListAsync());
}
//...
}
and
public class SlidersController : Controller
{
private readonly ApplicationDbContext _context;
public SlidersController(ApplicationDbContext context)
{
_context = context;
}
// GET: Sliders
public async Task<IActionResult> Index()
{
return View(await _context.Sliders.ToListAsync());
}
//...
}
Now I want the admin route to be
localhost:5000/admin/rooms
localhost:5000/admin/slider
while other routes remain
localhost:5000/
localhost:5000/about
localhost:5000/...
You can also use Attribute Routing for this. Till ASP.Net Web API we have the attribute named [RoutePrefix], but in ASP.Net Core 2 we can use [Route] attribute for the same purpose.
[Route("api/[controller]/[action]")]
public class DistrictController : ControllerBase
{
[Route("{id:int:min(1)}")] // i.e. GET /api/District/GetDetails/10
public IActionResult GetDetails(int id)
{
}
// i.e. GET /api/District/GetPage/?id=10
public IActionResult GetPage(int page)
{
}
[HttpDelete]
[Route("{id:int:min(1)}")] // i.e. Delete /api/District/Delete/10
public IActionResult Delete(int id)
{
}
[HttpGet]
[Route("~/api/States/GetAllState")] // i.e. GET /api/States/GetAllState
public IActionResult GetStates()
{
}
}
I solve the Problem by using MVC Area
docs

Route Prefix VS Controller Name ( Web api )

I was wondering that if we use RoutePrefix attribute in our web api controller with a different name from controller's actual name. So would it work or not?
As far as i did
[RouterPrefix("quotation")]
public class SaleOrderController : ApiController { ... }
if we define RoutePrefix like above we can't access it via /quotation but we can access it using saleorder.
So what is RoutePrefix for or am i doing something wrong ?
To use default route use Route("")
[RoutePrefix("quotation")]
public class SaleOrderController : ApiController {
//GET quotation
[Route("")]
[HttpGet]
public IHttpActionResult GetAll() { ... }
}
Source: Attribute Routing in ASP.NET Web API 2 : Route Prefix
In order for it to work, you need to call the code below inside your WebApiConfig.Register() method:
config.MapHttpAttributeRoutes();
So your RoutePrefix works as exptected:
[RoutePrefix("quotation")]
public class SaleOrderController : ApiController
{
[Route("example")]
[HttpGet]
public IHttpActionResult Example()
{
return Ok();
}
[Route("another")]
[HttpGet]
public IHttpActionResult Another()
{
return Ok();
}
}
So your could access your apis like this:
quotation/example
quotation/another

Categories

Resources