Google Code Prettify

2016年5月2日 星期一

spring MVC + jQuery ajax

現在的 web 系統前端程式的比重越來越重,以 ajax 方式與後端溝通成為常態,這篇要整理的是,當後端為 spring MVC 架構,前端使用 jQuery ajax 的情況下,兩者如何互動? 這裡的程式放在 Bitbucket,如有需要請自行下載。程式很簡單,如下,在輸入股票代號後,顯示出股票名稱及價格。
  1. 輸入前
  2. 輸入後 (不需要 submit)
  • jar 檔
因為這個程式在 MVC 傳回給前端時,是採用 JSON 格式,所以,請在 pom.xml 中增加如下設定。
<dependency>  
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${jackson.json.version}</version>
</dependency>
          
<dependency>  
    <groupId>com.fasterxml.jackson.dataformat</groupId>  
    <artifactId>jackson-dataformat-xml</artifactId>  
    <version>${jackson.json.version}</version>  
</dependency> 
  • 資料來源
因為這裡要讓程式儘量簡單,不從資料庫取得股票相關資料,而是設定在 spring 的 bean configure file 裡,如下,只有兩檔股票。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="stock2330" class="idv.steven.mvc.form.StockForm"
        p:code="2330"
        p:name="台積電"
        p:price="153.0" 
    />
        
    <bean id="stock2490" class="idv.steven.mvc.form.StockForm"
        p:code="2490"
        p:name="友達"
        p:price="9.4"
    />

    <bean id="allStocks" class="idv.steven.mvc.form.Stocks">
        <property name="stocks">
            <map>
                <entry key="2330">
                    <ref bean="stock2330"/>
                </entry>
                
                <entry key="2490">
                    <ref bean="stock2490"/>
                </entry>                
            </map>
        </property>
    </bean>
</beans>

上面設定檔中有兩個類別 StockForm 及 Stocks,分別如下:
public class StockForm {
    private String code;
    private String name;
    private Double price;
    
    public StockForm() {}
    
    public StockForm(String code, String name, Double price) {
        this.code = code;
        this.name = name;
        this.price= price;
    }
    
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
}
public class Stocks {
    private Map<String, StockForm> stocks;

    public Map<String, StockForm> getStocks() {
        return stocks;
    }

    public void setStocks(Map<String, StockForm> stocks) {
        this.stocks = stocks;
    }
}
  • ajax 網頁
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.12.3.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>查詢股價</title>

<script type="text/javascript">
var message;

$(function() {
    $("#code").bind("keyup", function(e) {
        if ($("#code").val().length != 4) {
            return;
        }

        var myUrl = "<%=request.getContextPath()%>/searchStock.do?code=" + $('#code').val();
        
        $.ajax({
            type: 'get',
            url: myUrl,
            dataType: 'json',
            success: function(data, result) {
                if (data.responseMsg.status == "success") {
                    message = $.parseJSON(data.jsondata);
                    $('#name').html('股票名稱: ' + message.name);
                    $('#price').html('股票價格: ' + message.price);
                }
                else {
                    alert('fail');
                    alert(data.responseMsg.message);
                }
            },
            error: function() {
                alert('error');
            }
        });
    });
});
</script>

</head>
<body>

<form>
    股票代號: <input id="code" name="code" type="text"/><br/>
    <div id="name"></div>
    <div id="price"></div>
</form>

</body>
</html> 
  1. bind 事件 (綠色部份): 網頁只有一個可輸入股票代號的欄位,在網頁載入時,透過 jQuery 先 bind 的一個 keyup 的事件給這個輸入欄位,當在這個 input tag 輸入資料時,都會觸發事件。
  2. 檢查條件是否符合需要 (橘色部份): 我們假設股票代號都是四碼 (實務上雖以四碼為主,也有許多不是四碼的股票!),所以當輸入的代號不到四碼之前都直接返回。
  3. 網址與參數 (紫色部份): 傳給後端的參數只有 code,即股票代號,因為採用 get 的方式,直接接在網址上即可。
  4. JSON (黃色部份): 指定傳回的是 json 格式,因為是 json,利用 jQuery 提供的 API (parseJSON) 解析,至於解析後為什麼會有 name、price 兩個屬性? 這是對應上面 StockForm 類別,因為在 controller 中傳回的就是 StockForm 類別轉化成的 JSON 資料。
  • 傳回類別
