2016-08-29 15 views
7

私は通常の方法で、@RequestMappingメソッド、Freemarkerの定義などを束ねてSpring MVCを使用するSpring Bootアプリケーションを持っています。これはすべてWebMvcConfigurerAdapterクラスで結ばれています。URLを解析し、S​​pring MVCでメソッドを「反映する」方法

ユーザーが有効なURLのリストを送信するサービスを提供したいと思います。ウェブアプリケーションは、呼び出されるコントローラーを決定し、パラメーターを渡し、すべてのURLの結合結果を返します1件のリクエスト。

これにより、ユーザーは何百ものHTTPコールを行う必要がなくなりますが、必要に応じて一度限りのリクエストを行うことができます。理想的には、私はちょうど自動的に設定されたSpring Beanを挿入するので、URLを繰り返して、Springが内部的に行う解決と適応と処理を繰り返す必要はなく、他のコントローラのコントローラのリストはrealコントローラのリスト。私はこのような何かを書くことを期待

(だけに単純化は無意味が、理解しやすい1つのURLに対処):

@Autowired BeanThatSolvesAllMyProblems allMappings; 

@PostMapping(path = "/encode", consumes = MediaType.TEXT_PLAIN_VALUE) 
@ResponseBody 
public String encode(@RequestBody String inputPath) { 
    if (allMappings.hasMappingForPath(inputPath)) { 
     return allMappings.getMapping(inputPath).execute(); 
    } else { 
     return "URL didn't match, sorry"; 
    } 
} 

を代わりに、私は私にはないのSpring Beanを定義しなければなりませんでした彼らは何をすべきか知っていると、ユーザはちょうど自分自身を呼び出して作られた場合、私は心配している春が私のために行うことを意図しているもののいくつかを、繰り返してきたことは、それと同じように、非常に同じを動作しません。

// these two are @Beans, with just their default constructor called. 
@Autowired RequestMappingHandlerMapping handlers; 
@Autowired RequestMappingHandlerAdapter adapter; 

@PostMapping(path = "/encode", consumes = MediaType.TEXT_PLAIN_VALUE) 
@ResponseBody 
public String encode(@RequestBody String inputText) { 
    final HttpServletRequest mockRequest = new MockHttpServletRequest(null, inputText); 
    final StringBuilder result = new StringBuilder(); 

    this.handlers.getHandlerMethods().forEach((requestMappingInfo, handlerMethod) -> { 
     if (requestMappingInfo.getPatternsCondition().getMatchingCondition(mockRequest) != null) { 
      try { 
       final MockHttpServletResponse mockResponse = new MockHttpServletResponse(); 
       result.append("Result: ").append(adapter.handle(mockRequest, mockResponse, handlerMethod)); 
       result.append(", ").append(mockResponse.getContentAsString()); 
       result.append("\n"); 
      } catch (Exception e) { 
       logger.error(e.getMessage(), e); 
      } 
     } 
    }); 

    return result.toString(); 
} 

私はかなりやっていると思ったこのパスをよく下っていますが、それはエラーMissing URI template variableで失敗しています。(Springがそれ自身を処理できる別のもの)リクエストパラメータをどのように置くべきかわからないだけでなく、これを行うことについて正しい道があります。だから、Webアプリケーション自体の中から、Spring MVCリクエストを「反射的に」シミュレートするにはどうすればよいですか?

+3

本当のメリットは何ですか。クライアントが多くのリクエストを行うのはまったく普通のことですが、それは問題とはみなされません。依然として依頼を送信しなければならず、何らかの方法でバッチ処理すると大幅なスピードアップは得られません。 – chrylis

+1

実際の使用例は、ドキュメントからURLを抽出し、処理された結果を含む同じドキュメントを返すので、オフラインで利用できます(またはサーバーがダウンした場合) –

+1

その後、呼び出しを再呼び出しします。 – chrylis

答えて

1

JSON API spec。要求ごとに複数の操作を送信できるようにすることで、この問題を解決します。 Elideと呼ばれるこの機能をサポートする非常に成熟した実装が存在します。しかし、これはあなたの要件を完全に満たしていないかもしれないと思います。

とにかく、できることは次のとおりです。

DispatcherServletは、適切なrequestハンドラとhandlerAdaptorsを検出するために使用されるhandlerMappingsリストを保持していることを考慮する必要があります。両方のリストの選択方法は設定可能です(DispatcherServlet#initHandlerMappingsおよび#initHandlerAdaptersを参照)。

handlerMappings/initHandlerAdaptersのリストを取得して、DispatcherServletと同期してください。

その後はあなたが実装することができ、独自のHandlerMapping/HandlerAdaptor(またはあなたの例のようにController方法を提示)request/encodeへのパスを扱うだろうと。ところで

HandlerMapping javadocが

インタフェース リクエストとハンドラとの間のマッピングは、我々はDefaultAnnotationHandlerMappingを取る場合、私たちをマッピングすることを言って

または単にオブジェクト定義するオブジェクトによって実装される言うように@RequestMappingでアノテート@Controller方法にHttpServletRequests。このマッピングを有することにより、コントローラメソッドf.exを消費するための着信要求が準備される(HandlerAdapter)。要求paramsbodyを抽出して、コントローラのメソッドを呼び出すためにそれらを使用。あなたは

呼び出す handlerMappingを持つ
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 
    for (HandlerMapping hm : this.handlerMappings) { 
     if (logger.isTraceEnabled()) { 
      logger.trace(
        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); 
     } 
     HandlerExecutionChain handler = hm.getHandler(request); 
     if (handler != null) { 
      return handler; 
     } 
    } 
    return null; 
} 

:これを持つ

、あなたはこれを呼び出してそれらをさらに処理し、ループのために必要な情報を保持するスタブHttpRequestsのリストを作成し、メインrequestからURL Sを抽出することができます

HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 
    for (HandlerAdapter ha : this.handlerAdapters) { 
     if (logger.isTraceEnabled()) { 
      logger.trace("Testing handler adapter [" + ha + "]"); 
     } 
     if (ha.supports(handler)) { 
      return ha; 
     } 
    } 

、その後、あなたは最終的に呼び出すことができます

今度はのparamsと、コントローラのメソッドを実行します

しかし、このすべてを持つ、私はこのアプローチを、以下にお勧めしません、代わりに、JSON API仕様やその他の用法について考えます。

1

スプリングはRestTemplateをクライアントとして使用するのはどうですか?ジェネリックは@RequestBody inputPathsのために動作しないことを

​​

注:それは外部の資源であるかのようにあなたは春のコントローラ内のあなたのコントローラを呼び出すことができます。

その他http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.htmlおよびhttps://spring.io/guides/gs/consuming-rest/を参照してください。

0

私はあなたの代わりにコードでそれを持つのは、プロジェクトの外でこの機能を考慮しなければならない他の回答に同意します。それはデザインの問題であり、あなたが望むアプローチを選択することができます。リクエストがGETリクエストであるとのあなたのコメントに基づいて、要求ディスパッチャがHttpServletResponseWrapperインスタンスを使用してレスポンスをキャプチャし、それぞれのURLに対して特別なコントローラサービスメソッド内でリクエストをトリガすることができます。

次のコードサンプルでは、​​ "consolidate"メソッドでカンマ区切りのURLを使用します( "http://localhost:8080/index/index1,index2"、ここでは "index1、index2"はURLリストです)、テキスト出力を単一のペイロードに統合して返します。この例のURLの場合は、http://localhost:8080/index1http://localhost:8080/index2の連結出力が返されます。 URLのパラメータや検証などを追加して拡張/変更することができます。私はSpring Boot 1.2.xでこのコードをテストしました。

@Controller 
public class MyController { 

    @RequestMapping("/index/{urls}") 
    @ResponseBody 
    String consolidate(@PathVariable String[] urls, HttpServletRequest request, HttpServletResponse response) { 

     StringBuilder responseBody = new StringBuilder(); 
     //iterate for each URL provided 
     for (String url : urls) { 
      RequestDispatcher dispatcher = request.getServletContext().getRequestDispatcher("/" + url); 
      HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) response) { 
       private CharArrayWriter output = new CharArrayWriter(); 

       @Override 
       public PrintWriter getWriter() { 
        return new PrintWriter(output); 
       } 

       @Override 
       public String toString() { 
        return output.toString(); 
       } 
      }; 

      try { 
       dispatcher.include(request, wrapper); 
       //append the response text 
       responseBody.append(wrapper.toString()); 
      } catch (ServletException | IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 

     } 
     //This holds the consolidated output 
     return responseBody.toString(); 
    } 

    @RequestMapping("/index1") 
    String index1() { 
     return "index1"; 
    } 

    @RequestMapping("/index2") 
    String index2() { 
     return "index2"; 
    } 

} 
関連する問題