Rest 表示深层资源树的最佳方法?
是否有一种简单的方法可以将路线“段”与控制器逻辑一起重用,以验证和使用段中的参数 例如,我想用类似的方式表示我的资源:Rest 表示深层资源树的最佳方法?,rest,asp.net-core,asp.net-core-webapi,Rest,Asp.net Core,Asp.net Core Webapi,是否有一种简单的方法可以将路线“段”与控制器逻辑一起重用,以验证和使用段中的参数 例如,我想用类似的方式表示我的资源: 房屋 {住址} 建筑 屋顶 侧线 房间 活着的 门 窗户 进餐 门 窗户 厨房 门 窗户 卧室 [0] 门 窗户 [1] 门 窗户 下面是一种可以做到的方法更好的方法是什么? [Route("[controller]")] [ApiController] class HousesController : ControllerBase
- 房屋
- {住址}
- 建筑
- 屋顶
- 侧线
- 房间
- 活着的
- 门
- 窗户
- 进餐
- 门
- 窗户
- 厨房
- 门
- 窗户
- 卧室
- [0]
- 门
- 窗户
- [1]
- 门
- 窗户
- [0]
- 活着的
- 建筑
- {住址}
[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;
}
}
这将是非常容易建立的基础。例如,我可以快速添加门和窗的控制器:
[HttpGet("[ROOM|BEDROOM]/doors")]
public Door[] GetDoors(Room room) => room.Doors;
[HttpGet("[ROOM|BEDROOM]/windows")]
public Window[] GetWindows(Room room) => room.Windows;
正如您所指出的,这是不可能的,但它仍然是可以实现的。首先,您希望依赖应用于控制器类本身的路由前缀。换句话说,不是:
[HttpGet("~houses/{houseId}/rooms/bedrooms/{bedroomId}")]
public ActionResult<Room> GetBedroom(string houseId, int bedroomId)
这看起来有点复杂,但所做的只是从路由中拉出
houseId
参数,并试图找到相关的房屋。如果没有找到,将立即返回404,否则,控制器上的House
属性将与该House一起设置。因此,您在该控制器中的每个操作现在都可以依赖于可用的房屋
属性,并可以相应地使用它,只需关注自己的特定行为。。细节(比如参数名以及如何将参数转换成对象)可以被封装和抽象——我喜欢!基于使用FindAsync
,传递什么作为houseId
应该无关紧要。例如,如果有人尝试了/houses/foo/rooms/beddrooms/1
,结果应该是404。但是,您可能仍然希望将路由约束添加到参数:house/{houseId:int}/rooms
和beddrooms/{beddroomid:int}
。
[HttpGet("~houses/{houseId}/rooms/bedrooms/{bedroomId}")]
public ActionResult<Room> GetBedroom(string houseId, int bedroomId)
[Route("houses/{houseId}/rooms")]
public class RoomsController : ControllerBase
{
[HttpGet("bedrooms/{bedroomId}")]
public ActionResult<Room> GetBedroom(int bedroomId)
private House House { get; set; }
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (context.RouteData.Values.TryGetValue("houseId", out var houseId))
{
House = await _context.Set<House>().FindAsync(houseId);
if (House == null)
context.Result = NotFound();
}
await base.OnActionExecutionAsync(context, next);
}