CreatedAtRoute 传递多个路由值

CreatedAtRoute passing multiple route values

我正在尝试创建一个接受 1 个或多个模型的端点 (POST),然后将这些模型批量插入到数据库中。

一切正常,除非我尝试使用已创建对象和路由的列表调用 CreatedAtRoute

当我用多个 routes/models 调用 CreatedAtRoute 时,我得到一个运行时 InvalidOperationException,没有匹配的路由。

例子

[HttpPost]
 public async Task<ActionResult<IEnumerable<QaiStateModel>>> CreateQaiStateAsync( 
        IEnumerable<QaiStateCreationModel> inputQaiStates )
 {            
     var qaiStates = _mapper.Map<IEnumerable<QaiState>>( inputQaiStates );
     await _qaiService.AddQaiStatesAsync( qaiState ).ConfigureAwait( false );

     var models = _mapper.Map<List<QaiStateModel>>( qaiState );

     var ids = models.Select( m => new { qaiStateID = m.ID } );
     return CreatedAtRoute( "GetQaiState", ids, models );
}

作为参考,我有以下 GET/{ID} 端点:

[HttpGet( "{qaiStateID}", Name = "GetQaiState" )]
public async Task<ActionResult<QaiStateModel>> GetQaiStateAsync( int qaiStateID )

问题

是否可以return同时URI访问新添加的资源以及这些资源的模型表示列表?

我也不确定这是否是处理这种情况的正确方法(关于我应该 returning)。

更改 CreatedAtRoute 如下所示:

return CreatedAtRoute("GetQaiState", new { qaiStateID = ids },models);

更改 GetQaiStateAsync 方法如下:

public async Task<ActionResult<QaiStateModel>> GetQaiStateAsync(List<int> qaiStateID )

为了填充 201 响应的位置 header,return 创建的模型并通过路由传递 ID,我必须执行以下操作:

  1. 创建一个需要 ID 数组的新 GET 处理程序
  2. 创建一个 ModelBinder 来转换从路由传递的 ID 数组
  3. POST 处理程序 CreatedAtRoute 指向此 GET 处理程序。此外,新创建的 ID's 必须连接到一个逗号分隔的列表中,该列表将传递给 CreatedAtRoute

第 1 步:

GET 处理程序现在期望通过路由传递 array 个 ID。注意指定的路线 ({ids})。在这里我指定我想要一个用括号 ().

分隔的值数组
[HttpGet( "({ids})", Name = "GetQaiStateCollection" )]
public async Task<ActionResult<IEnumerable<QaiStateModel>>> GetQaiStateCollectionAsync( 
        [FromRoute]
        [ModelBinder( BinderType = typeof( ArrayModelBinder ) )]
        IEnumerable<int>? ids )
{
    if ( ids is null )
        return BadRequest( "Invalid ID's" );

    var states = await _qaiService
        .GetQaiStatesAsync( ids ).ConfigureAwait( false );

    if ( ids.Count( ) != states.Count( ) )
        return NotFound( "Failed to find one or more states matching the provided ID's" );

    var models = _mapper.Map<IEnumerable<QaiStateModel>>( states );
    return Ok( models );
}

第 2 步:

为了将路线 http://localhost:32003/QaiStateCollection/(4,2,3) 转换为 IEnumerable<int>,必须创建自定义模型活页夹。

public class ArrayModelBinder : IModelBinder
{
    public Task BindModelAsync( ModelBindingContext bindingContext )
    {
        // This model binder only works for enumerable types.
        if ( !bindingContext.ModelMetadata.IsEnumerableType )
        {
            bindingContext.Result = ModelBindingResult.Failed( );
            return Task.CompletedTask;
        }

        // Get the input value via the value provider.
        var value = bindingContext.ValueProvider
            .GetValue( bindingContext.ModelName ).ToString( );
        
        if ( string.IsNullOrWhiteSpace( value ) )
        {
            // Returning null here will allow us to check for this
            // and return a bad request.
            bindingContext.Result = ModelBindingResult.Success( null );
            return Task.CompletedTask;
        }

        var elementType = bindingContext.ModelType.GenericTypeArguments.First( );
        var converter = TypeDescriptor.GetConverter( elementType );
        
        var values = value.Split( new [ ] { "," }, StringSplitOptions.RemoveEmptyEntries )
            .Select( v => converter.ConvertFromString( v.Trim( ) ) )
            .ToArray( );

        var typedValues = Array.CreateInstance( elementType, values.Length );
        values.CopyTo( typedValues, 0 );
        bindingContext.Model = typedValues;

        bindingContext.Result = ModelBindingResult.Success( bindingContext.Model );
        return Task.CompletedTask;
    }
}

第 3 步:

在这里,ID 被连接成逗号分隔的列表并传递给 CreatedAtRoute。新的 GET 处理程序名称也传递给 CreatedAtRoute.

[HttpPost]
public async Task<ActionResult<IEnumerable<QaiStateModel>>> CreateQaiStateCollectionAsync( 
           IEnumerable<QaiStateCreationModel> inputModels )
{            
    var qaiStates = _mapper.Map<IEnumerable<QaiState>>( inputModels );
    await _qaiService.AddQaiStateCollectionAsync( qaiStates ).ConfigureAwait( false );

    var models = _mapper.Map<IEnumerable<QaiStateModel>>( qaiStates );
    var ids = string.Join( ",", models.Select( m => m.ID ) );

    return CreatedAtRoute( "GetQaiStateCollection", new { ids }, models );
}