spring integration之http-rest例子解析

來源:互聯網
上載者:User

spring integration例子代碼,以及如何匯入eclipse並使用maven構建等請參看上一篇:

Spring
Integration執行個體程式碼分析之basic--http

先看一下http-rest例子的readme

This sample demonstrates how you can send an HTTP request to a Spring Integration's HTTP service while utilizing Spring Integration's new HTTP Path usage;
This sample also uses Spring Security for HTTP Basic authentication. With HTTP Path facility, the client program can send requests with URL Variables.
It consists of two parts - Client and Server.
The following client program can be used to test the HTTP Path usage.
1. RestHttpClientTest. It uses Spring's RestTemplate to assemble and send HTTP request
Server is Spring Integration's HTTP endpoint configuration.

此樣本示範了如何發送一個HTTP請求到一個Spring整合的HTTP服務,同時利用Spring Integration的新的HTTP Path;
此樣本還使用了Spring HTTP基本驗證安全性。 HTTP Path,用戶端程式可以發送請求的URL變數。
它由兩部分組成 - 用戶端和伺服器。
用戶端程式,可以用來測試使用HTTP path。RestHttpClientTest。它使用Spring的RestTemplate的組裝和發送HTTP請求
服務端是Spring整合的HTTP端點配置。

代碼結構仍然先看web.xml先是配置幾個變數,指定spring配置路徑和log4j的配置路徑
        <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>             <!-- Spring application context declaration -->              /WEB-INF/config/web-application-config.xml                      </param-value>    </context-param>        <!--Key of the system property that should specify the root directory of thisweb app. Applied by WebAppRootListener or Log4jConfigListener.--><context-param><param-name>webAppRootKey</param-name><param-value>rest-http.root</param-value></context-param>      <!--Location of the Log4J config file, for initialization and refresh checks.Applied by Log4jConfigListener.--><context-param><param-name>log4jConfigLocation</param-name><param-value>/WEB-INF/classes/log4j.properties</param-value></context-param>

然後配置spring的listener,用於啟動WebApplicationContext

<listener><listener-class>org.springframework.web.util.Log4jConfigListener</listener-class></listener>      <!--- Loads the root application context of this web app at startup,- by default from "/WEB-INF/applicationContext.xml".- Note that you need to fall back to Spring's ContextLoaderServlet for- J2EE servers that do not follow the Servlet 2.4 initialization order.-- Use WebApplicationContextUtils.getWebApplicationContext(servletContext)- to access it anywhere in the web application, outside of the framework.-- The root context is the parent of all servlet-specific contexts.- This means that its beans are automatically available in these child contexts,- both for getBean(name) calls and (external) bean references.-->    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>

接著是filter,包含指定字元的,和security的,用於認證。

