2

パラメーターのない Get と ID を受け取る Get を持つ単純なコントローラーがあります。

public class BooksController : ApiController
{
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    public string Get(Identity id)
    {
        return "value";
    }
}

Identity は、単純化すると次のようなカスタム構造体です。

public struct Identity
{
    // ....
    public Identity(Guid value)
    {
        internalValue = value;
    }
}

ただし、いずれかのエンドポイントに移動しようとすると、リクエストに一致する複数のアクションが見つかったことを示すエラーが表示されます。BooksController は、単一のリソースの Get が Identity 構造体ではなく Guid を受け取る場合に問題なく機能します。

私のカスタムモデルバインダーとワイヤーアップ:

public class IdentityModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        Identity identity = new Identity(Guid.Parse(bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue));
        bindingContext.Model = identity;
        return true;
    }
}

GlobalConfiguration.Configuration.BindParameter(typeof(Identity), new IdentityModelBinder());

パラメーターのない Get にパラメーターが追加されている場合、上記のバインドは正常に行われることに注意してください。つまり、BooksController を次のように変更すると:

public class BooksController : ApiController
{
    public IEnumerable<string> Get(int page, string searchTerm)
    {
        return new string[] { "value1", "value2" };
    }

    public string Get(Identity id)
    {
        return "value";
    }
}

私のルート構成は、すぐに使える例です。

    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
        );
    }

その後、両方のエンドポイントに正しく移動できます。パラメーターなしの Get とカスタム ID を受け取る Get の両方を許可するように BooksController を設定するにはどうすればよいですか?

4

1 に答える 1

0

問題の原因を見つけたと思いますが、それを回避する方法がわかりません。

Web API ApiControllerActionSelector 内、具体的には ActionCacheSelectorItem.ctor 内で、アクション パラメーターを解決するためのロジックは次のとおりです。

_actionParameterNames.Add(
       actionDescriptor,
       actionBinding.ParameterBindings
                    .Where(binding => !binding.Descriptor.IsOptional && TypeHelper.IsSimpleUnderlyingType(binding.Descriptor.ParameterType) && binding.WillReadUri())
                    .Select(binding => binding.Descriptor.Prefix ?? binding.Descriptor.ParameterName).ToArray());

残念ながら、これは単純な型 (プリミティブ、日時、GUID など) のみがパラメーター リストに含まれることを意味します。上記のフィルター ロジックを拡張して、次のようにパラメーターを文字列から変換できるかどうかも考慮すると、構造体をパラメーター リストに表示できます。

_actionParameterNames.Add(
       actionDescriptor,
       actionBinding.ParameterBindings
                    .Where(binding => !binding.Descriptor.IsOptional && 
                          (TypeHelper.IsSimpleUnderlyingType(binding.Descriptor.ParameterType) ||
                          TypeHelper.HasStringConverter(binding.Descriptor.ParameterType)) && 
                          binding.WillReadUri())
                    .Select(binding => binding.Descriptor.Prefix ?? binding.Descriptor.ParameterName).ToArray());

私が間違っていることを願っています。立ち往生していることが判明した場合は、クールダウン期間後に戻ってきて、これを回答としてマークします。

于 2013-03-15T18:15:30.953 に答える