2014-07-18 155 views
1

我需要編寫一個Web客戶端,它可以訪問舊版網絡應用,登錄到該網頁,從/widget頁面提取一些信息,並根據此頁面的HTML做一些工作。我選擇使用Groovy/HttpBuilder解決方案,原因不在此問題範圍之內。Groovy HttpBuilder與餅乾問題

唯一的缺點(從我可以告訴的是)HttpBuilder不支持在請求之間保留cookie。這是一個重大的問題,因爲(Java)的Web應用程序使用JSESSIONID cookie來確定用戶是否登錄,擁有權限等

因此,首先,如果我上面的說法是不正確的,HttpBuilder 確實支持跨請求保留Cookie,請糾正我,也許這裏的答案是一個解決方案,告訴我如何利用HttpBuilder的這一部分。在這種情況下,我所有的代碼都是沒有意義的。

假設我是正確的,這不是由HttpBuilder處理,我發現this excellent solution,我無法工作出於某種原因,因此我的問題。

我的代碼的調整(見上面的鏈接)如下:

TaskAutomator.groovy 
==================== 
package com.me.myapp.tasker 

import groovyx.net.http.ContentType 
import groovyx.net.http.Method 

class TaskAutomator { 
    static void main(String[] args) { 
     TaskAutomator tasker = new TaskAutomator() 
     String result = tasker.doWork("http://myapp.example.com") 

     println result 
    } 

    String doWork(String baseUrl) { 
     CookieRetainingHttpBuilder cookiedBuilder = new CookieRetainingHttpBuilder(baseUrl) 
     Map logins = [username: 'user', password: '12345'] 

     // Go to the main page where we will get back the HTML for a login screen. 
     // We don't really care about the response here, so long as its HTTP 200. 
     cookiedBuilder.request(Method.GET, ContentType.HTML, "", null) 

     // Log in to the app, where, on success, we will get back the HTML for a the 
     // "Main Menu" screen users see when they log in. We don't really care about 
     // the response here, so long as its HTTP 200. 
     cookiedBuilder.request(Method.POST, ContentType.HTML, "/auth", logins) 

     // Finally, now that our JSESSIONID cookies is authenticated, go to the widget page 
     // which is what we actually care about interacting with. 
     def response = cookiedBuilder.request(Method.GET, ContentType.HTML, "/widget", null) 

     // Test to make sure the response is what I think it is. 
     print response 

     String result 

     // TODO: Now actually do work based off the response. 

     result 
    } 
} 

CookieRetainingHttpBuilder 
========================== 
package com.me.myapp.tasker 

import groovyx.net.http.ContentType 
import groovyx.net.http.HTTPBuilder 
import groovyx.net.http.HttpResponseDecorator 
import groovyx.net.http.Method 

class CookieRetainingHttpBuilder { 
    private String baseUrl 
    private HTTPBuilder httpBuilder 
    private List<String> cookies 

    CookieRetainingHttpBuilder(String baseUrl) { 
     this.baseUrl = baseUrl 
     this.httpBuilder = initializeHttpBuilder() 
     this.cookies = [] 
    } 

    public def request(Method method, ContentType contentType, String url, Map<String, Serializable> params) { 
     httpBuilder.request(method, contentType) { request -> 
      uri.path = url 
      uri.query = params 
      headers['Cookie'] = cookies.join(';') 
     } 
    } 

    private HTTPBuilder initializeHttpBuilder() { 
     def httpBuilder = new HTTPBuilder(baseUrl) 

     httpBuilder.handler.success = { HttpResponseDecorator resp, reader -> 
      resp.getHeaders('Set-Cookie').each { 
       String cookie = it.value.split(';')[0] 
       cookies.add(cookie) 
      } 

      reader 
     } 

     httpBuilder 
    } 
} 

當我運行這段代碼,我得到了下面的堆棧跟蹤(我已經抹去了非有趣的部分是它的相當大):

