Giter VIP home page Giter VIP logo

Comments (12)

darewreck54 avatar darewreck54 commented on August 30, 2024

I did some more research and debug the Swashbuckle.Odata code. Figure i'll help debug the issue and make it easier for you.

private static ODataPath GenerateSampleODataPath(ODataRoute oDataRoute, string sampleODataAbsoluteUri)
{
var oDataPathRouteConstraint = oDataRoute.GetODataPathRouteConstraint();

        var model = oDataRoute.GetEdmModel();

        string route = ServiceRoot.AppendPathSegment(oDataRoute.RoutePrefix);

        ODataPath path = oDataPathRouteConstraint.PathHandler.Parse(model, route, sampleODataAbsoluteUri);
        return path;
    }

So the issue happens when it tries to Parse the IODataPathHandler. For some reason it cannot convert the parameters:

ServiceRoute: "http://any/"
route: "http://any/odata"
sampleODataAbsoluteUri : "http://any/odata/Orders(42)/Customers"

It throws an ODataException with an error message "Bad Request - Error in query syntax.".

But it's able to parse this successfully:

ServiceRoute: "http://any/"
route: "http://any/odata"
sampleODataAbsoluteUri : "http://any/odata/Customers(42)/Orders"

In the config file:

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

        config.AddCustomSwaggerRoute(customODataRoute, "/Orders({orderID})/Customers")
        .Operation(HttpMethod.Post)
        .PathParameter<int>("orderID")
        .BodyParameter<Customer>("customer");

CustomersController:

public Task Post([FromODataUri] int orderID, Customer customer)
{
throw new NotImplementedException();
}

OrdersController:
public async Task Post([FromODataUri] int customerId, Order order)
{
order.OrderId = SequentialGuidGenerator.Generate(SequentialGuidType.SequentialAtEnd);
order.CustomerId = customerId;

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _db.Orders.Add(order);
        await _db.SaveChangesAsync();

        return Created(order);
    }

It's not clear what is missing in the model that is causing the exception to be thrown when it tries to parse the OData path.

Source Code of Sample project:

https://onedrive.live.com/redir?resid=30915BBAD31877D9!427&authkey=!APPSsuCfmp4DyeQ&ithint=file%2czip

from swashbuckle.odata.

rbeauchamp avatar rbeauchamp commented on August 30, 2024

ok. thanks for the investigation! it helps.

from swashbuckle.odata.

rbeauchamp avatar rbeauchamp commented on August 30, 2024

This configuration:

config.AddCustomSwaggerRoute(customODataRoute, "/Orders({orderID})/Customers")
        .Operation(HttpMethod.Post)
        .PathParameter<int>("orderID")
        .BodyParameter<Customer>("customer");

and this controller action:

        [ResponseType(typeof(Customer))]
        public Task<IHttpActionResult> Post([FromODataUri] int orderID, Customer customer)
        {
            throw new NotImplementedException();
        }

won't work because the Order entity has a primary key of type GUID.

I'll investigate the issue reported in your original post. I.e.,

I'm having issue displaying other custom route other then HttpMethod.Post & HttpMethod.Get

from swashbuckle.odata.

rbeauchamp avatar rbeauchamp commented on August 30, 2024

This path in your original post does not look correct:

config.AddCustomSwaggerRoute(customODataRoute, "/Customers({Id})/Orders/({orderID}")

You don't have a closing parenthesis on the orderID... Maybe you don't even want the opening parenthesis? Also, do you want the forward slash after Orders?

from swashbuckle.odata.

rbeauchamp avatar rbeauchamp commented on August 30, 2024

@darewreck54 A fix is available in v2.8.1. FYI, your custom route config should look like:

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

and the OrdersController action:

        public async Task<IHttpActionResult> Delete([FromODataUri] int customerId, [FromODataUri] Guid orderID)
        {
            throw new NotImplementedException();
        }

See this unit test.

from swashbuckle.odata.

darewreck54 avatar darewreck54 commented on August 30, 2024

Thanks,

config.AddCustomSwaggerRoute(customODataRoute, "/Customers({Id})/Orders({orderID})")
.Operation(new HttpMethod("PATCH"))
.PathParameter("Id")
.PathParameter("orderID")
.BodyParameter("order");

with action:

public async Task Patch([FromODataUri] int customerId, [FromODataUri] Guid orderID, Order order)
{
throw new NotImplementedException();
}

