3

比方说,我有一个 REST 风格的控制器映射

@RequestMapping(value="users", produces = {MediaType.APPLICATION_JSON_VALUE})
public List<User> listUsers(@ReqestParams Integer offset, @ReqestParams Integer limit, @ReqestParams String query) {
    return service.loadUsers(query, offset, limit);
}

服务 JSON(甚至 XML)不是问题,这很容易使用 ContentNegotation 和 MessageConverters

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="true" />
    <property name="favorParameter" value="false" />
    <property name="ignoreAcceptHeader" value="false" />
    <property name="mediaTypes" >
        <value>
            html=text/html
            json=application/json
            xml=application/xml
        </value>
    </property>
</bean>

现在,我需要添加对 PDF 的支持。自然,我想尽可能地使用(Spring)MVC + REST。我发现的大多数示例都使用不使用 REST 样式的显式定义来实现这一点,例如

@RequestMapping(value="users", produces = {"application/pdf"})
public ModelAndView listUsersAsPdf(@ReqestParams Integer offset, @ReqestParams Integer limit, @ReqestParams String query) {
    List<User> users = listUsers(offset, limit, query); // delegated
    return new ModelAndView("pdfView", users);
}

这行得通,但不是很舒服,因为对于每个备用输出(PDF、Excel、...),我都会添加一个请求映射。

我已经添加application/pdf到内容协商解析器中;不幸的是,任何带有后缀.pdf或 Accept-Headerapplication/pdf的请求都会以406.

REST/MVC 样式模式集成 PDF 等替代输出的理想设置是什么?

4

3 回答 3

0

Knalli,如果您将@ResponseBody 替换为 ModelAndView(),您可以同时实现这两个功能。您有什么理由要保留 @ResponseBody 吗?我只是想知道我是否缺少任何东西,只是想学习。

其他选择是编写 HttpMessageConverters 然后:一些示例在这里。

使用 @ResponseBody 自定义 HttpMessageConverter 来做 Json 事情

http://www.javacodegeeks.com/2013/07/spring-mvc-requestbody-and-responsebody-demystified.html

于 2014-03-06T20:08:45.323 回答
0

这是工作样本。我为此配置了 contentnegotiationviewresolver,并给出了最高的顺序。之后,我有用于 JSTL 和 Tiles View 的 ResourceBundleViewResolver,然后是用于 excelResolver、pdfResolver、rtfResolver 的 XmlViewResolver。excelResolver、pdfResolver、rtfResolver。XmlViewResolver 和 ResourceBundleViewResolver 仅适用于 MAV,但 MappingJacksonJsonView 和 MarshallingView 负责 MAV 和 @ResponseBody 返回值。

<bean id="contentNegotiatingResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order"
                  value="#{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE}" />
<property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
                <entry key="xml" value="application/xml" />
                <entry key="pdf" value="application/pdf" />
                <entry key="xlsx" value="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
                <entry key="doc" value="application/msword" />
            </map>
        </property>

        <property name="defaultViews">
            <list>
                <!-- JSON View -->
                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />

                <!-- XML View -->
                <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
                        <constructor-arg>
                            <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
                            <property name="classesToBeBound">
                            <list>
                                <value>Employee</value>
                                <value>EmployeeList</value>
                            </list>
                            </property>
                            </bean>
                        </constructor-arg>
            </bean>
            </list>
        </property>
        <property name="ignoreAcceptHeader" value="true" />
 </bean>
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver"
        id="resourceBundleResolver">
        <property name="order" value="#{contentNegotiatingResolver.order+1}" />
    </bean>
<bean id="excelResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
       <property name="location">
           <value>/WEB-INF/tiles/spring-excel-views.xml</value>
       </property>
       <property name="order" value="#{resourceBundleResolver.order+1}" />
    </bean>

    <bean id="pdfResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
       <property name="location">
           <value>/WEB-INF/tiles/spring-pdf-views.xml</value>
       </property>
       <property name="order" value="#{excelResolver.order+1}" />
    </bean>

    <bean id="rtfResolver" class="org.springframework.web.servlet.view.XmlViewResolver">
       <property name="location">
           <value>/WEB-INF/tiles/spring-rtf-views.xml</value>
       </property>
       <property name="order" value="#{excelResolver.order+1}" />
    </bean>

