Microsoft Azure API App Invalid Swagger error

Jared Johnson, 02 June 2015

In trying out the new Azure API apps by converting an existing ASP.NET Web API, you can run into this error as soon as you publish your API to azure for the first time and check out the API definition blade:

Trying to add the API as an Azure App client can get you nicer errors like: "Found operation objects with duplicate operationId 'Contacts_Get'". or worse just Bad Request errors. So what does this mean?

In Web API it is common to name endpoints based on the http verb needed to call them, if there are multiple using the same verb but with different parameters Web API can handle figuring out which is the intended endpoint.

However Swagger, the documentation framework that is used by API apps does not do this, it will break and cause the bad request errors if there are multiple endpoints with parameters.

Swagger also by default uses the endpoint name to create the operationId, in the form of {ControllerName}_{EndpointName}. This is used by Azure to identify the different endpoints available in the API and is the cause of the duplicate operationId errors.

In solving these issues, the easiest one to get out of the way is the duplicate operationId's. This can be done by renaming the endpoints, or by adding the ActionName annotation: [ActionName("GetById")]

Next, multiple endpoints that return the same result, such as endpoints that have additional parameters for filtering like GET api/contacts and GET api/contacts?type={type}. The swagger specification considers this to be one operation with optional parameters. There is a config setting called ResolveConflictingActions provided to merge multiple endpoints in this situation, however you will need to write the code that merges the descriptions yourself. The most basic way to do this is c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); which will just take the first conflicting endpoint as the correct one. Another way to handle this is to create a single action with optional parameters that will then call your existing endpoints which have now been made private.

As a consequence of the above behaviour of Swagger, endpoints that do not have the same result can also be caught up in this, like the following common scenario: GET api/contacts and Get api/contacts?id={id}. These obviously should not be merged. An easy way to solve this is by using routes. Setting a route annotation of [Route("api/contacts/{id}")] on the Get by Id endpoint will differentiate the two endpoints enough for swagger, and the original way of calling the endpoint by using the query string will not be affected.