Существует ли простой способ повторно использовать «сегмент» маршрута вместе с логикой контроллера, которая используется для проверки и использования параметров в сегменте?
Например, я хотел бы представить мои ресурсы, аналогичныеэто:
- дома
- {адрес дома}
- архитектура
- комнаты
- жилые
- столовая
- кухня
- спальни
Ниже приведен один из возможных способов. Какой путь лучше?
[Route("[controller]")]
[ApiController]
class HousesController : ControllerBase
{
[HttpGet]
public IEnumerable<House> Get() => _housesRepo.GetAll();
[HttpGet("{houseId}")]
public ActionResult<House> Get(string houseId)
{
if (!_housesRepo.TryGetHouse(houseId, out var house))
return NotFound("Bad house ID");
}
}
class RoomsController : ControllerBase
{
[HttpGet("~houses/{houseId}/rooms/bedrooms/{bedroomId}")]
public ActionResult<Room> GetBedroom(string houseId, int bedroomId)
{
if (!_housesRepo.TryGetHouse(houseId, out var house))
return NotFound("Bad house ID");
var bedrooms = house.Bedrooms;
if (bedroomId < 0 || bedroomId >= bedrooms.Length)
return NotFound("Bad bedroom ID");
return bedrooms[bedroomId];
}
[HttpGet("~houses/{houseId}/rooms/{roomClass}")]
public ActionResult<Room> GetRoom(string houseId, string roomClass)
{
if (!_housesRepo.TryGetHouse(houseId, out var house))
return NotFound("Bad house ID");
var roomsByClass = house.RoomsByClass;
if (!roomsByClass.TryGetValue(roomClass, out var room))
return NotFound("Bad room class");
return room;
}
}
class ArchitectureController : ControllerBase
{
[HttpGet("~houses/{houseId}/architecture")]
public ActionResult<string[]> Get(string houseId)
{
if (!_housesRepo.TryGetHouse(houseId, out _))
return NotFound("Bad house ID");
return new [] { "roof", "siding" };
}
[HttpGet("~houses/{houseId}/architecture/roof")]
public ActionResult<Roof> GetRoof(string houseId)
{
if (!_housesRepo.TryGetHouse(houseId, out var house))
return NotFound("Bad house ID");
return house.Architecture.Roof;
}
[HttpGet("~houses/{houseId}/architecture/siding")]
public ActionResult<Siding> GetSiding(string houseId)
{
if (!_housesRepo.TryGetHouse(houseId, out var house))
return NotFound("Bad house ID");
return house.Architecture.Siding;
}
}
Но обратите внимание, сколько повторений:
~houses/{houseId}
сегмент маршрута - Проверка
houseId
и извлечение связанных House
И повторение становится только хуже, когда ресурсы становятся более сложными.
Кажется, что Microsoft подталкивает людей к очень плоской иерархии.Например:
~houses/{houseId}
~architecture/{architectureId}
~rooms/{roomId}
Но это только скрывает проблему.Например, Room
имеет смысл только как компонент House
, поэтому {roomId}
также должен включать информацию об идентификаторе дома.Затем мне нужно было бы проанализировать roomId
для извлечения houseId
в дополнение к проверке и извлечению связанного ресурса House
.
Было бы хорошо, если бы я мог просто получить это:
[Segment("HOUSE")]
[HttpGet("houses/{houseId}")]
public ActionResult<House> GetHouse(string houseId) => _housesRepo
.TryGetValue(houseId, out var house)
? new ActionResult<House>(house)
: NotFound("Bad house ID");
[HttpGet("[HOUSE]/architecture")]
public string[] GetArchitectureCategories(House house) => new [] { "roof", "siding" };
[HttpGet("[HOUSE]/architecture/roof")]
public Roof GetRoof(House house) => house.Architecture.Roof;
[HttpGet("[HOUSE]/architecture/siding")]
public Siding GetSiding(House house) => house.Architecture.Siding;
[Segment("BEDROOM")]
[HttpGet("[HOUSE]/rooms/bedrooms/{bedroomId}")]
public Room GetBedroom(House house, int bedroomId) => house.Bedrooms[bedroomId];
[Segment("ROOM")]
[HttpGet("[HOUSE]/rooms/{roomClass}")]
public ActionResult<Room> GetRoom(House house, string roomClass) => house
.RoomsByClass
.TryGetValue(roomClass, out var room)
? new ActionResult<Room>(room)
: NotFound("Bad room class");
Это было бы очень легко построить.Например, я мог бы быстро добавить контроллеры для дверей и окон:
[HttpGet("[ROOM|BEDROOM]/doors")]
public Door[] GetDoors(Room room) => room.Doors;
[HttpGet("[ROOM|BEDROOM]/windows")]
public Window[] GetWindows(Room room) => room.Windows;