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

Routes with parameters are not working in some cases #2188

Closed
ambienthack opened this issue Dec 23, 2018 · 18 comments
Closed

Routes with parameters are not working in some cases #2188

ambienthack opened this issue Dec 23, 2018 · 18 comments
Assignees
Labels
bug good first issue REST Issues related to @loopback/rest package and REST transport in general

Comments

@ambienthack
Copy link

ambienthack commented Dec 23, 2018

Description / Steps to reproduce / Feature proposal

Create two routes like this

  @patch('/users/{userId}/meals/{id}')
  async updateById(
    @param.path.string('userId') userId: string,
    @param.path.string('id') id: string
  ): Promise {
    //anyting
  }

  @patch('/users/{id}')
  async updateById(
    @param.path.string('id') id: string
  ): Promise {
    //anything
  }

Make a request PATCH /users/5c1d78842566764efc8b9605

Current Behavior

{
  "error": {
    "statusCode": 404,
    "name": "NotFoundError",
    "message": "Endpoint \"PATCH /users/5c1d78842566764efc8b9605\" not found."
  }
}

If the error doesn't arise, try changing parameter names in path patterns, like this:
@patch('/users/{id}/meals/{mealId}')
@patch('/users/{userId}')

Expected Behavior

Controller method should be invoked

I believe there is a bug here

    for (const child of children) {
        const result = search(keys, index + 1, params, child.node);
        if (result) {
            Object.assign(params, child.params);
            return result;
        }
    }

Should be if (result && isNodeWithValue(result.node)) {.

When multiple children are found, if the first child checked is @patch('/users/{userId}/meals/{id}'), search() method assumes route is found & doesn't check other routes.

@bajtos
Copy link
Member

bajtos commented Jan 25, 2019

@ambienthack thank you for reporting the issue, your analysis looks reasonable to me. Would you like to contribute the fix yourself? See https://loopback.io/doc/en/contrib/code-contrib.html to get started.

@raymondfeng you are the author of the trie router, could you PTAL?

@bajtos bajtos added bug good first issue REST Issues related to @loopback/rest package and REST transport in general labels Jan 25, 2019
@Joel-Raju
Copy link

Joel-Raju commented Feb 7, 2019

I seem to have run into the same issue. These are my controller actions

Action 1

@get('/restaurants/{id}')
async getRestaurantById(
@param.path.number('id') id: number,
  ): Promise<Restaurant> {
    console.log(id);
    return await this.restaurantRepository.findById(id);
  }

Action 2

@get('/restaurants/', {
async find(
@param.query.object('filter', getFilterSchemaFor(Restaurant))
filter?: Filter,
): Promise<Restaurant[]> {
  return await this.restaurantRepository.find(filter);
}

Current Behavior
Action 1 and Action 2 throws a 404

{
  "error": {
    "statusCode": 404,
    "name": "NotFoundError",
    "message": "Endpoint \"GET /restaurants/1\" not found."
  }
}

Expected Behavior

Controller method should be invoked

Other Observations

Even after removing Action 2 and all other actions in the controller that matches the same route Action 1 is still not being invoked.

@Joel-Raju
Copy link

would really appreciate if this could be resolved soon.

@raymondfeng
Copy link
Contributor

@Joel-Raju Do you have both methods in the same controller class?

@raymondfeng
Copy link
Contributor

BTW, do you know if return await this.restaurantRepository.findById(id); is invoked and returns an instance?

@raymondfeng
Copy link
Contributor

Can you remove trailing / and see?

@get('/restaurants', {
async find(
@param.query.object('filter', getFilterSchemaFor(Restaurant))
filter?: Filter,
): Promise<Restaurant[]> {
  return await this.restaurantRepository.find(filter);
}

@raymondfeng
Copy link
Contributor

raymondfeng commented Feb 8, 2019

@Joel-Raju Please verify #2355. Let us know whether the newly added test cover your case or not.

@Joel-Raju
Copy link

Joel-Raju commented Feb 8, 2019

@raymondfeng thanks for the quick follow-up

@Joel-Raju Do you have both methods in the same controller class?

Yes.

BTW, do you know if return await this.restaurantRepository.findById(id); is invoked and returns an instance?

It doesn't seem to hit route @get('/restaurants/{id})', I tried logging before querying the repository

Can you remove trailing / and see?

@get('/restaurants', {
async find(
@param.query.object('filter', getFilterSchemaFor(Restaurant))
filter?: Filter,
): Promise<Restaurant[]> {
  return await this.restaurantRepository.find(filter);
}

When removing the trailing space, the @get('/restaurants)' is being invoked.

@Joel-Raju Please verify #2355. Let us know whether the newly added test cover your case or not.

this covers my use case.

@raymondfeng
Copy link
Contributor

When I remove the trailing space, the @get('/restaurants)' is being invoked. @get('/restaurants/{id})' doesn't work

That's weird. What order do you define two routes in the controller?

Which request URL do you use to invoke the API? With trailing slash or not?

@Joel-Raju
Copy link

I first have @get(/restaurants) and then @get(/restaurants/{id}). Also tried reversing the order, still doesn't work.

Even tried just with @get(/restaurants/{id}) commenting out all other methods in the controller but doesn't work.

These are the request URLs

this works
curl -X GET "http://localhost:3001/restaurants" -H "accept: application/json"

this doesnt work
curl -X GET "http://localhost:3001/restaurants/1" -H "accept: application/json"

@raymondfeng
Copy link
Contributor

@Joel-Raju Can you create a simple app that can reproduce your problem and share it with us on github?
You can folk loopback-next and add your app to sandbox directory.

Joel-Raju added a commit to Joel-Raju/loopback-next that referenced this issue Feb 8, 2019
@Joel-Raju
Copy link

Joel-Raju commented Feb 8, 2019

@raymondfeng forked and added a sample project that illustrates the issue

https://github.com/Joel-Raju/loopback-next/tree/master/sandbox/test-app

this route works
http://localhost:3000/restaurants

this doesn't work
http://localhost:3000/restaurants/1

@Joel-Raju
Copy link

Joel-Raju commented Feb 8, 2019

Guess I didn't mention that I have a hasMany relationship. It worked before adding the relationship.

@raymondfeng
Copy link
Contributor

@Joel-Raju Thanks for the sample app. I was able to reproduce the problem and fix it in #2355.

@raymondfeng
Copy link
Contributor

Guess I didn't mention that I have a hasMany relationship. It worked before adding the relationship.

That's the root cause as you added /restaurants/{restaurantId}/orders, which overlaps with /restaurants/{id} using different var names.

@Joel-Raju
Copy link

oh okay. This will get resolved with #2355 ?

@raymondfeng
Copy link
Contributor

Yes. Before it's landed, you can probably work around the issue by changing the path /restaurants/{restaurantId}/orders to be /restaurants/{id}/orders.

@Joel-Raju
Copy link

thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug good first issue REST Issues related to @loopback/rest package and REST transport in general
Projects
None yet
Development

No branches or pull requests

4 participants