Spring REST-将GET参数绑定到嵌套对象

uyl*_*lmz 4 spring

我知道您可以将get请求参数绑定到像这样的pojo:

@RequestMapping(value = "/reservation",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)

    return service.loadReservations(criteria);
}
Run Code Online (Sandbox Code Playgroud)

使用像这样的pojo:

public class ReservationCriteria {
    String hotelName;

    DateRange reservationDateRange;
    //getters-setters omitted
}
Run Code Online (Sandbox Code Playgroud)

有要求:/ reservation?hotelName = myHotel

myHotel将绑定到ReservationCriteria对象中的hotelName。

但是,如何将参数绑定到嵌套对象DateRange?定义如下:

public class DateRange {
    Date from;
    Date to;

    //getters-setters omitted
}
Run Code Online (Sandbox Code Playgroud)

有没有一种URL模式,它允许这种绑定,例如:

/reservation?hotelName=myHotel&reservationDateRange={fromDate=14.04.2016,toDate=15.04.2016}
Run Code Online (Sandbox Code Playgroud)

还是我必须声明单独的请求参数并手动绑定它们?

@RequestMapping(value = "/reservation",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(
    ReservationCriteria criteria,
    @RequestParam Date from,
    @RequestParam Date to)

    DateRange range = new DateRange();
    range.setFrom(from);
    range.setTo(to);

    criteria.setDateRange(range);

    return service.loadReservations(criteria);
}
Run Code Online (Sandbox Code Playgroud)

我宁愿不修改ReservationCriteria类,因为它在许多其他项目中使用,这将导致进行大量重构。

Gam*_*tes 18

至少从 Spring 4 开始,您可以传入以“.”分隔的嵌套对象。在网址中。

在 OP 情况下,它将用于查询参数:

?reservationDateRange.from=2019-04-01&reservationDateRange.to=2019-04-03
Run Code Online (Sandbox Code Playgroud)

这假设可以从给定的字符串解析 Date。这可能不适用于任意级别的嵌套,但我已经测试过它可以与一个额外的嵌套对象一起使用。


Val*_*udi 6

当您将POJO作为数据容器传递时,Spring使用属性名称来构建查询字符串,并与通过适当的转换器传递来构建pojo的数据一起使用。这适用于平面pojo或换句话说没有嵌套,为此,您提供了转换器。因此,您会产生如下感想:

public class ReservationCriteria {
    String hotelName;

  Date from;
    Date to;
    //getters-setters omitted
}

@RequestMapping(value = "/reservation",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)

    return service.loadReservations(criteria);
}
Run Code Online (Sandbox Code Playgroud)

/ reservation?hotelName = value&from = val&to = val

这样,您可以受益于SpringMVC的标准转换器。

您尝试使用某种json来编排内部对象的尝试无效,因为默认情况下,Spring在查询字符串中无法理解此演示文稿,为此您提供了一个转换器。

更新本答案的答案:

如果要实现自定义Converter,则需要实现org.springframework.core.convert.converter.Converter<S, T>,然后在Spring Conversion Service上注册新的Converter。

在xml配置上,您可以使用FormattingConversionServiceFactoryBean并将其注册在mvc命名空间上,如下所示:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <mvc:annotation-driven  conversion-service="conversionService"/>

    <context:component-scan base-package="com.springapp.mvc"/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>


    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <util:list>
                <bean class="com.springapp.mvc.DateRangeToStringConverter"/>
                <bean class="com.springapp.mvc.StringToDateRangeConverter"/>
            </util:list>
        </property>
    </bean>
</beans>
Run Code Online (Sandbox Code Playgroud)

在Java配置上,您可以扩展WebMvcConfigurerAdapter并添加如下所示的bena:

@Configuration
@EnableWebMvc
public class YourWebConfigurationClass extends WebMvcConfigurerAdapter{

    @Override
    public void addFormatters(FormatterRegistry formatterRegistry) {
        formatterRegistry.addConverter(yourConverter());
    }

   ...

}
Run Code Online (Sandbox Code Playgroud)

您的转换器可以如下所示:

public class DateRangeToStringConverter implements Converter<DateRange,String> {

    @Override
    public String convert(DateRange dateRange) {
        return Json.createObjectBuilder().add("fromDate",DateFormatData.DATE_FORMAT.format(dateRange.getFrom()))
                .add("toDate", DateFormatData.DATE_FORMAT.format(dateRange.getTo()))
                .build()
                .toString();
    }

}



public class StringToDateRangeConverter implements Converter<String,DateRange> {


    @Override
    public DateRange convert(String dateRange) {
        DateRange range = new DateRange();
        JsonObject jsonObject = Json.createReader(new StringReader(dateRange)).readObject();

        try {
            range.setFrom(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("fromDate")));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        try {
            range.setTo(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("toDate")));
        } catch (ParseException e) {
            e.printStackTrace();
        }

        System.out.println(range);
        return range;
    }

}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以在URL上生成列表: http://localhost:8080/reservation?hotelName=myHotel&reservationDateRange={"fromDate":"14.04.2016","toDate":"15.04.2016"}

请注意保留DateRange字段,因为我将其编码为json。

希望对您有所帮助