<filter>    <filter-name>charEncodingFilter</filter-name>    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>    <init-param>      <param-name>encoding</param-name>      <param-value>UTF-8</param-value>    </init-param>    <init-param>      <param-name>forceEncoding</param-name>      <param-value>true</param-value>    </init-param></filter><filter-mapping>    <filter-name>charEncodingFilter</filter-name>    <url-pattern>/*</url-pattern></filter-mapping><filter>    <filter-name>springSecurityFilterChain</filter-name>    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><filter-mapping>    <filter-name>springSecurityFilterChain</filter-name>    <url-pattern>/*</url-pattern></filter-mapping>

最後才是用於spring mvc的DispatcherServlet,指定設定檔為contextConfigLocation的 /WEB-INF/config/web-application-config.xml

<servlet><servlet-name>Spring Integration Rest HTTP Path Usage</servlet-name>    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <init-param>      <param-name>contextConfigLocation</param-name>      <param-value></param-value>    </init-param>    <load-on-startup>2</load-on-startup></servlet><servlet-mapping>    <servlet-name>Spring Integration Rest HTTP Path Usage</servlet-name>    <url-pattern>/*</url-pattern></servlet-mapping>

Spring 配置先看:web-application-config.xml這個配置裡引入了security-config.xml 和 applicationContext-http-int.xml兩個配置並且使用context:component-scan指定自動掃描載入註解的位置。

<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"       xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context           http://www.springframework.org/schema/context/spring-context-3.0.xsd"><import resource="security-config.xml" /><import resource="classpath:META-INF/spring/integration/applicationContext-http-int.xml"/><context:component-scan base-package="org.springframework.integration.samples.rest"/> </beans>

我們主要看一下applicationContext-http-int.xml先配置一個int:annotation,查到其作用是Enables annotation support for Message Endpoints,也就是開啟訊息端點的註解支援吧。

<int:annotation-config/>

inbound gateway's 和 inbound adapter 都需要指定這個,用於其path屬性。

<bean class="org.springframework.integration.http.inbound.UriPathHandlerMapping"/>

接著指定兩個channel通道,一個用於請求一個用於響應。

<!-- Inbound/Outbound Channels --><int:channel id="employeeSearchRequest" /><int:channel id="employeeSearchResponse" />

下面就是gateway(能翻譯為網關嗎??)了,使用http inbound-gateway來通過http接受資訊,關聯上之前的兩個通道,指定path路徑,通過路徑解析出參數id.

<!-- To receive messages over HTTP, you need to use an HTTP Inbound Channel Adapter or Gateway.  --><int-http:inbound-gateway id="inboundEmployeeSearchRequestGateway"supported-methods="GET, POST" request-channel="employeeSearchRequest"reply-channel="employeeSearchResponse"mapped-response-headers="Return-Status, Return-Status-Msg, HTTP_RESPONSE_HEADERS"view-name="/employee" path="/services/employee/{id}/search"reply-timeout="50000"><int-http:header name="employeeId" expression="#pathVariables.id"/></int-http:inbound-gateway>
配置一個ContentNegotiatingViewResolver,其作用主要是使用內容協商來實現多視圖,可以參考http://www.cnblogs.com/zhaoyang/archive/2012/01/07/2315428.html這個文章,詳細講解。大意就是說使用它來支援返回json xml等多種內容形式。其中用於將對象轉為xml的為marshaller,它的contextPath="org.springframework.integration.samples.rest.domain",就是對這個包下的進行轉換。
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"><property name="order" value="1" /><property name="defaultContentType" value="application/xml"/><property name="favorParameter" value="true"/><property name="ignoreAcceptHeader" value="true" /><property name="mediaTypes"><map><entry key="json" value="application/json" /><entry key="xml" value="application/xml" /></map></property><property name="defaultViews"><list><beanclass="org.springframework.integration.samples.rest.json.view.ExtendedMappingJacksonJsonView" ><property name="objectMapper" ref="jaxbJacksonObjectMapper"/></bean><bean class="org.springframework.web.servlet.view.xml.MarshallingView"><constructor-arg ref="marshaller"/></bean></list></property></bean><!-- 將java bean 轉換為xml string並利用ContentNegotiatingViewResolver來提供輸出As stated in the introduction, a marshaller serializes an object to XML, and an unmarshaller deserializes XML stream to an object. --><oxm:jaxb2-marshaller id="marshaller" contextPath="org.springframework.integration.samples.rest.domain" /><bean id="jaxbJacksonObjectMapper" class="org.springframework.integration.samples.rest.json.JaxbJacksonObjectMapper"/>

用於json的定義一個ExtendedMappingJacksonJsonViewMappingJacksonJsonView輸出會返回{model類名:{內容}} 的json格式, 例如期望的返回是 {success:true,message:”return ok”}; 
但實際返回的卻是 {"jsonResult":{"success":true,"msg":"return ok"}} 
原因是MappingJacksonJsonView中對傳回值的處理未考慮modelMap中只有一個值的情況,直接是按照mapName:{mapResult}的格式來返回資料的。 
修改方法如下代碼,重載MappingJacksonJsonView類並重寫filterModel方法,處理一下map.size為1的情況。代碼如下:

public class ExtendedMappingJacksonJsonView extends MappingJacksonJsonView {@SuppressWarnings({"rawtypes" })@Overrideprotected Object filterModel(Map<String, Object> model){Object result = super.filterModel(model);if (!(result instanceof Map)){return result;}Map map = (Map) result;if (map.size() == 1){return map.values().toArray()[0];}return map;}}

最後定義一個service-activator,關聯上兩個通道,並關聯employeeSearchService的getEmployee方法即可。

<int:service-activator id="employeeServiceActivator" input-channel="employeeSearchRequest"output-channel="employeeSearchResponse" ref="employeeSearchService" method="getEmployee" requires-reply="true"  send-timeout="60000"/>
代碼解析把配置看明白了,代碼就很簡單了,看下JAVA代碼,Employee.java
@XmlAccessorType(XmlAccessType.FIELD)@XmlType(name = "", propOrder = {    "employeeId",    "fname",    "lname"})@XmlRootElement(name = "Customer")public class Employee {private Integer employeeId;private String fname;private String lname;public Employee() {}public Employee(Integer employeeId, String fname, String lname) {this.employeeId = employeeId;this.fname = fname;this.lname = lname;}        //get and set ...}

EmployeeList

@XmlAccessorType(XmlAccessType.FIELD)@XmlType(name = "", propOrder = {    "employee",    "returnStatus",    "returnStatusMsg"})@XmlRootElement(name = "EmployeeList")public class EmployeeList {@XmlElement(name = "Employee", required = true)private List<Employee> employee;@XmlElement(name = "returnStatus", required = true)private String returnStatus;@XmlElement(name = "returnStatusMsg", required = true)private String returnStatusMsg;/** * @return the employee */public List<Employee> getEmployee() {if (employee == null){employee = new ArrayList<Employee>();}return employee;}}

