Question

I have just upgraded my web API help page to version 2.1. When I tried to open the help page, I got this error:

[ArgumentException: An item with the same key has already been added.]
System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) +52
System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) +10695474
System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value) +10
System.Web.Http.Description.ApiExplorer.TryExpandUriParameters(IHttpRoute route, HttpParsedRoute parsedRoute, ICollection`1 parameterDescriptions, String& expandedRouteTemplate) +407
System.Web.Http.Description.ApiExplorer.PopulateActionDescriptions(HttpActionDescriptor actionDescriptor, IHttpRoute route, String localPath, Collection`1 apiDescriptions) +258
System.Web.Http.Description.ApiExplorer.ExploreDirectRoute(HttpControllerDescriptor controllerDescriptor, CandidateAction[] candidates, IHttpRoute route) +358
System.Web.Http.Description.ApiExplorer.InitializeApiDescriptions() +410
System.Lazy`1.CreateValue() +416
System.Lazy`1.LazyInitValue() +152
System.Lazy`1.get_Value() +75
System.Web.Http.Description.ApiExplorer.get_ApiDescriptions() +40
CobaWebApi.Areas.HelpPage.HelpPageConfigurationExtensions.GetHelpPageApiModel(HttpConfiguration config, String apiDescriptionId) in e:\SPIKES\CobaWebApi\Areas\HelpPage\HelpPageConfigurationExtensions.cs:224
CobaWebApi.Areas.HelpPage.Controllers.HelpController.Api(String apiId) in e:\SPIKES\CobaWebApi\Areas\HelpPage\Controllers\HelpController.cs:38
lambda_method(Closure , ControllerBase , Object[] ) +180
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +59
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +435
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +60
System.Web.Mvc.Async.ActionInvocation.InvokeSynchronousActionMethod() +76
System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +36
System.Web.Mvc.Async.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult) +73
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +136
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +102
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +49
System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +117
System.Web.Mvc.Async.<>c__DisplayClass48.<InvokeActionMethodFilterAsynchronouslyRecursive>b__41() +323
System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +44
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +47
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +136
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +102
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +50
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +72
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +185
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +42
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +133
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +56
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +40
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +34
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +44
System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +62
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +39
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +39
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +70
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +139
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +59
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +40
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9628972
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

I have tracked down the problem to these two controller actions:

public class MyController : ApiController
{
    /// <summary>
    /// a quick brown fox
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    [HttpGet]
    [ResponseType(typeof(OneResponse))]
    [Route("api/stuff/{Id}")]
    public IHttpActionResult GetOne([FromUri] OneRequest request)
    {
        return Ok(new OneResponse());
    }

    /// <summary>
    /// lazy dog
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    [HttpGet]
    [ResponseType(typeof(ManyResponse))]
    [Route("api/stuff")]
    public IHttpActionResult GetMany([FromUri] ManyRequest request)
    {
        return Ok(new ManyResponse());
    }
}

If I remove [FromUri], help page will display without error. Somehow it considers the two endpoints to be the same if I use [FromUri].

Is there a workaround for this?

Was it helpful?

Solution

This is indeed a bug in ApiExplorer. Filed the following issue: https://aspnetwebstack.codeplex.com/workitem/1683

The issue here can be reproed using a single action. In the above scenario I believe you have an Id property in OneRequest.

Workaround:
Rename the property on the route template so that it has a different case rather the one in the property of the model. So here rename the route parameter from Id to id in the template and this should fix your problem.

public class OneRequest
{
     public int Id { get; set; }
     ....
}

[Route("api/stuff/{id}")]
public IHttpActionResult GetOne([FromUri] OneRequest request)

Actually Web API shouldn't be doing a case sensitive way of matching these and this seems to be core bug because of which you are seeing the error.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top