我们的 XMLViewResolver spring-pdf-views.xml 看起来像这样。

<bean id="employees"
        class="EmployeePDFView"/>

EmployeePDFView 将具有生成 pdf 和在 Response 对象上写入 pdf 字节流的代码。这将解析为以 .pdf 扩展名结尾的 rest url,以及当您返回带有“employees”id 的 MAV 时。

于 2014-03-07T15:01:14.767 回答
0

您可以创建一个 WEB-INF/spring/pdf-beans.xml,如下所示。

 <bean id="listofusers" class="YourPDFBasedView"/>

您的控制器方法将返回视图名称作为用户列表。

@RequestMapping(value="users")
public ModelAndView listUsersAsPdf(@ReqestParams Integer offset, @ReqestParams Integer limit, @ReqestParams String query) {
    List<User> users = listUsers(offset, limit, query); // delegated
    return new ModelAndView("listofusers", users);
}

您可以通过这种方式使用 contentNegotiationViewResolver :

 <bean class="org.springframework.web.servlet.view.XmlViewResolver">
            <property name="order" value="1"/>
            <property name="location" value="WEB-INF/spring/pdf-views.xml"/>
        </bean>

<!--
        View resolver that delegates to other view resolvers based on the content type
    -->
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
       <!-- All configuration is now done by the manager - since Spring V3.2 -->
       <property name="contentNegotiationManager" ref="cnManager"/>
    </bean>

    <!--
        Setup a simple strategy:
           1. Only path extension is taken into account, Accept headers are ignored.
           2. Return HTML by default when not sure.
     -->
    <bean id="cnManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="ignoreAcceptHeader" value="true"/>        
        <property name="defaultContentType" value="text/html" />
    </bean>

对于 JSON:创建如下所示的通用 JSON 视图解析器,并将其注册为上下文文件中的 bean。

public class JsonViewResolver implements ViewResolver {
    /**
      * Get the view to use.
      *
      * @return Always returns an instance of {@link MappingJacksonJsonView}.
     */
    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        MappingJacksonJsonView view = new MappingJacksonJsonView();
        view.setPrettyPrint(true);      // Lay the JSON out to be nicely readable 
        return view;
    }
}

XML 相同:

public class MarshallingXmlViewResolver implements ViewResolver {

    private Marshaller marshaller;

    @Autowired
    public MarshallingXmlViewResolver(Marshaller marshaller) {
        this.marshaller = marshaller;
    }

    /**
     * Get the view to use.
     * 
     * @return Always returns an instance of {@link MappingJacksonJsonView}.
     */
    @Override
    public View resolveViewName(String viewName, Locale locale)
            throws Exception {
        MarshallingView view = new MarshallingView();
        view.setMarshaller(marshaller);
        return view;
    }
}

并在上下文文件中注册上述 xml 视图解析器,如下所示:

<oxm:jaxb2-marshaller id="marshaller" >
        <oxm:class-to-be-bound name="some.package.Account"/>
        <oxm:class-to-be-bound name="some.package.Customer"/>
        <oxm:class-to-be-bound name="some.package.Transaction"/>
    </oxm:jaxb2-marshaller>

    <!-- View resolver that returns an XML Marshalling view. -->
    <bean class="some.package.MarshallingXmlViewResolver" >
        <constructor-arg ref="marshaller"/>
    </bean>

您可以在此链接中找到更多信息:http: //spring.io/blog/2013/06/03/content-negotiation-using-views/

使用所有视图解析器技术,您可以避免在控制器中编写重复的方法,例如一个用于 xml/json,另一个用于 excel,另一个用于 pdf,另一个用于 doc、rss 等等。

于 2014-03-06T14:56:49.380 回答