Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controllers not being rendered depending on what functions defined (issue started the transition from 2.0.0 to 2.1.0) #28

Closed
darewreck54 opened this issue Dec 20, 2015 · 17 comments
Labels

Comments

@darewreck54
Copy link

After upgrading to 2.6.1, my controllers are not rendering on the UI.

The swagger ui screen just display "Finished Loading Resource Information. Rendering Swagger UI..." and never loads the controllers.

Did anything change between those versions?

I actually was able to narrow it down when it started breaking and that was from version 2.0.0 to version 2.1.0. In my project, I injected a custom route which implements the SelectAction & SelectController methods. Surprisingly in 2.0.0, when you trigger the swagger UI the code will hit the SelectionAction and the controllerContext will not be null. However, in 2.1.0+ the controllerContext is null and that is why the Swagger UI does not render any of the controllers or actions.

Any idea of what could have caused this?

Update

So it seems the issue is because from 2.0.0 to 2.1.1, more stricter logic was put into place. For some reason Swashbuckle.OData would crap out and the exception would never be thrown or never be displayed in the UI. As a result, controllers that are process after that exception never gets rendered in the swagger UI. I believe it process the controllers in ABC order.

I was also able to isolate the problem even further. There is three problems that I've seen.

  1. If you implement a function with the signature:
[EnableQuery]
public Task<IHttpActionResult> Get(int value)
{
        throw new NotImplementedException();
}

This will throw an exception "{"Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details."}"

Inner Exception: "
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)
at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)
at System.Web.OData.Routing.ODataActionSelector.SelectAction(HttpControllerContext controllerContext)
at Swashbuckle.OData.Descriptions.HttpRequestMessageExtensions.GetHttpActionDescriptor(HttpRequestMessage request)
"

  1. If you have a function parameter of ODataQueryOptions
[EnableQuery]
 public Task<IHttpActionResult> Get(ODataQueryOptions<Customer> queryOptions)
{
        throw new NotImplementedException();
 }

This will actually cause the controller to be not rendered completely on the swagger UI. This will also prevent any other processing of any further controllers that have not be processed. The UI will only display the controller that were processed before the issue occurred.

  1. It's also interesting that if your "Customer" controller has a function Get() instead of GetCustomer(), the swagger will automatically generate two "Get" calls for the UI. One that takes in only the [enableQuery] parameters, and the other takes in an "int id" and a "select" parameter, but none of the other [enableQuery] parameters you would expect.

BTW: the current sample that is checked in goes in an infinite loop and never the swagger UI never loads. the 2.6.1 tagged sample. Also it doesn't compile due to some minor issue that can be fixed easily.

Let me know if this make sense,
You can actually just add to the project i linked you before and just add the methods I mentioned above to reproduce the problems.

Thanks,
Derek

@darewreck54 darewreck54 changed the title Controllers not displaying after upgrade from 2.0.0 to 2.6.1 Controllers not being rendered depending on what functions defined (issue started the transition from 2.0.0 to 2.1.0) Dec 20, 2015
@rbeauchamp
Copy link
Owner

Thanks Derek for the detailed analysis and explanation. I always ensure that the tests pass before releasing, so it's a matter of building enough scenario coverage. I'll include your project and investigate.

@darewreck54
Copy link
Author

The project I included is just the modified sample of 2.0.0. It seems like you did a lot of changes up to 2.6.1. I just used my example to prove the issue since the newest sample wasn't really working.

Also just curious, does the swashbuckle.odata code make some assumption on the routing and convention? Does it leverage off the select controller and select action methods?

Thanks
Derek

Sent from my iPhone

On Dec 20, 2015, at 11:54 AM, Richard Beauchamp notifications@github.com wrote:

Thanks Derek for the detailed analysis and explanation. I always ensure that the tests pass before releasing, so it's a matter of building enough scenario coverage. I'll include your project and investigate.


Reply to this email directly or view it on GitHub.