Exception in thread "main" groovyx.net.http.HttpResponseException: Not Found 
    at groovyx.net.http.HTTPBuilder.defaultFailureHandler(HTTPBuilder.java:642) 
     ... (lines omitted for brevity) 
    at groovyx.net.http.HTTPBuilder$1.handleResponse(HTTPBuilder.java:494) 
     ... (lines omitted for brevity) 
    at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:506) 
    at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:425) 
    at groovyx.net.http.HTTPBuilder.request(HTTPBuilder.java:374) 
    at groovyx.net.http.HTTPBuilder$request.call(Unknown Source) 
    at com.me.myapp.tasker.CookieRetainingHttpBuilder.request(CookieRetainingHttpBuilder.groovy:20) 
     ... (lines omitted for brevity) 
    at com.me.myapp.tasker.TaskAutomator.doWork(TaskAutomator.groovy:23) 
     ... (lines omitted for brevity) 
    at com.me.myapp.tasker.TaskAutomator.main(TaskAutomator.groovy:13) 

CookieRetainingHttpBuilder:20是從request這一行:

httpBuilder.request(method, contentType) { request -> 

任何人都可以看到爲什麼我得到這個?此外,我想在TaskAutomater#doWork(...)方法中確認我的方法/策略。 是在我感覺我使用CookieRetainingHttpBuilder「糾正」

  1. 進入主/登錄頁面
  2. 發佈登錄creds和登錄
  3. 將小部件頁

還是有不同的方式來使用HttpBuilder,這裏更好/更高效(記住CookieRetainingHttpBuilder畢竟只是HttpBuilder的包裝器)。

+0

基於從https://github.com/jgritman/httpbuilder/blob/master/src/main/java/groovyx/net/http/HTTPBuilder.java代碼,你就得到了http請求的一些失敗。我不確定究竟是什麼「未找到」,可能的主持人。您可以捕獲https://github.com/jgritman/httpbuilder/blob/master/src/main/java/groovyx/net/http/HttpResponseException.java並檢查內部。 – Vartlok

+1

即使您決定使用'HttpBuilder',我建議查看[Fluent HttpClient](http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fluent.html) - 查看cookie使用請參閱http://java.dzone.com/tips/fluency-and-control-httpclient – ChrLipp

回答

3

我相信錯誤可能是由於缺少導入,或者可能是舊版本的HttpBuilder。展望HttpBuilder.Class,我看到這一點,它通知我的建議:

protected java.lang.Object parseResponse(org.apache.http.HttpResponse resp, java.lang.Object contentType) throws groovyx.net.http.HttpResponseException { /* compiled code */ } 

我相當肯定你可以在你的httpBuilder設置中使用headers.'Set-Cookie。語法與您所擁有的語法不同,但這種更改很小且很簡單,這是使用HttpBuilder時所使用的基本方法。

@Grab(group = 'org.codehaus.groovy.modules.http-builder', module = 'http-builder', version = '0.7) 
    import groovyx.net.http.HTTPBuilder 
    import org.apache.http.HttpException 
    import static groovyx.net.http.ContentType.TEXT 
    import static groovyx.net.http.Method.GET 

    def http = new HTTPBuilder(urlToHit) 
    http.request(urlToHit, GET, TEXT) { req -> 

    headers.'User-Agent' = ${userAgent} 
    headers.'Set-Cookie' = "${myCookie}" 

    response.success = { resp, reader -> 
     html = reader.getText() 
    } 

    response.failure = { resp, reader -> 
     System.err.println "Failure response: ${resp.status}" 
     throw new HttpException()   
    }   
} 

還有一點要注意的是,你沒有失敗處理。我不知道這是否會引發例外,但可能值得探討。

編輯 至於建議,我合併我的答案(謝謝你讓我知道......我不知道是什麼適當的禮儀)。

這是我想出來的。我盡我所能重複使用您發佈的代碼。我盡我所能評論。如果您有任何問題,請告訴我。

@Grab(group = 'org.codehaus.groovy.modules.http-builder', module = 'http-builder', version = '0.7') 
import static groovyx.net.http.ContentType.HTML 
import static groovyx.net.http.Method.POST 
import static groovyx.net.http.Method.GET 
import groovyx.net.http.ContentType 
import groovyx.net.http.HTTPBuilder 
import groovyx.net.http.URIBuilder 
import groovyx.net.http.Method 
import org.apache.http.HttpException 

/** 
* This class defines the methods used for getting and using cookies 
* @param baseUrl The URL we will use to make HTTP requests. In this example, it is https://www.pinterest.com 
*/ 

class CookieRetainingHttpBuilder { 

    String baseUrl 

    /** 
    * This method makes an http request and adds cookies to the array list for later use 
    * @param method The method used to make the http request. In this example, we use GET and POST 
    * @param contentType The content type we are requesting. In this example, we are getting HTML 
    * @param url The URI path for the appropriate page. For example, /login/ is for the login page 
    * @param params The URI query used for setting parameters. In this example, we are using login credentials 
    */ 

    public request (Method method, ContentType contentType, String url, Map<String, Serializable> params) { 

     List<String> cookies = new ArrayList<>() 

     def http = new HTTPBuilder(baseUrl) 

     http.request(baseUrl, method, contentType) { req -> 

      URIBuilder uriBuilder = new URIBuilder(baseUrl) 
      uriBuilder.query = params 
      uriBuilder.path = url 

      headers.'Accept' = HTML 
      headers.'User-Agent' = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36" 
      headers.'Set-Cookie' = cookies.join(";") 

      response.success = { resp, reader -> 

       resp.getHeaders('Set-Cookie').each { 
        def cookie = it.value.split(";").toString() 
        cookies.add(cookie) 
       } 

       return reader 

      } 

      response.failure = { resp, reader -> 
       System.err.println "Failure response: ${resp.status}" 
       throw new HttpException() 
      } 

     } 

    } 

} 

/** 
* This class contains the method to make HTTP requests in the proper sequence 
* @param base The base URL 
* @param user The username of the site being logged in to 
* @param pass The password for the username 
*/ 

class TaskAutomator { 

    private static String base = "http://myapp.example.com" 
    private static String user = "thisIsMyUser" 
    private static String pass = "thisIsMyPassword" 

    /** 
    * This method contains the functions in proper order to set cookies and login to a site 
    * @return response Returns the HTML from the final GET request 
    */ 

    static String doWork() { 

     CookieHandler.setDefault(new CookieManager()); 

     CookieRetainingHttpBuilder cookiedBuilder = new CookieRetainingHttpBuilder(baseUrl: base) 
     Map logins = [username: user, password: pass] 

     // Go to the main page where we will get back the HTML for a login screen. 
     // We don't really care about the response here, so long as its HTTP 200. 
     cookiedBuilder.request(GET, HTML, "", null) 

     // Log in to the app, where, on success, we will get back the HTML for a the 
     // "Main Menu" screen users see when they log in. We don't really care about 
     // the response here, so long as its HTTP 200. 
     cookiedBuilder.request(POST, HTML, "/login/", logins) 

     // Finally, now that our JSESSIONID cookies is authenticated, go to the widget page 
     // which is what we actually care about interacting with. 
     def response = cookiedBuilder.request(GET, HTML, "/", null) 

     // Test to make sure the response is what I think it is. 
     return response 

     // TODO: Now actually do work based off the response. 

    } 

} 

TaskAutomator tasker = new TaskAutomator() 
String result = tasker.doWork() 
println result 
+1

我剛剛意識到我錯過了@Grab上的一個撇號,所以它應該是'@Grab(group ='org.codehaus。 groovy.modules.http-builder',module ='http-builder',version ='0.7')' – paranoid

+0

Thanks @paranoid(+1) - 我對你的解決方案很感興趣,任何機會你都可以更新它來顯示它用法(特別是對我的用例)?我將如何使用它來:(1)打一個登錄頁面,(2)在該頁面保存'JSESSIONID',(3)登錄,(4)轉到同一站點上的另一個(已認證)頁面,使用cookie ?再次感謝! – IAmYourFaja