【问题标题】:How to handle optional query parameters in Play framework如何处理 Play 框架中的可选查询参数
【发布时间】:2012-03-28 06:25:39
【问题描述】:

假设我在 Scala 中有一个基于 Play 2.0 框架的应用程序,它提供如下 URL:

http://localhost:9000/birthdays

返回所有已知生日的列表

我现在想通过添加使用可选的“from”(日期)和“to”请求参数来限制结果的能力来增强这一点,例如

http://localhost:9000/birthdays?from=20120131&to=20120229

(此处的日期解释为 yyyyMMdd)

我的问题是如何使用 Scala 在 Play 2.0 中处理请求参数绑定和解释,特别是考虑到这两个参数都应该是可选的。

这些参数是否应该在“路由”规范中以某种方式表达?或者,响应控制器方法是否应该以某种方式从请求对象中挑选参数?有没有其他方法可以做到这一点?

【问题讨论】:

    标签: scala playframework playframework-2.0


    【解决方案1】:

    将您的可选参数编​​码为Option[String](或Option[java.util.Date],但您必须实现自己的QueryStringBindable[Date]):

    def birthdays(from: Option[String], to: Option[String]) = Action {
      // …
    }
    

    并声明如下路线:

    GET   /birthday       controllers.Application.birthday(from: Option[String], to: Option[String])
    

    【讨论】:

    • 对我来说,这不足以在没有参数的情况下进行路由。我把它扩展到GET /birthday controllers.Application.birthday(from: Option[String] ?= None, to: Option[String] ?= None)
    • 更成功:GET /birthday controllers.Application.birthday(Option[from], Option[to])
    • 这里描述了可选参数 (v2.7+) => playframework.com/documentation/2.7.x/…
    【解决方案2】:

    对 java 用户来说,一种可能不太干净的方法是设置默认值:

    GET  /users  controllers.Application.users(max:java.lang.Integer ?= 50, page:java.lang.Integer ?= 0)
    

    在控制器中

    public static Result users(Integer max, Integer page) {...}
    

    还有一个问题,每当您链接到模板中的页面时,您都必须重复默认设置

    @routes.Application.users(max = 50, page = 0)
    

    【讨论】:

    • 有人可以确认 (max : java.lang.Integer = null) 有效吗? 'max' 对我来说总是为空......
    • 我修正了答案,应该是 ?=
    • 在Java中你可以使用play.libs.F.Option而不是Scala Option
    • 你有使用 Scala 或 F 选项的整个例子吗?困扰我的是模板中默认设置的重复。我总是收到将 _ 用于部分应用函数的建议
    【解决方案3】:

    除了朱利安的回答。如果您不想将其包含在 routes 文件中。

    你可以在控制器方法中使用RequestHeader获取这个属性

    String from = request().getQueryString("from");
    String to = request().getQueryString("to");
    

    这将为您提供所需的请求参数,并保持您的 routes 文件干净。

    【讨论】:

    • 您的路由器文件看起来如何?我有类似GET /url @com.mycompany.controllers.MyClass.fetchget() 的东西,但它找不到路线
    【解决方案4】:

    这是 Julien 使用 F.Option 用 java 重写的示例:(适用于 play 2.1)

    import play.libs.F.Option;
    public static Result birthdays(Option<String> from, Option<String> to) {
      // …
    }
    

    路线:

    GET   /birthday       controllers.Application.birthday(from: play.libs.F.Option[String], to: play.libs.F.Option[String])
    

    您也可以选择任意查询参数作为字符串(您必须自己进行类型转换):

    public static Result birthdays(Option<String> from, Option<String> to) {
      String blarg = request().getQueryString("blarg"); // null if not in URL
      // …
    }
    

    【讨论】:

      【解决方案5】:

      对于可选的查询参数,你可以这样做

      在路由文件中,声明API

      GET   /birthdays     controllers.Application.method(from: Long, to: Long)
      

      你也可以给一些默认值,如果 API 不包含这些查询参数,它会自动为这些参数分配默认值

      GET   /birthdays    controllers.Application.method(from: Long ?= 0, to: Long ?= 10)
      

      在控制器应用程序中编写的方法中,如果没有分配默认值,则这些参数将具有值null,否则为默认值。

      【讨论】:

      • 感谢您的回答,现在它以带有默认值的字段的形式出现。就我而言,我有一个强制参数和一个可选参数。
      【解决方案6】:

      我的做法是使用自定义QueryStringBindable。这样,我将路由中的参数表示为:

      GET /birthdays/ controllers.Birthdays.getBirthdays(period: util.Period)
      

      Period 的代码如下所示。

      public class Period implements QueryStringBindable<Period> {
      
        public static final String PATTERN = "dd.MM.yyyy";
        public Date start;
      
        public Date end;
      
        @Override
        public F.Option<Period> bind(String key, Map<String, String[]> data) {
            SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
      
            try {
                start = data.containsKey("startDate")?sdf.parse(data.get("startDate")  [0]):null;
                end = data.containsKey("endDate")?sdf.parse(data.get("endDate")[0]):null;
            } catch (ParseException ignored) {
                return F.Option.None();
            }
            return F.Option.Some(this);
        }
      
        @Override
        public String unbind(String key) {
            SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
            return "startDate=" + sdf.format(start) + "&amp;" + "endDate=" + sdf.format(end);
        }
      
        @Override
        public String javascriptUnbind() {
            return null;
        }
      
        public void applyDateFilter(ExpressionList el) {
            if (this.start != null)
                el.ge("eventDate", this.start);
            if (this.end != null)
                el.le("eventDate", new DateTime(this.end.getTime()).plusDays(1).toDate());
        }
      
      }
      

      applyDateFilter 只是我在控制器中使用的一种方便方法,如果我想对查询应用日期过滤。显然,您可以在此处使用其他日期默认值,或者在 bind 方法中使用其他默认值而不是 null 作为开始和结束日期。

      【讨论】:

      • 知道如何让 swagger 使用这个解决方案吗?
      猜你喜欢
      • 2011-04-08
      • 1970-01-01
      • 1970-01-01
      • 2017-01-12
      • 2013-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-21
      相关资源
      最近更新 更多