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

How to use regular expressions in route path and how to make parameters optional #1866

Closed
hbakhtiyor opened this issue Oct 17, 2018 · 24 comments
Assignees

Comments

@hbakhtiyor
Copy link

hbakhtiyor commented Oct 17, 2018

  • how to use regular expression in route path?

for example like expressjs

app.get('/assets/:uuid([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', function (req, res) {
}) 
  • how to make route param is optional?

for example

/assets/eed761cf-5418-53d3-9f67-c279f58ac875.png
/assets/eed761cf-5418-53d3-9f67-c279f58ac875, .format is optional

import { get, param } from '@loopback/rest';

export class AssetController {
  @get('/assets/{uuid}.{format}')
  assets(@param.path.string('uuid') uuid: string,
             @param.path.string('format') format?: string): object {
      return {};
  }
}
@hbakhtiyor
Copy link
Author

cc @raymondfeng

@dhmlau
Copy link
Member

dhmlau commented Oct 22, 2018

@hacksparrow , could you please help?

@hacksparrow
Copy link
Contributor

hacksparrow commented Oct 23, 2018

@hbakhtiyor LB4 uses Express' serve-static middleware for serving static files, you can see how to use regular expression and other patterns for a path at http://expressjs.com/en/4x/api.html#path-examples.

@hbakhtiyor
Copy link
Author

i can't use like this in lb4, which valid in expressjs

@get('/assets/{uuid}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})')

@hacksparrow
Copy link
Contributor

hacksparrow commented Oct 23, 2018

At the moment, static files can be served from a single mount point only. We don't have a controller interface yet. Having said that, you LB4 provides an Express-like method (app.static()) for setting up statics assets, here is an example:

  const app = new Lb4appApplication(options);

  const publicDir = path.join(__dirname, 'public');

  await app.boot();
  await app.start();

  app.static(/\/abc|\/xyz/, publicDir); // <-- regex path

  const url = app.restServer.url;
  console.log(`Server is running at ${url}`);
  console.log(`Try ${url}/ping`);

@hbakhtiyor
Copy link
Author

i don't need exactly for static serve, not related to my questions

@hacksparrow
Copy link
Contributor

LB4 does not support regex based paths.

@bajtos @raymondfeng @strongloop/sq-lb-apex how about supporting regex-based paths?

@marioestradarosa
Copy link
Contributor

how to use regular expression in route path?

Is this is to evaluate a regular expression against the uuid path parameter ?
something like this?

