V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
zycojamie
V2EX  ›  程序员

后端要求前端传的查询参数

  •  
  •   zycojamie · 2020-04-17 14:39:12 +08:00 via Android · 7989 次点击
    这是一个创建于 1710 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如,你输入一个手机号,进行查询,要把你手机号的值,查询是 like 还是=,数据是 string 还是 int,模糊还是精准查询,全部前端拼给后端。。。

    这种方案可行吗。。。

    79 条回复    2020-05-16 00:18:42 +08:00
    onepunch
        1
    onepunch  
       2020-04-17 14:43:42 +08:00
    模糊查询前端做
    racecoder00
        2
    racecoder00  
       2020-04-17 14:43:49 +08:00   ❤️ 1
    不怕前端传个 drop table?
    dremy
        3
    dremy  
       2020-04-17 14:44:18 +08:00 via iPhone
    SQL 注入了解一下,公司分分钟倒闭
    opengps
        4
    opengps  
       2020-04-17 14:44:38 +08:00
    关键词,和是否 like,可以传
    至于 string 还是 int 啥意思?
    Jooooooooo
        5
    Jooooooooo  
       2020-04-17 14:46:59 +08:00
    可行

    关注一下防注入就行

    不过虽然是可行, 方案不合理啊, 前端关心的是"模糊查询"而不是"模糊查询用 like"
    BreezeInWind
        6
    BreezeInWind  
       2020-04-17 14:49:29 +08:00
    传一串字符串,如果本身其实是可以精确匹配到的,但是查询人以为是模糊匹配的,因而传的是 like,后端怎么处理呢?一串非常长的数字,本来应该是字符串形式存放的,查询人以为是数字,传的是 int,此时又该怎么处理呢
    jatesun
        7
    jatesun  
       2020-04-17 14:50:35 +08:00
    算了,这种事前端就别揽了
    zycojamie
        8
    zycojamie  
    OP
       2020-04-17 14:51:29 +08:00 via Android
    [' tempMatchType ": " 5,4 ", " propertyName ": " name,mobile, " propertyvaluel ": ",13541218876 "),
    {" tempMatchType ": " 7 ", " propertyName ": " age " " propertyvaluel ": " 12 ", " tempType ": " Integer "},
    tempMatchType ": " 11 ", " propertyName ": " role.id ", " prepertyvalue1 ": " 1,2,3,4 ", " tempType ": " List < Long >"}]
    翻译成 hql = where ( name like %张三%' or mobile = 13541218876 and age > 12 and role.idin ( 1.2.3.4 )

    前端就要把查询参数封装成上面那种数组
    zycojamie
        9
    zycojamie  
    OP
       2020-04-17 14:53:52 +08:00 via Android
    @jatesun 后端架构师说,为了他们拜托大量 if else,所以要求前端按照他们订的查询规则封装请求参数。。。
    littleylv
        10
    littleylv  
       2020-04-17 14:54:03 +08:00
    我觉得楼主的意思并不是说前端要拼接好 ‘mobile like '%123%'’这样提交给后端,而是使用
    {'field': 'mobile', 'value': '123', 'is_fuzzy': true} 这样传给后端,让后端去拼接。
    loading
        11
    loading  
       2020-04-17 14:57:18 +08:00 via Android
    先在手机号前后拼接%,然后参数查询,用 like 。

    如果有注入风险,请大佬们马上提醒我。
    loading
        12
    loading  
       2020-04-17 14:58:17 +08:00 via Android
    sql 参数拼接还是不难的,没几行。
    谁都是 crud boy 。
    zycojamie
        13
    zycojamie  
    OP
       2020-04-17 14:58:19 +08:00 via Android
    @littleylv 类似,但要求更复杂些(把大于等于,小于,小于等于,等于)都约定好是什么特定值,然后前端传参的时候,就要传(大于等于,小于,小于等于,等于)
    littleylv
        14
    littleylv  
       2020-04-17 15:00:00 +08:00
    @loading #11
    @loading #12 没看懂你想表达什么


    @zycojamie #13 我觉得这么做挺好的,没问题
    Rwing
        15
    Rwing  
       2020-04-17 15:00:40 +08:00
    GraphQL 欢迎你
    yukiloh
        16
    yukiloh  
       2020-04-17 15:03:28 +08:00 via Android   ❤️ 2
    这种接口别被我逮到,不然就直接送你出名
    ZSeptember
        17
    ZSeptember  
       2020-04-17 15:04:33 +08:00
    这不是很正常的吗,难道不同的查询就要新增加一个接口?
    RESTful 一般只提供一个查询接口,参考
    https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md#971-filter-operations
    lancerly
        18
    lancerly  
       2020-04-17 15:05:03 +08:00
    @zycojamie 这太搞笑了吧,前端的代码就不需要整洁性了?就可以随便写一大堆 if else 了?
    wangxiaoaer
        19
    wangxiaoaer  
       2020-04-17 15:08:40 +08:00
    @yukiloh #16 这种接口有什么问题吗?

    假如一些业务系统就是很需要很精确的查询:比较、类别、模糊 /精确切换等你怎么做???

    现在后端要求是前端把这些非常精细的条件传过去,后端翻译成 sql,又没让前端直接拼、传 sql,只要后端在生成 sql 的时候做好防注入,有什么问题?
    pap3r
        20
    pap3r  
       2020-04-17 15:09:53 +08:00
    前端爸爸不帮后端儿子擦屁股
    jziwenchen
        21
    jziwenchen  
       2020-04-17 15:12:21 +08:00
    graphql 可以了解看看
    sun1991
        22
    sun1991  
       2020-04-17 15:14:58 +08:00
    要区分具体需求是什么再谈实现, 不要一上来就一顿喷...
    曾经做过的灵活可定制查询界面大概就是这样的实现.
    lneoi
        23
    lneoi  
       2020-04-17 15:15:16 +08:00
    这样处理太繁琐了吧 有点像是做后台的大量查询的需求
    fumichael
        24
    fumichael  
       2020-04-17 15:15:44 +08:00
    像这个一样?前端可以组装查询条件呀
    但不能后端直接接收前端参数作为 sql 语句
    我想到的是,可以使用枚举进行处理
    https://i.loli.net/2020/04/17/AfIQ4U7weZtDdSB.png
    purensong
        25
    purensong  
       2020-04-17 15:16:08 +08:00
    graphQL 不错
    cedoo22
        26
    cedoo22  
       2020-04-17 15:18:52 +08:00
    没安全规则吗???让前端拼。。😂😂
    z1154505909
        27
    z1154505909  
       2020-04-17 15:21:19 +08:00
    前端拼接????头一次听说
    CismonX
        28
    CismonX  
       2020-04-17 15:24:59 +08:00   ❤️ 1
    突然想起来之前我们学校有个英语课在线答题系统,前端传给后端的参数是一条条完整的 SQL 。

    有好事者试着传了个 DROP TABLE xxx,然后就没有然后了😂
    zycojamie
        29
    zycojamie  
    OP
       2020-04-17 15:40:59 +08:00 via Android
    安全的话,后端说他们通过框架层去校验和拦截,前端需要把传统的查询参数由{ user:"张三"}变成{ user:{
    matchType:"1",
    propertyName:"user",
    propertyValue:"张三",
    dataType:"string"
    }}
    matchType 为 1 代表“包含”
    Erroad
        30
    Erroad  
       2020-04-17 15:51:44 +08:00
    前端把 ui“翻译”成参数还是很正常的,拼 sql 是肯定不行的,让前端拼 sql 给后端的后端都是 xx
    littleylv
        31
    littleylv  
       2020-04-17 15:54:12 +08:00
    @cedoo22 #26
    @z1154505909 #27
    你们到底有没看仔细看楼主说的。。。。。。。。。。。。。。无力吐槽
    wyz123723
        32
    wyz123723  
       2020-04-17 15:57:23 +08:00
    现在后端真是越来越烂 /懒了
    wellsc
        33
    wellsc  
       2020-04-17 16:00:49 +08:00
    graphql + 1
    1et
        34
    1et  
       2020-04-17 16:03:14 +08:00
    string 和 int 确实有点迷,传 like 、=没什么问题,比如 name:3 默认等值查询,name_like:3 是模糊查询
    javapythongo
        35
    javapythongo  
       2020-04-17 16:10:51 +08:00
    我觉得可以
    buffzty
        36
    buffzty  
       2020-04-17 16:13:50 +08:00
    我就是这样做的.已经用了一年多了.如果项目很小,开发速度第一可以试试.缺点就是只能普通查询,如果查询有 join 的需要手动写规则
    下面这个是我查询列表的一个通用方法.新加一个模型只需要设置一下 model 类型 和 filter 映射和 order 映射
    用这套接口速度起飞,但有局限性
    ```php
    // Base controller
    public function lists(): Json
    {
    $json = new JsonRes();
    [$page, $limit, $filter, $order, $extra] = $this->getListParam();
    $scene = $extra['scene'] ?? $this->model::SCENE['default'];
    $where = $this->model::buildWhereMap($filter);
    $fmtOrder = $this->model::buildFmtOrder($order);
    if ( $scene === $this->model::SCENE['admin'] && !$this->isAdminUser() ) {
    return $this->noAuth();
    }
    [$list, $count] = $this->model::getListNotThrowEmptyEx($page, $limit,
    $this->model::NEED_COUNT, $where, [], $order);
    $fmtList = [];
    /** @var \app\model\Base $model */
    foreach ($list as $model) {
    $fmtList[] = $model->getFmtDetail($scene);
    }
    $json->setData([
    'list' => $fmtList,
    ]);
    $json->setPage($page);
    $json->setLimit($limit);
    $json->setCount($count);
    return json($json);
    }

    // Base Model
    /**
    * 根据 filter 构建查询条件.
    *
    * @param array|null $filterList
    * @param array $filterFieldMapDbField
    * @return array
    */
    public static function buildWhereMap($filterList, array $filterFieldMapDbField =
    []): array
    {

    $filterFieldMapDbField = empty($filterFieldMapDbField) ? static::FILTER_FIELD_MAP_DB_FIELD : $filterFieldMapDbField;
    $map = [];
    if ( !is_array($filterList) ) {
    return [];
    }
    foreach ($filterList as $filterField => $item) {
    if ( $item instanceof Closure ) {
    $map[] = $item;
    continue;
    }
    if ( !is_array($item) || !array_key_exists('condition', $item)
    || !array_key_exists('val', $item) ) {
    continue;
    }
    if ( $item['val'] === '' || $item['condition'] === Condition::TYPE['undefined'] ||
    !in_array($item['condition'], Condition::TYPE, true)
    ) {
    continue;
    }
    if ( !array_key_exists($filterField, $filterFieldMapDbField) ) {
    continue;
    }
    $dbField = $filterFieldMapDbField[$filterField];
    $condition = Condition::DB_CONDITION[$item['condition']];
    $map[] = [
    $dbField, $condition, Condition::formatVal($item['condition'], $item['val']),
    ];
    }

    return $map;
    }
    ```
    前端代码:
    ```typescript
    // 一个模型只需要定义下面这些 就可以使用通用增删改查 5 个接口了
    type Model = Expert
    const route = '/expert'
    type FilterKeys =
    | 'id'
    | 'ctime'
    | 'expertCode'
    | 'name'
    | 'userName'
    | 'departmentId'
    | 'orgCode'
    | 'orgName'
    | 'provCode'
    | 'cityCode'
    | 'countyCode'
    | 'provName'
    | 'cityName'
    | 'countyName'
    | 'level'
    | 'intro'
    | 'type'
    | 'mobile'
    | 'phone'
    | 'email'
    | 'qq'
    | 'wx'
    | 'remark'
    | 'serviceType'
    type OrderKeys = 'id' | 'ctime'
    type Scene = 'admin' | 'default'
    interface QueryExtra extends BaseExtra<Scene> {}
    type ActGetListParam = GetListParam<FilterKeys, OrderKeys, QueryExtra>
    type AddData = Omit<Model, 'adeptLabelList' | 'department' | 'id'>
    type EditData = AddData & Pick<Model, 'id'>

    export async function lists<T = Model>(data: ActGetListParam) {
    return bpost<{ list: T[] }>({
    url: route + '/list',
    data,
    })
    }
    ```
    buffzty
        37
    buffzty  
       2020-04-17 16:14:56 +08:00
    谁能告诉我这破站到底怎么发代码?
    baiyi
        38
    baiyi  
       2020-04-17 16:19:57 +08:00
    GraphQL +10086
    6IbA2bj5ip3tK49j
        39
    6IbA2bj5ip3tK49j  
       2020-04-17 16:40:17 +08:00 via iPhone
    这不就是那个男人经常推广的场景吗?
    masker
        40
    masker  
       2020-04-17 17:00:09 +08:00 via Android
    @pap3r 你这种优越🐶,真把自己当回事
    vitoliu
        41
    vitoliu  
       2020-04-17 17:59:53 +08:00
    防 sql 注入的工具很多,后端随便引一下就解决了,而且的确能摆脱大量的判空和 if else 。
    参数混淆啥的也都没做,而且看参数名应该是 B 端系统吧,用户量少这么干的确也没啥问题。
    不过前端传驼峰不传下划线的确让我感觉挺不得劲的哈哈哈。
    areless
        42
    areless  
       2020-04-17 18:06:44 +08:00 via Android
    可行,写了一套这样的前后端,本来想用。感觉那就每天没事干,盯着前端写 SQL 就可以了
    IGJacklove
        43
    IGJacklove  
       2020-04-17 18:09:47 +08:00
    @ZSeptember 感觉一个接口和这个也没啥关系吧,换个方式,加个枚举什么的都比前端传 like 强吧
    Deffi
        44
    Deffi  
       2020-04-17 18:18:31 +08:00   ❤️ 1
    我是移动端,要是考虑版本迭代,逻辑变更,我不会用这种接口。前端应该尽量少做逻辑处理
    red2dog
        45
    red2dog  
       2020-04-17 18:19:44 +08:00
    graphql ++
    guyeu
        46
    guyeu  
       2020-04-17 18:51:11 +08:00
    在有很多复杂查询的场景使用自己的 DSL 代替若干个接口是有意义的,但需要注意的是不应该让前端拼 SQL,而且需要对 DSL 的应用范围进行限制,最好是从设计上避免危险操作的可能性。
    guyeu
        47
    guyeu  
       2020-04-17 18:52:53 +08:00
    不拼 SQL 的主要原因是让数据库和客户端解耦,因为在不通的架构模式下 SQL 的写法可能有较大差异,这不应该影响客户端实现,而表结构也不应该对客户端透明。
    k9990009
        48
    k9990009  
       2020-04-17 18:59:05 +08:00 via Android
    可以的,但是不要前端拼 SQL 。以前我们前端人手不够,前端也是后端写的。自定义参数解析。然后后端调用根据关键字,这个关键字正好是 mybatis plus 的方法名,通过反射调用自动拼。后端只要继承默认实现 CRUD 就好了。比如前端传 name_leftlike,只要写前端就好了,超爽。后端做参数过滤就好了。
    oatw
        49
    oatw  
       2020-04-17 19:01:40 +08:00 via iPhone
    @zycojamie 不考虑后端的前端和不考虑前端的后端,都是业余选手。就像这个架构师。
    randyo
        50
    randyo  
       2020-04-17 19:05:34 +08:00 via Android
    流量不要钱吗,前端为了摆脱复杂数据果断抛回去让后端自己写
    rioshikelong121
        51
    rioshikelong121  
       2020-04-17 19:08:09 +08:00
    这样前端也得懂表结构了 而且复杂的 sql 怎么处理。还得单独写接口吧。
    kohos
        52
    kohos  
       2020-04-17 19:18:51 +08:00 via Android
    这种如果是 where 后面全部靠前端提供的参数生成的话,很不安全,例如楼主的接口,改一下 roleId 就能操作其他角色的数据了,后端想偷懒也不是这样做的
    component
        53
    component  
       2020-04-17 19:47:46 +08:00
    GraphQL 了解一下
    jugelizi
        54
    jugelizi  
       2020-04-17 19:50:53 +08:00 via iPhone
    可行
    但不是拼接。
    应该是传 json k v 指定查询类型 以及查询内容
    MilletChili
        55
    MilletChili  
       2020-04-17 19:51:20 +08:00
    一般的查询,我都是叫前端去拼的,虽然我就是那个前端
    start_num = int(request.GET.get('start_num') or 0) # 起始数
    get_num = int(request.GET.get('get_num') or 10) # 需求数
    filters = request.GET.get('filters') # 筛选条件
    sorts = request.GET.get('sorts') # 排序条件
    want_fileds = request.GET.get('want_fileds') # 指定需要的字段
    is_distinct = request.GET.get('is_distinct') # 是否去重

    qset = model.objects.using(using_database).all()

    if filters:
    # print('筛选条件:',filters)
    qset = filterHelper(filters, qset)
    if sorts:
    # print('排序条件:',sorts)
    qset = qset.order_by(*sorts.split(','))
    if is_distinct == 'yes':
    qset = qset.distinct()

    all_num = qset.count() # 总个数
    qset = qset[start_num: start_num + get_num]

    if want_fileds:
    data_list = createSerializers(model, want_fileds.split(','))(qset, many=True).data
    else:
    data_list = Ser_L(qset, many=True).data
    data = {'all_num': all_num, 'data_list': data_list}
    CEBBCAT
        56
    CEBBCAT  
       2020-04-17 19:56:17 +08:00
    wushigejiajia01
        57
    wushigejiajia01  
       2020-04-17 20:19:01 +08:00 via Android
    楼主大致意思: 前端传参,要把参数类型,以及查询范围都传给后端,这样后端能把接口做的很灵活多用

    应该是这个意思吧?

    只要不是由前端拼接 SQL,这没啥问题啊
    shadeofgod
        58
    shadeofgod  
       2020-04-17 21:30:25 +08:00
    这届后端还是不行 🐶
    jswh
        59
    jswh  
       2020-04-17 21:44:49 +08:00
    管理后台可以,产品中最好不要。这事我也干过,本质上可所谓的 restful 或者 graphql 的想法是一样的,就是定义一套自己的 DSL 免得老是做相同的事情。
    lizhuoli
        60
    lizhuoli  
       2020-04-17 21:53:46 +08:00 via iPhone
    重新自己实现一个垃圾版本 GraphQL ?意义何在
    hooopo
        61
    hooopo  
       2020-04-17 21:56:44 +08:00 via Android
    graphql 了解一下
    puilu
        62
    puilu  
       2020-04-18 00:19:07 +08:00
    这届后端不行
    hq136234303
        63
    hq136234303  
       2020-04-18 00:19:35 +08:00
    @zycojamie 你们架构师真的可爱。如果是前端加密做的话 终有一天解开 sql 注入 会爽的飞起的。。。。
    a1562619919
        64
    a1562619919  
       2020-04-18 02:42:38 +08:00 via Android
    数据量极小的模糊查询前端做,比如手机登录帐号。但数据多到超过几十个了明显前端不该处理,或者涉及业务的数据尽量不让前端处理,前端可以模糊查询一下个人非业务的缓存数据(个人姓名手机号这种)
    icy37785
        65
    icy37785  
       2020-04-18 03:15:25 +08:00
    GraphQL + 10086
    bllue
        66
    bllue  
       2020-04-18 05:43:48 +08:00
    架构师应该拎得清
    只要避免前端拼接后端拿来直接用的情况
    Jrue0011
        67
    Jrue0011  
       2020-04-18 06:56:02 +08:00
    @zycojamie 这种设计感觉和 DSL 有点像,elastic search 的查询 api 就是类似的设计
    yufeng0681
        68
    yufeng0681  
       2020-04-18 08:11:26 +08:00
    我能想到 前端能拒绝的理由:
    如果有三个终端 需要使用这个功能。 三个终端的开发都要懂 SQL,都要完整测试,不合理,也浪费成本。
    SyncWorld
        69
    SyncWorld  
       2020-04-18 08:41:53 +08:00
    直接传 SQL 岂不是美包包~
    qce7
        70
    qce7  
       2020-04-18 08:59:21 +08:00
    我们这边用 Yii2 的 DSL


    // 过滤条件
    // 过滤条件按需使用,没有过滤条件就不传 filter
    "filter": {
    // and 表示 "且",也支持 or,但是实际业务用的比较少而且性能不佳 or 不建议使用
    "and": [
    // like 条件
    {"mobile": {"like": "1"}},
    // level = 1
    {"level": 1},
    // 范围条件 id in [2, 5, 9]
    {"id": {"in": [2, 5, 9]}},
    // createdAt > 0
    {"createdAt": {"gt":0}},
    // createdAt < 10000000000
    {"createdAt": {"lt":10000000000}}
    ]
    }
    // 每页记录数,后端默认 10 条
    "perPage": 10,
    // 请求页数
    "page": 1,
    // 排序值,优先级从前到后“-”表示倒序
    "sort": "-isDefault,-isCertified,-id"
    }
    SjwNo1
        71
    SjwNo1  
       2020-04-18 09:48:36 +08:00
    graphql 吧,sql 不安全不太好
    michaelcheng
        72
    michaelcheng  
       2020-04-18 09:53:11 +08:00
    复杂的查询传参还是可以的,不一定是要 sql ,语义化的传参,约定参数类型、运算符、值约束等
    a852695
        73
    a852695  
       2020-04-18 09:59:56 +08:00
    如果是直接传 sql 不可,但是玩着玩着就变成了直接传 sql,不信试试看?后端尝到了甜头。
    如果是拼约定字符串可以,玩着玩着,自己都不知道约定了啥。
    onfuns
        74
    onfuns  
       2020-04-18 10:16:11 +08:00
    可行,不过这种方案都是用在内网的系统,可以减少很多的工作量。外网的应用还是应该规规矩矩的,现在的安全可是很敏感了。
    royzxq
        75
    royzxq  
       2020-04-18 12:58:07 +08:00
    这不就是传说中 apiJSON 的场景吗, 让你们后端用啊(
    pap3r
        76
    pap3r  
       2020-04-20 13:12:53 +08:00
    @masker 后端儿子连数据类型都要前端传 怎么优越了? 你就是后端儿子吧
    masker
        77
    masker  
       2020-04-20 13:35:07 +08:00 via Android
    @pap3r 说实话挺可怜你们这种伪物理阉割的太监的呀。。。自己生育功能不行,还喜欢到处叫别人儿子。。
    pap3r
        78
    pap3r  
       2020-05-16 00:16:08 +08:00
    @masker 说实话没父母的孤儿 有人叫你声儿子 你就认了吧
    pap3r
        79
    pap3r  
       2020-05-16 00:18:42 +08:00
    @masker 自动抬杠怪物 人身攻击 好恶心 生活肯定过得很凄惨
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2883 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 12:40 · PVG 20:40 · LAX 04:40 · JFK 07:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.