0

随着 Angular、NgRx-Data 和 NestJs 越来越流行,我觉得可能有不少程序员对以下查询语法感到疑惑。

我有一个使用 Angular 8 和 NgRx-Data 组成的客户端(前端)的运行原型。后端是基于 NestJs 的服务器 + MySQL。

除了查询之外,我可以很好地在所有部分之间检索和传递数据。我似乎无法找到有关语法的适当文档。

以下是如何设置客户端的示例:

                        // Simple entity example (all ngrx-data metadata are declared and set):
export class Hero { 
    id: number;
    name?: string;
    age?: number;
}

实体服务/用于获取数据

@Injectable({providedIn: 'root'})
export class HeroService extends EntityCollectionServiceBase<Hero> {
  constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
    super('Hero', serviceElementsFactory);
  }
}

显示数据的组件

@Component({
  selector: 'hero-comp',
  templateUrl: './hero.component.html', 
  styleUrls: ['./hero.component.scss']
})
export class HeroComponent {
    heroData$: Observable<Hero[]>;
    constructor(private heroDatService: HeroService) {
        this.heroData$ = this.heroDatService.entities$;
    }

    private getAllData() {
                          // This works nicely, I get all records from the db via server
        this.heroDatService.getAll();
    }

    private queryData() {
                          // This queryParams syntax fails - server complains with this error:
                          //      [HttpExceptionFilter] GET /hero/?age>20 
                          //      QueryFailedError: ER_EMPTY_QUERY: Query was empty
                          // QUESTION: What is the proper syntax?
        let queryParams: QueryParams = {
            'age > 20'
        }
        this.fetchDataService.getWithQuery(queryParams);
    }

这是与服务器相关的代码摘录:-(有一个服务,但为了简单起见,我将 repo 函数移至控制器函数):

@Controller('hero')
export class HeroController <Hero> {
    constructor(readonly repo: Repository<Hero>) {}

                          // This returns nicely all Hero records from the MySQL db server
@Get()
async getAll(): Promise<Hero[]> {
    return await this.repo.find();
} 
                          // This does not work !
                          // I am not sure about any piece of the code here !
@Get('query')
async query(@Query() sql): Promise<any> {
                          // Does the sql argument need to be manipulated into parameters: {...} ?
                          // If yes - how ?
    let parameters: undefined;
    return await this.repo.query(sql, parameters);
}

请查看每个代码行上方的注释 - 问题已在此处详细说明。

以下是重要的问题:

在客户端,我们如何正确传递其中一些示例的查询条件: - {'age > 20'} - {'age BETWEEN 20 AND 40'} - {'age = 20 OR age = 30 OR age = 40'} - {'name = "Superman"'} - {'name LIKE "Super%"'} - 等等。

此外,传递完整 SQL 语句的语法是什么,例如: - {'SELECT * FROM Heroes WHERE name LIKE "Super%" AND Age > 20;'} 并从服务器获取结果。

为了使这些查询正常工作,两端(客户端和服务器)需要做什么?

非常感谢所有输入。

4

1 回答 1

1

您似乎对 HTTP 查询参数和 SQL 查询感到困惑,这是两个不同的主题。在 HTTP 的上下文中,查询参数是可以从客户端传递到服务器并修改 HTTP 调用结果的参数。查询参数在 URL 中始终以 a 开头,以 的形式传递,并以 . 分隔。?<key>=<value>&

SQL 查询是一个特定的字符串,它告诉 SQL 服务器要查询的表、哪些列、哪些条件。通常以 的形式出现SELECT <columns> FROM <table> WHERE <conditions>;,但它们可能比这复杂得多。

现在定义已经完成,您尝试到达的端点应该是/hero/query. 您最终需要在服务器端进行大量数据处理、清理、确保传入的字符串适用于 SQL WHERE 子句、确保您不会受到 SQL 注入的攻击(如果您通过查询参数直接到您的查询,您将是),但一个非常非常天真的方法看起来像这样:

@Controller('hero')
export class HeroController {

  constructor(@InjectRepository(Hero) private readonly repo: Repository<Hero>) {}
  @Get('query')
  queryForHero(@Query() queryParam) {
    return this.repo.query(`SELECT <field_go_here> FROM Hero WHERE ${queryParams.query};`);
  }
}

出于对世界上所有美好事物的热爱,请不要实际使用上述代码。它 100% 容易受到各种 SQL 注入的攻击。

相应的请求可能类似于

curl http://<host>/hero/query?query=Name%3D%27Superman%27

这将导致服务器使用查询

SELECT <fields_go_here> FROM Hero WHERE Name='Superman';

真的很想在发送到你的 SQL 服务器之前添加大量关于进入服务器的验证,以免你最终像 Little Bobby Table 一样

希望这有助于您走上正确的道路。

于 2020-06-09T03:10:19.627 回答