1. 程式人生 > >使用ANGULAR2的HTTP傳送AJAX請求

使用ANGULAR2的HTTP傳送AJAX請求

使用Angular2的Http傳送AJAX請求

Angular的文件並不詳細,甚至API文件也有一些錯誤。經過查閱資料並經大量實驗,終於明確了Angular的Http傳送Ajax請求的方式方法。本文描述了通過Angular的Http傳送Ajax請求相關的全部內容。請各位同事仔細閱讀並付諸實踐。

1. Angular的Http類

Angular的Http類負責與服務端通訊,通過Ajax的形式訪問遠端伺服器。

Angular的Http訪問遠端伺服器,可以使用GET、POST、PUT、DELETE、OPTIONS等方法(method)。

當然,我們最常使用的是GET和POST方法。

當Angular發現你所訪問的地址是跨域的,會自動傳送一次OPTIONS方法的請求,確定此跨域伺服器是否支援跨域訪問。若不支援跨域訪問,則給出錯誤,不能執行真正的GET、POST請求。

2. 使用Angular傳送GET請求

// 以下是一些必須的匯入
import { Http, URLSearchParams,RequestOptions } from '@angular/http'; // 傳送請求有關的類
import { Observable } from "rxjs"; // 明確請求後的可觀察物件是Rx的可觀察物件,如未指明是Rx的可觀察物件,會報錯。
import 'rxjs/add/operator/toPromise'; // 引入toPromise操作符,否則會報錯,toPromise方法未定義。
// 傳送get請求的方法
get() : Promise<string> {
    let serverurl: string = "url地址";
    // 建立請求引數物件,所有請求引數被放在此物件中。
    let param = new URLSearchParams();
    param.append("param1","test"); // 向請求引數中放入引數及值
    // 建立請求設定物件,將請求引數作為其構造方法引數物件的"search"屬性值。
    // 請注意,在這裡我們並未明確請求頭(Headers),Angular會根據請求方法(method)
    // 及請求引數的型別,自動確定請求頭型別。
    let options = new RequestOptions({search:param});
    // 發起get請求。注意http屬性必須由依賴注入而來。
    return this.http.get(serverurl, options)
    .toPromise()    // 將可觀察物件轉為承諾,接下來按照承諾的方式處理。
    .then(response => {
        let data = response.json();
        if (data.is_ok == 2) {
            let result:string = data.rs;
            console.log(result);
            return Promise.resolve(result);
        }
        return Promise.reject("false");
    }).catch(error => {
        console.log(error);
        return Promise.reject("false"); 
    });
}

 

3. 使用Angular傳送POST請求

// 以下是一些必須的匯入
import { Http, URLSearchParams,RequestOptions } from '@angular/http'; // 傳送請求有關的類
import { Observable } from "rxjs"; // 明確請求後的可觀察物件是Rx的可觀察物件,如未指明是Rx的可觀察物件,會報錯。
import 'rxjs/add/operator/toPromise'; // 引入toPromise操作符,否則會報錯,toPromise方法未定義。
// 傳送Post請求的方法
postByForm(): Promise<string> {
    let serverurl: string = "url地址";
    // 建立請求引數物件,所有請求引數被放在此物件中。
    let param = new URLSearchParams();
    param.append("param1","test"); // 向請求引數中放入引數及值
    // 發起Post請求(http屬性必須由依賴注入而來),請注意,post方法比get方法多了一個引數,多的是第2個引數。
    // 第1個引數是url地址,第2個引數是請求引數;
    // 第3個引數對應get方法的第2個引數,是請求設定(RequestOptions)物件。
    // 在get請求中,我們將請求引數(URLSearchParams)放在了請求設定的(RequestOptions)物件的"search"屬性中,
    // 在post方法中,post請求引數作為post方法的第2個引數,不能放到請求設定的(RequestOptions)物件的"search"屬性中。
    // 如放到了請求設定的(RequestOptions)物件的"search"屬性中,則被作為請求get引數的一部分。
    // 如:url地址?param1=test,
    // 這不是我們想要的。我們使用了post方法,即是希望在請求體的form-data部分傳輸我們的引數,
    // 而不希望請求引數出現在url地址上。
    // 在這裡,請求設定物件是個null,因為我們不需要給本次請求設定請求頭,請求頭由anglar自動計算。
    return this.http.post(serverurl, param, null)
    .toPromise()
    .then(response => {
        let data = response.json();
        if (data.is_ok == 2) {
            let result:string = data.rs;
            console.log(result);
            return Promise.resolve(result);
        }
         return Promise.reject("false");
    }).catch(error => {
        console.log(error);
        return Promise.reject("false");
    });
}

 

4. 使用Angular以FormData物件作為引數傳送POST請求