@rbeauchamp
Copy link
Owner

does the swashbuckle.odata code make some assumption on the routing and convention?

It basically works like this: it generates a set of potential paths based upon the entity data model (and custom routes). It then verifies these against the actual controllers/actions by simulating a real HTTP request. The request locates the controller/action by going through the WebApi/OData infrastructure like a real request would. Once it locates a controller and action it generates an ApiDescription that is expected/leveraged by Swashbuckle. From that point on Swashbuckle infrastructure takes over.

@rbeauchamp
Copy link
Owner

And in terms of development/deployment, the latest sample website always runs against the latest code base in master. And whenever a commit occurs to master the code is automatically built and the tests are run. For example, see the latest CI build for the v2.6.1 tagged sample. The "Try it out!" sample website automatically gets deployed for each commit to master. So when you say "the newest sample wasn't really working.", I'm not sure what you are referring to.

@darewreck54
Copy link
Author

Oo I meant that I do 2.6.1, ran the sample from my machine (at the time the code didn't compile) so I made fixes to make it compile. When I started the swagger ui, it would never render and keep trying to load. It was also late, so I can try it again.

Sent from my iPhone

On Dec 20, 2015, at 5:52 PM, Richard Beauchamp notifications@github.com wrote:

And in terms of development/deployment, the latest sample website always runs against the latest code base in master. And whenever a commit occurs to master the code is automatically built and the tests are run. For example, see the latest CI build for the v2.6.1 tagged sample. The "Try it out!" sample website automatically gets deployed for each commit to master. So when you say "the newest sample wasn't really working.", I'm not sure what you are referring to.


Reply to this email directly or view it on GitHub.

@ghost ghost added the bug label Dec 21, 2015
@darewreck54
Copy link
Author

I did some investigating why i had issue with building and running the sample:

So when you load up the project, I get a complaint with:

"Swashbuckle.OData.Nuget\Swashbuckle.OData.NuGet.nuproj: The application which this project type is based on was not found. Please try this link for further information: http://go.microsoft.com/fwlink/?LinkID=299083&projecttype=FF286327-C783-4F7A-AB73-9BCBAD0D4460"

So the errors is a result of which visual studio you use. If you use 2013, there are some operations that aren't valid:

Doesn't recognize "nameOf"

  • throw new ArgumentOutOfRangeException(nameof(parameter), parameter, null);

or $

  • throw new Exception($"Could not determine .NET type for parameter type {type} and format 'null'");

In 2015, those operators are recognizable.

You also have to install https://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970 and enable static checking on the Sample project to get it to run successfully.

@ghost
Copy link

ghost commented Dec 23, 2015

Yes, it's developed with VS 2015 and uses the new C# 6.0 features. And the NuGet packages need to be restored before loading the NuGet project (which is an anomaly of the NuGet project type. Probably need to add the NuGet targets to source control). I'll add some development notes to the README for future devs.

EDIT:

I've now added a Development section to the README and have added the tools required to load the Swashbuckle.OData.Nuget project.

ghost pushed a commit that referenced this issue Dec 26, 2015
@rbeauchamp
Copy link
Owner

Hey @darewreck54, maybe the issues have been fixed between v2.6.1 and v2.8.0, but I have written unit tests in v2.8.0 to reproduce your issues and they are all passing. Please give v2.8.0 a try and let me know of you still have any issues. If you do, please update the unit tests to reproduce your issue(s). Will re-open if your issues still exist. Thanks!

@darewreck54
Copy link
Author

l only see 2.7.2 to dl. Not v2.8.0.

Date: Sat, 26 Dec 2015 15:04:11 -0800
From: notifications@github.com
To: Swashbuckle.OData@noreply.github.com
CC: d_hang10@hotmail.com
Subject: Re: [Swashbuckle.OData] Controllers not being rendered depending on what functions defined (issue started the transition from 2.0.0 to 2.1.0) (#28)

Hey @darewreck54, maybe the issues have been fixed between v2.6.1 and v2.8.0, but I have written unit tests in v2.8.0 to reproduce your issues and they are all passing. Please give v2.8.0 a try and let me know of you still have any issues. If you do, please update the unit tests to reproduce your issue(s). Will re-open if your issues still exist. Thanks!


Reply to this email directly or view it on GitHub.

@rbeauchamp
Copy link
Owner

You should be able to see it now. Sometimes it takes a while to index the NuGet package...

@darewreck54
Copy link
Author

I found a minor issue when testing.

If you were to add:

OrdersController.cs

[EnableQuery]
public async Task Get([FromODataUri] int customerId, ODataQueryOptions queryOptions)
{
throw new NotImplementedException();
}

Config:

 config.AddCustomSwaggerRoute(customODataRoute, "/Customers({Id})/Orders")
           .Operation(HttpMethod.Get)
           .PathParameter<int>("Id");

The "EnableQuery" doesn't get picked up by the Swashbuckle.OData. As a result, when you use the swagger UI, you are not presented the option to add OData Query params such as expand.

I also notice that if you have two Get methods:

  • GetCustomer(..)
  • Get(..)

Naturally either WebAPI or OData will choose GetCustomer to execute first vs. the Get method. I'm not sure how priority works in this case.

Unit test for you:

[Test]
public async Task It_allows_definition_of_custom_get_routes_and_has_all_optional_odata_query_parameters()
{
using (WebApp.Start(HttpClientUtils.BaseAddress, appBuilder => Configuration(appBuilder, typeof(OrdersController))))
{
// Arrange
var httpClient = HttpClientUtils.GetHttpClient(HttpClientUtils.BaseAddress);

            // Act
            var swaggerDocument = await httpClient.GetJsonAsync<SwaggerDocument>("swagger/docs/v1");

            // Assert
            PathItem pathItem;
            swaggerDocument.paths.TryGetValue("/odata/Customers({Id})/Orders)", out pathItem);
            pathItem.get.parameters.Where(parameter => parameter.name.StartsWith("$")).Should().OnlyContain(parameter => parameter.required == false);

            await ValidationUtils.ValidateSwaggerJson();
        }
    }

I think that's how it should look.

Thanks,
D

@rbeauchamp
Copy link
Owner

Thanks, yeah the enablequery issue was documented today in #37.

@darewreck54
Copy link
Author

do you know how the priority works for the question on:

I also notice that if you have two Get methods:

GetCustomer(..)
Get(..)

Either WebAPI or OData will choose GetCustomer to execute first vs. the Get method. I'm not sure how priority works in this case.

Thanks,
Derek

@rbeauchamp
Copy link
Owner

Typically WebApi and OData have different base paths and the methods are in different controllers so they should both be captured and available on the swagger ui. I don't think any priority comes into play. does that answer your question?

@darewreck54
Copy link
Author

Actually found a problem....

If you go to the customercontroller, and comment out all the Get Methods and only have:

[EnableQuery]
    public Task<IHttpActionResult> Get(ODataQueryOptions<Customer> queryOptions)
    {
        throw new NotImplementedException();
    }

The swagger UI will only load one controller which is the client. It won't load the customer controller at all and any other controllers after customer.

However, If you include a GET for a specific customer then it works:

[EnableQuery]
public Task Get(ODataQueryOptions queryOptions)
{
throw new NotImplementedException();
}
[EnableQuery]
public Task Get(int key)
{
throw new NotImplementedException();
}

@rbeauchamp
Copy link
Owner

ok, thanks for the problem investigation. and thanks for the unit test above.

@rbeauchamp rbeauchamp reopened this Dec 29, 2015
@ghost ghost closed this as completed in 62f164c Dec 30, 2015
@ghost
Copy link

ghost commented Dec 30, 2015

The scenario above:

comment out all the Get Methods and only have:

is now fixed in v2.8.5. Thanks for finding the issue!

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants