使用 Spring Web 服务为每个请求提供不同的 ClientInterceptor
Provide different ClientInterceptor per request using Spring Web Services
我通过扩展 WebServiceGatewaySupport
创建了一个自定义 Web 服务客户端,还实现了自定义 ClientInterceptor
来记录一些 request/response 数据。
我必须为每个调用创建新的拦截器,因为它必须存储一些关于请求的数据。
当我向我的客户发出两次或更多次呼叫时出现问题。第一个请求使用它自己的拦截器 clientId
。第二个应该做同样的事情。但是由于两个请求在我的客户端中使用了相同的 WebServicetemplate
,第二个请求用它自己的拦截器替换了拦截器,其中 clientId
在那里。
因此,我应该将以下输出输出到控制台:
Request: clientId-1
Request: clientId-2
Response: clientId-1
Response: clientId-2
但是我得到了这个:
Request: clientId-1
Request: clientId-2
Response: clientId-2
Response: clientId-2
这里是代码示例(只是为了理解它应该如何工作):
@Data
class Response {
private final String result;
public Response(String result) {
this.result = result;
}
}
@Data
class Request {
private final String firstName;
private final String lastName;
}
@Data
class Context {
private final String clientId;
}
@Data
class Client {
private final String clientId;
private final String firstName;
private final String lastName;
}
class CustomInterceptor extends ClientInterceptorAdapter {
private final String clientId;
public CustomInterceptor(String clientId) {
this.clientId = clientId;
}
@Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Request: " + clientId);
return true;
}
@Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Response: " + clientId);
return true;
}
@Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Error: " + clientId);
return true;
}
}
@Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor(context.getClientId())};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate().marshalSendAndReceive(request);
}
}
@Service
@RequiredArgsConstructor
class CustomService {
private final CustomClient customClient;
public String call(Request request, Context context) {
Response response = customClient.sendRequest(request, context);
return response.getResult();
}
}
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
class CustomController {
private final CustomService service;
public CustomController(CustomService service) {
this.service = service;
}
@PostMapping
public String test(@RequestBody Client client) {
Request request = new Request(client.getFirstName(), client.getLastName());
Context context = new Context(client.getClientId());
return service.call(request, context);
}
}
是否可以为每个调用实现具有某种状态的自定义拦截器?最好不要对 WebServicetemplate
进行任何锁定以避免性能下降。
好的。我已经找到适合我案例的解决方案。
我已经创建了 WebServiceMessageCallback
的实现并使用它我不是在拦截器中而是在 WebServiceMessage
的 mime header.
中保存每个请求的数据
@Data
class CustomMessageCallback implements WebServiceMessageCallback {
private final String clientId;
@Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
MimeHeaders headers = ((SaajSoapMessage) message).getSaajMessage().getMimeHeaders();
headers.addHeader("X-Client-Id", clientId);
}
}
并在我的客户端实现中传递此回调:
@Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor()};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate()
.marshalSendAndReceive(request, new CustomMessageCallback(context.getClientId()));
}
}
所以现在我可以在通过拦截器处理 request/response/error 的同时获取这些数据。
@Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String clientId = ((SaajSoapMessage) messageContext.getRequest())
.getSaajMessage()
.getMimeHeaders()
.getHeader("X-Client-Id")[0];
System.out.println("Request: " + clientId);
return true;
}
我通过扩展 WebServiceGatewaySupport
创建了一个自定义 Web 服务客户端,还实现了自定义 ClientInterceptor
来记录一些 request/response 数据。
我必须为每个调用创建新的拦截器,因为它必须存储一些关于请求的数据。
当我向我的客户发出两次或更多次呼叫时出现问题。第一个请求使用它自己的拦截器 clientId
。第二个应该做同样的事情。但是由于两个请求在我的客户端中使用了相同的 WebServicetemplate
,第二个请求用它自己的拦截器替换了拦截器,其中 clientId
在那里。
因此,我应该将以下输出输出到控制台:
Request: clientId-1
Request: clientId-2
Response: clientId-1
Response: clientId-2
但是我得到了这个:
Request: clientId-1
Request: clientId-2
Response: clientId-2
Response: clientId-2
这里是代码示例(只是为了理解它应该如何工作):
@Data
class Response {
private final String result;
public Response(String result) {
this.result = result;
}
}
@Data
class Request {
private final String firstName;
private final String lastName;
}
@Data
class Context {
private final String clientId;
}
@Data
class Client {
private final String clientId;
private final String firstName;
private final String lastName;
}
class CustomInterceptor extends ClientInterceptorAdapter {
private final String clientId;
public CustomInterceptor(String clientId) {
this.clientId = clientId;
}
@Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Request: " + clientId);
return true;
}
@Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Response: " + clientId);
return true;
}
@Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
System.out.println("Error: " + clientId);
return true;
}
}
@Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor(context.getClientId())};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate().marshalSendAndReceive(request);
}
}
@Service
@RequiredArgsConstructor
class CustomService {
private final CustomClient customClient;
public String call(Request request, Context context) {
Response response = customClient.sendRequest(request, context);
return response.getResult();
}
}
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor
class CustomController {
private final CustomService service;
public CustomController(CustomService service) {
this.service = service;
}
@PostMapping
public String test(@RequestBody Client client) {
Request request = new Request(client.getFirstName(), client.getLastName());
Context context = new Context(client.getClientId());
return service.call(request, context);
}
}
是否可以为每个调用实现具有某种状态的自定义拦截器?最好不要对 WebServicetemplate
进行任何锁定以避免性能下降。
好的。我已经找到适合我案例的解决方案。
我已经创建了 WebServiceMessageCallback
的实现并使用它我不是在拦截器中而是在 WebServiceMessage
的 mime header.
@Data
class CustomMessageCallback implements WebServiceMessageCallback {
private final String clientId;
@Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
MimeHeaders headers = ((SaajSoapMessage) message).getSaajMessage().getMimeHeaders();
headers.addHeader("X-Client-Id", clientId);
}
}
并在我的客户端实现中传递此回调:
@Component
class CustomClient extends WebServiceGatewaySupport {
public Response sendRequest(Request request, Context context) {
CustomInterceptor[] interceptors = {new CustomInterceptor()};
setInterceptors(interceptors);
return (Response) getWebServiceTemplate()
.marshalSendAndReceive(request, new CustomMessageCallback(context.getClientId()));
}
}
所以现在我可以在通过拦截器处理 request/response/error 的同时获取这些数据。
@Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
String clientId = ((SaajSoapMessage) messageContext.getRequest())
.getSaajMessage()
.getMimeHeaders()
.getHeader("X-Client-Id")[0];
System.out.println("Request: " + clientId);
return true;
}