FormData是 XMLHttpRequest Level 2 新增的一個物件,在IE10及以上版本受支援,其他瀏覽器的新版本基本都支援。

FormData的結構就是一組鍵值對,鍵名是引數名,鍵值是引數值。它最大的特點是支援二進位制流,所以我們使用FormData主要是用AJAX技術上傳檔案。上傳檔案時,檔案是FormData中的一組鍵值對,除了檔案以外的引數,也可以被放到FormData中一併提交給伺服器。

// 以下是一些必須的匯入
import { Http, URLSearchParams,RequestOptions } from '@angular/http'; // 傳送請求有關的類
import { Observable } from "rxjs"; // 明確請求後的可觀察物件是Rx的可觀察物件,如未指明是Rx的可觀察物件,會報錯。
import 'rxjs/add/operator/toPromise'; // 引入toPromise操作符,否則會報錯,toPromise方法未定義。
// 傳送Post請求的方法
postByFormData(): Promise<string> {
    let serverurl: string = "url地址";
    let param = new FormData();
    // 放入要上傳的檔案
    let file = this.fileElement.nativeElement.files[0];
    param.append("file",file);
    // 放入其他引數
    param.append("param1","test");
    // 傳送post請求(http屬性必須由依賴注入而來),可以看到,除了第二個引數param的型別為FormData外,其餘引數與普通post請求一致。
    // angular會自動計算請求頭型別。
    return this.http.post(serverurl, param, null)
    .toPromise()
    .then(response => {
        let data = response.json();
        if (data.is_ok == 2) {
            let result:string = data.rs;
            console.log(result);
            return Promise.resolve(result);
        }
        return Promise.reject("false");
    }).catch(error => {
        console.log(error);
        return Promise.reject("false");
    });
}

 

5. 伺服器端的處理

5.1 接收引數

後臺程式接收引數的方式方法與傳統接收引數的方式方法完全一致。這裡以PHP和Java程式舉例:

// php
$param1 = $_GET['param1'];
// j2ee servlet
String param1 = request.getParameter("param1");
// java struts
private String param1;
public getParam1(){
    return param1;
}
public setParam1(String param1){
    this.param1 = param1;
}
public String execute(){
    System.out.println(param1);
    return SUCCESS;
}

 

若客戶端傳入了檔案流,則按照傳統方式獲得檔案即可。

5.2 跨域訪問

當Angular發起跨域Ajax請求時,會自動傳送一次OPTIONS方法的請求,確定此跨域伺服器是否支援跨域訪問,服務端程式應對此請求進行特殊處理,使OPTIONS方法的請求不執行真正的業務處理,並告知客戶端允許跨域。

不執行真正的業務處理,是為了防止由於兩次請求而導致的重複業務操作。

告知客戶端允許跨域,是為了通知Angular可以執行接下來真正的Post、Get或其他方法的請求。

以上兩點必須都被正確實現和處理!

這裡以Java Servlet程式為例。

<!-- web.xml,正確的新增過濾器 -->
    <!-- 允許AJAX跨域請求,處理OPTION請求。該過濾器必須在其他過濾器之前定義,也就是說,當請求到達伺服器,首先由此過濾器處理後,如需過濾器鏈中後續的過濾器繼續處理,才能執行此過濾器後面的過濾器。 -->
    <!-- 若順序錯誤,則無法實現上述第1點(不執行真正的業務處理)。 -->
    <filter-mapping>
        <filter-name>ACAO-option</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter>
        <filter-name>ACAO-option</filter-name>
        <filter-class>包名.ACAOFilter</filter-class>
    </filter>

 

// ACAOFilter
public class ACAOFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(!(request instanceof HttpServletRequest)){
            chain.doFilter(request, response);
        }
        HttpServletRequest req = (HttpServletRequest)request;
        String method = req.getMethod();
        HttpServletResponse res = (HttpServletResponse)response;
        // 向響應頭中新增3個關鍵值。
        // 請注意,這3個關鍵值無論請求方法是否為OPTIONS都要新增。
        // 若沒有新增這3個關鍵值,則Angular發起真實的GET和POST請求時,仍會接到錯誤通知:“伺服器不允許跨域”。
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTION");
        res.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
        // 如果不是OPTIONS方法的請求,過濾器鏈繼續執行。否則結束。
        if(!"OPTIONS".equals(method)){
            chain.doFilter(request, response);
        }
    }
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    public void destroy() {
    }
}
    /**
     * php允許跨域訪問。並返回是否需要繼續處理。
     */
    public static function allowOriginRequest():void{
        header('Access-Control-Allow-Origin:*');
        $method = $_SERVER['REQUEST_METHOD'];
        if(strtoupper($method) == "OPTIONS"){
            exit();
        }
    }