为每个请求使用不同的ClientInterceptor,使用Spring Web Services。

huangapple go评论143阅读模式
英文:

Provide different ClientInterceptor per request using Spring Web Services

问题

我通过扩展WebServiceGatewaySupport创建了一个自定义的网络服务客户端,还实现了自定义的ClientInterceptor来记录一些请求/响应数据。
因为每次调用都需要存储一些关于请求的数据,所以我必须为每个调用创建新的拦截器。

问题出现在当我对我的客户端进行两次或更多次调用时。第一个请求应用了它自己的带有其clientId的拦截器。第二个请求也应该是同样的情况。但由于两个请求都在我的客户端中使用相同的WebServiceTemplate,第二个请求会用它自己的带有其clientId的拦截器替换掉原来的拦截器。

因此,我应该在控制台上获得以下输出:

  1. Request: clientId-1
  2. Request: clientId-2
  3. Response: clientId-1
  4. Response: clientId-2

但实际上我得到了这个:

  1. Request: clientId-1
  2. Request: clientId-2
  3. Response: clientId-2
  4. Response: clientId-2

以下是一些代码示例(仅用于理解其工作原理):

  1. // 代码示例见原文

是否可能为每个调用实现带有一些状态的自定义拦截器?最好不要在WebServiceTemplate上使用任何锁定以避免性能下降。

英文:

I've created a custom web service client by extending WebServiceGatewaySupport and also implement custom ClientInterceptor to log some request/response data.
I have to create new interceptor for every call because it has to store some data about the request.

The problem occurs when I make two or more calls to my client. The first request applies its own interceptor with its clientId. The second should do the same. But since both requests use the same WebServicetemplate in my client, the second request replaces the interceptor with its own, with its clientId there.

As a result, I should get the following output to the console:

  1. Request: clientId-1
  2. Request: clientId-2
  3. Response: clientId-1
  4. Response: clientId-2

But I got this:

  1. Request: clientId-1
  2. Request: clientId-2
  3. Response: clientId-2
  4. Response: clientId-2

Here is come code examples (just for understanding how it should work):

  1. @Data
  2. class Response {
  3. private final String result;
  4. public Response(String result) {
  5. this.result = result;
  6. }
  7. }
  8. @Data
  9. class Request {
  10. private final String firstName;
  11. private final String lastName;
  12. }
  13. @Data
  14. class Context {
  15. private final String clientId;
  16. }
  17. @Data
  18. class Client {
  19. private final String clientId;
  20. private final String firstName;
  21. private final String lastName;
  22. }
  23. class CustomInterceptor extends ClientInterceptorAdapter {
  24. private final String clientId;
  25. public CustomInterceptor(String clientId) {
  26. this.clientId = clientId;
  27. }
  28. @Override
  29. public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
  30. System.out.println("Request: " + clientId);
  31. return true;
  32. }
  33. @Override
  34. public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
  35. System.out.println("Response: " + clientId);
  36. return true;
  37. }
  38. @Override
  39. public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
  40. System.out.println("Error: " + clientId);
  41. return true;
  42. }
  43. }
  44. @Component
  45. class CustomClient extends WebServiceGatewaySupport {
  46. public Response sendRequest(Request request, Context context) {
  47. CustomInterceptor[] interceptors = {new CustomInterceptor(context.getClientId())};
  48. setInterceptors(interceptors);
  49. return (Response) getWebServiceTemplate().marshalSendAndReceive(request);
  50. }
  51. }
  52. @Service
  53. @RequiredArgsConstructor
  54. class CustomService {
  55. private final CustomClient customClient;
  56. public String call(Request request, Context context) {
  57. Response response = customClient.sendRequest(request, context);
  58. return response.getResult();
  59. }
  60. }
  61. @RestController
  62. @RequestMapping("/test")
  63. @RequiredArgsConstructor
  64. class CustomController {
  65. private final CustomService service;
  66. public CustomController(CustomService service) {
  67. this.service = service;
  68. }
  69. @PostMapping
  70. public String test(@RequestBody Client client) {
  71. Request request = new Request(client.getFirstName(), client.getLastName());
  72. Context context = new Context(client.getClientId());
  73. return service.call(request, context);
  74. }
  75. }

Is it possible to implement custom interceptors with some state for each call? Preferably without any locks on WebServicetemplate to avoid performance degradation.

答案1

得分: 0

好的。我已经为您找到了我的解决方案。
我已经创建了WebServiceMessageCallback的实现,并且在使用它时,我将每个请求的数据保存在WebServiceMessage的 MIME 头中。

  1. @Data
  2. class CustomMessageCallback implements WebServiceMessageCallback {
  3. private final String clientId;
  4. @Override
  5. public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
  6. MimeHeaders headers = ((SaajSoapMessage) message).getSaajMessage().getMimeHeaders();
  7. headers.addHeader("X-Client-Id", clientId);
  8. }
  9. }

然后在我的客户端实现中传递此回调:

  1. @Component
  2. class CustomClient extends WebServiceGatewaySupport {
  3. public Response sendRequest(Request request, Context context) {
  4. CustomInterceptor[] interceptors = {new CustomInterceptor()};
  5. setInterceptors(interceptors);
  6. return (Response) getWebServiceTemplate()
  7. .marshalSendAndReceive(request, new CustomMessageCallback(context.getClientId()));
  8. }
  9. }

因此,现在我可以在通过拦截器处理请求/响应/错误时获取这些数据。

  1. @Override
  2. public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
  3. String clientId = ((SaajSoapMessage) messageContext.getRequest())
  4. .getSaajMessage()
  5. .getMimeHeaders()
  6. .getHeader("X-Client-Id")[0];
  7. System.out.println("Request: " + clientId);
  8. return true;
  9. }
英文:

Okay. I've found the solution for my case.
I've created an implementation of WebServiceMessageCallback and using it I'm saving data of each request not in interceptor but in WebServiceMessage's mime header.

  1. @Data
  2. class CustomMessageCallback implements WebServiceMessageCallback {
  3. private final String clientId;
  4. @Override
  5. public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
  6. MimeHeaders headers = ((SaajSoapMessage) message).getSaajMessage().getMimeHeaders();
  7. headers.addHeader("X-Client-Id", clientId);
  8. }
  9. }

And pass this callback in my client implementation:

  1. @Component
  2. class CustomClient extends WebServiceGatewaySupport {
  3. public Response sendRequest(Request request, Context context) {
  4. CustomInterceptor[] interceptors = {new CustomInterceptor()};
  5. setInterceptors(interceptors);
  6. return (Response) getWebServiceTemplate()
  7. .marshalSendAndReceive(request, new CustomMessageCallback(context.getClientId()));
  8. }
  9. }

So now I can get this data while processing request/response/error via interceptor.

  1. @Override
  2. public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
  3. String clientId = ((SaajSoapMessage) messageContext.getRequest())
  4. .getSaajMessage()
  5. .getMimeHeaders()
  6. .getHeader("X-Client-Id")[0];
  7. System.out.println("Request: " + clientId);
  8. return true;
  9. }

huangapple
  • 本文由 发表于 2020年10月21日 17:34:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/64460717.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定