This will result in issues with the swagger UI. Order Controller will not render at all.

Also is there a better way for error handling. It seems if if there is any issues with the configuration, you will either get controllers that do not render (user may never know) or stacktraces where it's hard to really determine what went wrong (A case is when you have incorrect parameters like you pointed out in my code)

If you update your unit test with:

[Test]
    public async Task It_allows_definition_of_custom_patch_routes()
    {
        using (WebApp.Start(HttpClientUtils.BaseAddress, builder => Configuration(builder, 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({orderID})", out pathItem);
            pathItem.Should().NotBeNull();
            pathItem.patch.Should().NotBeNull();

            await ValidationUtils.ValidateSwaggerJson();
        }
    }

and

private static void Configuration(IAppBuilder appBuilder, Type targetController)
{

       var config = appBuilder.GetStandardHttpConfig(targetController);

        // Define a custom route with custom routing conventions
        var conventions = ODataRoutingConventions.CreateDefault();
        conventions.Insert(0, new CustomNavigationPropertyRoutingConvention());
        var customODataRoute = config.MapODataServiceRoute("CustomODataRoute", "odata", GetCustomRouteModel(), batchHandler: null, pathHandler: new DefaultODataPathHandler(), routingConventions: conventions);

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

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

        config.AddCustomSwaggerRoute(customODataRoute, "/Customers({Id})/Orders({orderID})")
                   .Operation(new HttpMethod("PATCH"))
                   .PathParameter<int>("Id")
                   .PathParameter<Guid>("orderID")
                   .BodyParameter<Order>("order");

        config.EnsureInitialized();
    }

all the custom route test will fail now.

from swashbuckle.odata.

 avatar commented on August 30, 2024

You're welcome,
I'll research the PATCH issue. Re-opening this issue.

Also is there a better way for error handling

I'm working on some error handling now that might help. Yeah, it's sometimes hard for me (and users) to determine the root cause/scenario for an issue. Better handling will develop over time as the assumptions are understood and documented. Suggestions and, better yet, pull requests are welcome. Thanks!

from swashbuckle.odata.

darewreck54 avatar darewreck54 commented on August 30, 2024

Actually, I found the problem. It deals with the CustomNavigationPropertyRoutingConvention class and how you have to specify the correct parameters for each controller.

This is the updated code to make the new test changes I put above:

else if (typeof(OrdersController) == controllerType)
{
if (odataPath.PathTemplate.Equals("/entityset/key/navigation")) //POST OR GET
{
controllerContext.RouteData.Values["customerID"] = (odataPath.Segments[1] as KeyValuePathSegment).Value;
return controllerContext.Request.Method.ToString();
}
if (odataPath.PathTemplate.Equals("
/entityset/key/navigation/key")) //PATCH OR DELETE
{
controllerContext.RouteData.Values["customerID"] = (odataPath.Segments[1] as KeyValuePathSegment).Value;

                controllerContext.RouteData.Values["orderID"] = (odataPath.Segments[3] as KeyValuePathSegment).Value;
                return controllerContext.Request.Method.ToString();
            }
        }

Notice the "orderID" matches with the controller Patch function and the configuration parameters in the .PathParamter.

I guess when you use custom routing, with the way it's implemented you have to be very careful.

Removing the need to specify the addition routes via config.AddCustomSwaggerRoute() would help elevate this problem which is more of a feature enhancement as discussed in previous issues.

Thanks,
D

from swashbuckle.odata.

 avatar commented on August 30, 2024

As the root cause of the issue you mentioned and resolved isn't in the Swashbuckle.OData code base, I'm closing this issue. Thanks.

from swashbuckle.odata.

bkwdesign avatar bkwdesign commented on August 30, 2024

@darewreck54 - after 2 years now.. do you know if AddCustomSwaggerRoute now supports PATCH.. or is there a link you can post to the issue in the Swashbuckle.OData code base (was an issue opened?)

UPDATE: I misread @ghost's comment above. Disregard. I now see what you guys are talking about

from swashbuckle.odata.

darewreck54 avatar darewreck54 commented on August 30, 2024

from swashbuckle.odata.

bkwdesign avatar bkwdesign commented on August 30, 2024

The other side of the coin is correctly describing custom PATCH actions in WebApiConfig.cs using the ODataConventionModelBuilder - getting all the stars to align is a non-trivial task

from swashbuckle.odata.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.