2017-01-18 103 views
10

我有一個Spring + CXF應用程序,它使用在另一個服務器上運行的傳輸API:Transmission RPCSpring - 重試請求,如果服務返回409 HTTP代碼

根據傳輸文檔,您需要發送在第一個請求上生成的令牌。服務器隨後以409個http代碼和一個包含令牌的標題進行響應。此令牌應在所有後續呼叫中發送:

2.3.1。 CSRF保護大多數傳輸RPC服務器都需要一個X-Transmission-Session-Id頭部來發送請求,以防止CSR35F的攻擊。當您的請求具有錯誤的ID時 - 例如當您發送第一個請求時,或者服務器到期時,CSRF令牌 - 傳輸RPC服務器將返回HTTP 409錯誤,並返回 右邊的X-Transmission-Session- ID在它自己的頭文件中。所以,正確的 處理409響應的方法是更新您的X-Transmission-Session-Id並重新發送先前的請求。

我一直在尋找使用CXF過濾器或攔截器的解決方案,它基本上會處理409響應並重試添加令牌頭的初始請求。我在考慮客戶可以堅持這個令牌並在未來的通話中發送。

我不是很熟悉cxf,所以我想知道這是否可以完成和如何。任何提示都會有幫助。

謝謝!

回答

3

這裏spring-retry可以使用,這是一個獨立的項目,不再是春季批次的一部分。

如解釋here重試回撥將有助於使用令牌標頭更新另一個呼叫。

僞代碼/邏輯在這種情況下看起來像下面

RetryTemplate template = new RetryTemplate(); 
Foo foo = template.execute(new RetryCallback<Foo>() { 
    public Foo doWithRetry(RetryContext context) { 
     /* 
     * 1. Check if RetryContext contains the token via hasAttribute. If available set the header else proceed 
     * 2. Call the transmission API 
     * 3.a. If API responds with 409, read the token 
     * 3.a.1. Store the token in RetryContext via setAttribute method 
     * 3.a.2. Throw a custom exception so that retry kicks in 
     * 3.b. If API response is non 409 handle according to business logic 
     * 4. Return result 
     */ 
    } 
}); 

確保以合理的重試&退避策略配置RetryTemplate,以避免任何資源爭/驚喜。

讓任何疑問/路障的情況下,在評論中知道。

N.B.RetryContext的實施RetryContextSupport具有彈簧芯AttributeAccessor

1

繼承假設你使用的是Apache CXF JAX RS客戶端的hasAttribute & setAttribute方法很容易通過只是創建一個自定義運行時異常和ResponseExceptionMapper爲它做。所以想法是手動將409個結果轉換爲某種異常,然後正確處理它們(在您的情況下重試服務調用)。

請參閱下面的代碼以查看完整的工作示例。

@SpringBootApplication 
@EnableJaxRsProxyClient 
public class SpringBootClientApplication { 
    // This can e stored somewhere in db or elsewhere 
    private static String lastToken = ""; 

    public static void main(String[] args) { 
     SpringApplication.run(SpringBootClientApplication.class, args); 
    } 

    @Bean 
    CommandLineRunner initWebClientRunner(final TransmissionService service) { 
     return new CommandLineRunner() { 
      @Override 
      public void run(String... runArgs) throws Exception { 
       try { 
        System.out.println(service.sayHello(1, lastToken)); 
       // catch the TokenExpiredException get the new token and retry 
       } catch (TokenExpiredException ex) { 
        lastToken = ex.getNewToken(); 
        System.out.println(service.sayHello(1, lastToken)); 
       } 
      } 
     }; 
    } 

    public static class TokenExpiredException extends RuntimeException { 
     private String newToken; 

     public TokenExpiredException(String token) { 
      newToken = token; 
     } 

     public String getNewToken() { 
      return newToken; 
     } 
    } 

    /** 
     * This is where the magic is done !!!! 
    */ 
    @Provider 
    public static class TokenExpiredExceptionMapper implements ResponseExceptionMapper<TokenExpiredException> { 

     @Override 
     public TokenExpiredException fromResponse(Response r) { 
      if (r.getStatus() == 409) { 
       return new TokenExpiredException(r.getHeaderString("X-Transmission-Session-Id")); 
      } 
      return null; 
     } 

    } 

    @Path("/post") 
    public interface TransmissionService { 
     @GET 
     @Path("/{a}") 
     @Produces(MediaType.APPLICATION_JSON_VALUE) 
     String sayHello(@PathParam("a") Integer a, @HeaderParam("X-Transmission-Session-Id") String sessionId) 
      throws TokenExpiredException; 
    } 
} 
相關問題