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

时间:2016-04-15 14:35:19

标签: spring

我知道你可以将get请求参数绑定到pojo,如:

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

    return service.loadReservations(criteria);
}

使用类似的pojo:

public class ReservationCriteria {
    String hotelName;

    DateRange reservationDateRange;
    //getters-setters omitted
}

请求:/ reservation?hotelName = myHotel

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

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

public class DateRange {
    Date from;
    Date to;

    //getters-setters omitted
}

是否存在允许这种绑定的URL模式:

/reservation?hotelName=myHotel&reservationDateRange={fromDate=14.04.2016,toDate=15.04.2016}

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

@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);
}

我不想修改ReservationCriteria类,因为它在许多其他项目中使用,这会导致很多重构。

2 个答案:

答案 0 :(得分:4)

When you pass a POJO as container of data, Spring use the name of the properties for build the query string and with the data that you pass build the pojo through an appropriated converter. This works for planar pojo or in other words without nesting, for this purpose you have provide the your converter. for this reason you cold have a think like below:

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);
}

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

in this way you can benefit of standard converter of SpringMVC.

the your attempt to use a sort of json for codificate the inner object didn't work because Spring by default in query string don't understand this presentation you have provide a converter for this purpose.

Update for answer to Ben's comment:

If you want implement a custom Converter you had implements the org.springframework.core.convert.converter.Converter<S, T> and then register the your new Converter on the Spring Conversion Service.

On xml configuration you can use FormattingConversionServiceFactoryBean and register it on mvc namespace like below:

<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>

on java config you can extends WebMvcConfigurerAdapter and add you bena like below:

@Configuration
@EnableWebMvc
public class YourWebConfigurationClass extends WebMvcConfigurerAdapter{

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

   ...

}

the your converter can be like below:

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;
    }

}

in this way you can listgening on the url: http://localhost:8080/reservation?hotelName=myHotel&reservationDateRange={"fromDate":"14.04.2016","toDate":"15.04.2016"}

pleas pay attenction on reservation DateRange field because I encoded it like a json.

I hope that it can help you

答案 1 :(得分:1)

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

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

?reservationDateRange.from=2019-04-01&reservationDateRange.to=2019-04-03

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