EmployeeSearchService

@Service("employeeSearchService")public class EmployeeSearchService {private static Logger logger = Logger.getLogger(EmployeeSearchService.class);/** * The API <code>getEmployee()</code> looks up the mapped in coming message header's id param * and fills the return object with the appropriate employee details. The return * object is wrapped in Spring Integration Message with response headers filled in. * This example shows the usage of URL path variables and how the service act on * those variables. * @param inMessage * @return an instance of <code>{@link Message}</code> that wraps <code>{@link EmployeeList}</code> */@Secured("ROLE_REST_HTTP_USER")public Message<EmployeeList> getEmployee(Message<?> inMessage){EmployeeList employeeList = new EmployeeList();Map<String, Object> responseHeaderMap = new HashMap<String, Object>();try{MessageHeaders headers = inMessage.getHeaders();String id = (String)headers.get("employeeId");boolean isFound;if (id.equals("1")){employeeList.getEmployee().add(new Employee(1, "John", "Doe"));isFound = true;}else if (id.equals("2")){employeeList.getEmployee().add(new Employee(2, "Jane", "Doe"));isFound = true;}else if (id.equals("0")){employeeList.getEmployee().add(new Employee(1, "John", "Doe"));employeeList.getEmployee().add(new Employee(2, "Jane", "Doe"));isFound = true;}else{isFound = false;}if (isFound){setReturnStatusAndMessage("0", "Success", employeeList, responseHeaderMap);}else{setReturnStatusAndMessage("2", "Employee Not Found", employeeList, responseHeaderMap);}}catch (Throwable e){setReturnStatusAndMessage("1", "System Error", employeeList, responseHeaderMap);logger.error("System error occured :"+e);}Message<EmployeeList> message = new GenericMessage<EmployeeList>(employeeList, responseHeaderMap);return message;}/** * The API <code>setReturnStatusAndMessage()</code> sets the return status and return message * in the return message payload and its header. * @param status * @param message * @param employeeList * @param responseHeaderMap */private void setReturnStatusAndMessage(String status, String message, EmployeeList employeeList, Map<String, Object> responseHeaderMap){employeeList.setReturnStatus(status);employeeList.setReturnStatusMsg(message);responseHeaderMap.put("Return-Status", status);responseHeaderMap.put("Return-Status-Msg", message);}}

這些代碼就都很簡單,一目瞭然,不用怎麼解釋了。

其中幾個組件關係如所示:運行run as --> run on server -->選擇tomcat運行用junit運行RestHttpClientTest,就可以得到資料。
final String fullUrl = "http://localhost:8080/rest-http/services/employee/{id}/search";EmployeeList employeeList = restTemplate.execute(fullUrl, HttpMethod.GET,new RequestCallback() {@Overridepublic void doWithRequest(ClientHttpRequest request) throws IOException {HttpHeaders headers = getHttpHeadersWithUserCredentials(request);headers.add("Accept", "application/xml");}}, responseExtractor, employeeSearchMap);
@Testpublic void testGetEmployeeAsJson() throws Exception{Map<String, Object> employeeSearchMap = getEmployeeSearchMap("0");final String fullUrl = "http://localhost:8080/rest-http/services/employee/{id}/search?format=json";HttpHeaders headers = getHttpHeadersWithUserCredentials(new HttpHeaders());headers.add("Accept", "application/json");HttpEntity<Object> request = new HttpEntity<Object>(headers);ResponseEntity<?> httpResponse = restTemplate.exchange(fullUrl, HttpMethod.GET, request, EmployeeList.class, employeeSearchMap);logger.info("Return Status :"+httpResponse.getHeaders().get("X-Return-Status"));logger.info("Return Status Message :"+httpResponse.getHeaders().get("X-Return-Status-Msg"));assertTrue(httpResponse.getStatusCode().equals(HttpStatus.OK));jaxbJacksonObjectMapper.writeValue(System.out, httpResponse.getBody());}

可以擷取到xml內容,也可以得到json內容,上面為測試類別的主要代碼。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.