public class ResponseMessage {
    private String message;
    private String status;
    
    public ResponseMessage(String message, String status) {
        this.message = message;
        this.status = status;
    }
    
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
}
定義這個類別,用來傳回成功或失敗的訊息,及查詢所得結果,這個類別要有那些欄位當然依程式的需要來設計。
  • Controller
@Controller
public class StockController {
    @Autowired
    private Stocks allStocks;
    
    @RequestMapping(value="/searchStock", method=RequestMethod.GET)
    public @ResponseBody Map<String, Object> getStockPrice(String code) {
        Map<String, Object> result = new HashMap<String, Object>();
        
        Map<String, StockForm> stocks = allStocks.getStocks();
        StockForm stock = stocks.get(code);
        
        if (stock != null) {
            result.put("jsondata", toJson(stock));
            result.put("responseMsg", new ResponseMessage("取得股票資料", "success"));
        }
        
        return result;
    }
    
    public String toJson(Object object) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(object);
        }
        catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
  1. 載入資料 (藍色部份): 前面在 beans-config.xml 中有初始化 Stocks 類別的值,在這裡透過 @Autowired 載入。
  2. 網址 (綠色部份): 在上一篇就有提到網址要寫在 @RequestMapping 裡,當只有網址一個屬性時,可以寫成 @RequestMapping("/searchStock"),但是因為在這裡要限定只接收 GET 傳送過來的資料,變成有兩個參數,所以網址要寫成 value="searchStock"。
  3. 傳回 JSON (橘色部份): 當返回的值不是 html 頁面時 (JSON、xml ...),需要加上這個 @ResponseBody。
  4. 接收參數 (紅色部份): controller 一般會定義一個 form class 來接收 jsp 傳過來的資料,但是因為這個程式太簡單了,只會收到一個參數,所以直接用 code 這個參數去接收就可以了。
  5. 傳回值 (紫色部份): 傳回值不一定要用 map,不過這是一個相當通行且簡單的方法,這裡的 map 會放入兩個值 - jsondata 及 responseMsg,responseMsg 只是指出查詢是否成功,真正的傳回值在 jsondata,這部份要轉成 JSON 格式,文章最開頭在 pom.xml 中引入的 jar 檔,就是 JSON 處理的類別庫。

  • see also

        jQuery: 處理 json 資料





【日影 - I am a hero (喪屍末日戰)】
這部電影算是恐怖片,也算英雄片? 但是一想到主角是大泉洋,很難把他和英雄聯想在一起!但不管怎麼樣,他在劇中的名字是「鈴木英雄」,是真正的英雄! 我看過的日劇、日影很多,大泉洋主演的這是第一次! 在電影宣傳裡有村架純飾演的早狩比呂美相較於長澤雅美飾演的更受重視,看之前我還以為是有村架純主演,實際上劇中長澤雅美的戲份不會比較少,也比較有表現的空間,只不過早狩比呂美和鈴木英雄的特殊關係,會讓有村架純就算不出現在螢幕上,也會隱隱感覺到她的存在。
這部電影後來又拍了前傳 - 「請叫我英雄 最初之日」,以長澤雅美飾演的藪為主角,講述疫情爆發第一天的情況,這五集的短片拍攝的手法比較特別,是以類紀錄片的方式拍攝,裡面很多時候還會是單鏡頭拍攝,為什麼要這麼拍呢? 這個嘛~ 得請巷子裡的人出來講講才知道了 ...
長澤雅美人長得漂亮演技又好,看她演戲還蠻享受的。

沒有留言:

張貼留言