if  (/^[0-9a-fA-F]{24}$/.test(uuidParamter)) {
} else { //error, not evaluated }

@hbakhtiyor
Copy link
Author

yeah, want to add this in @get decorator like express route

@raymondfeng
Copy link
Contributor

@hbakhtiyor We intentionally disabled the regular expression/wildcard support in paths to be compatible with OpenAPI and swagger-ui. See some discussions at #1765.

OpenAPI spec is very vague about path templating - https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#path-templating. See more information at:

We can reevaluate the issue once the spec has a clear direction.

@bajtos
Copy link
Member

bajtos commented Oct 30, 2018

+1 to what @raymondfeng wrote above.

app.get(
   '/assets/:uuid([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', 
   function (req, res) {
  }
);

My recommendation: Move the regular expression from the path definition into an OpenAPI spec parameter constraint.

    this.route(
      'get',
      '/asserts/:uuid',
      {
        parameters: [
          {
            name: 'uuid',
            in: 'path',
            schema: {
              type: 'string',
              pattern:
                '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$',
            },
          },
        ],
        responses: {
          /*...*/
        },
      },
      function(uuid: string) {
        // serve the asset
      },
    );

Please note that LB4 does not validate non-body parameters (e.g. path parameters yet) - see #1573. (Implementing these validation should be pretty easy, would you like to contribute it yourself? Let's discuss in #1573).

@bajtos bajtos changed the title questions related to route How to use regular expressions in route path and how to make parameters optional Oct 30, 2018
@bajtos
Copy link
Member

bajtos commented Oct 30, 2018

how to make route param is optional?

export class AssetController {
  @get('/assets/{uuid}.{format}')
  assets(@param.path.string('uuid') uuid: string,
             @param.path.string('format') format?: string): object {
      return {};
  }
}

Please note that in OpenAPI, path parameters are always required and all other parameters are optional by default. See OpenAPI's section Parameter Object.

Field Name Type Description
required boolean Determines whether this parameter is mandatory. If the parameter location is "path", this property is REQUIRED and its value MUST be true. Otherwise, the property MAY be included and its default value is false.

As a result, it is not possible to have an optional path parameter in LB4, you have to move such parameter e.g. to a query string.


Ideally, I would like LB4 to provide the following syntax for annotating parameters as required:

@param.query.string('format', {required: true}) format?: string

For now, you have to use @param() directly (but don't have to specify the type since LB4 can infer the type from TypeScript metadata):

@param(name: 'format', in: 'query', required: true}) format?: string

I opened a new issue to keep track of this improvement, see #1940

@bajtos
Copy link
Member

bajtos commented Oct 30, 2018

@hbakhtiyor did we answer your questions?

@raymondfeng @hacksparrow @dhmlau I think we should capture these Q&A in our docs. What do you think?

@hacksparrow
Copy link
Contributor

Agreed. He won't be the first and the last to come up with this requirement.

@hbakhtiyor
Copy link
Author

@bajtos, partially, yeah

currently going with the snippet, also can't put . in the lb4 route, throw an error

  @get('/assets/{params}')
  async assets(
    @param.path.string('params') params: string
  ): Promise<object> {
    const routeAssetsMatches = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:\.(jpeg|jpg|png))?$/i.exec(params);
    if (!routeAssetsMatches || !routeAssetsMatches[1]) {
      throw new HttpErrors.UnprocessableEntity('the format of route is invalid');
    }

    const uuid = routeAssetsMatches[1];
    const format = routeAssetsMatches[2] || 'jpeg';
  }

@bajtos
Copy link
Member

bajtos commented Nov 23, 2018

currently going with the snippet

👍

also can't put . in the lb4 route, throw an error

Could you please post a code snippet showing what are you trying to do?

@bajtos
Copy link
Member

bajtos commented Nov 23, 2018

I am closing this issue as resolved.

@bajtos bajtos closed this as completed Nov 23, 2018
@hbakhtiyor
Copy link
Author

Could you please post a code snippet showing what are you trying to do?

@bajtos , e.g

  @get('/assets/{uuid}.{format}')
  async assets(
    @param.path.string('uuid') uuid: string,
    @param.path.string('format') format: string
  ): Promise<object> {
  }

@bajtos
Copy link
Member

bajtos commented Nov 26, 2018

@hbakhtiyor Interesting! I tried your example path in Swagger Editor (http://editor.swagger.io) and it looks like your example is a valid OpenAPI spec path template.

The spec:

/pet/{petId}.{format}:
    get:
      # ...

The request made by the editor (petId = 1, format = json):

curl -X GET "https://petstore.swagger.io/v2/pet/1.json" -H "accept: application/xml"

Please open a new GitHub issue to discuss how LoopBack can add support for this use case.

@raymondfeng
Copy link
Contributor

I thought it was supported by design. Let me check.

@bajtos
Copy link
Member

bajtos commented Nov 27, 2018

@hbakhtiyor the issue with /assets/{uuid}.{format} should be fixed now. Could you please update your dependencies to latest versions and let us know if the problem went away?

@hbakhtiyor
Copy link
Author

@bajtos works well, but .{format} is still not making optional

@bajtos
Copy link
Member

bajtos commented Dec 4, 2018

works well, but .{format} is still not making optional

Hmm, that's certainly not expected. I assume you have upgraded your dependencies to use @loopback/rest@1.4.0?

Could you please take a look at the changes made in 47c24cb and try to write a new test that will reproduce your use case and fail against the current implementation? Start by forking loopback-next, then create a new feature branch, etc.

@raymondfeng
Copy link
Contributor

works well, but .{format} is still not making optional

Why do you expect .{format} to be optional? The pattern says it expects the format comes after a ..

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

No branches or pull requests

6 participants