I'm trying to handle errors coming from my backend. The handleMessage() is called if an error occurs but the content is an instance of XmlMessage. I would like to change it to my own response - just set the response code and add some message.
I haven't found any proper documentation which could tell me how to do this...
These axamples are for REST but I'd like to manage this thing in SOAP too.
interceptor
public class ErrorHandlerInterceptor extends AbstractPhaseInterceptor<Message> {
public ErrorHandlerInterceptor() {
super(Phase.POST_LOGICAL);
}
#Override
public void handleMessage(Message message) throws Fault {
Response response = Response
.status(Response.Status.BAD_REQUEST)
.entity("HOW TO GET A MESSAGE FROM AN EXCEPTION IN HERE???")
.build();
message.getExchange().put(Response.class, response);
}
}
context.xml
<bean id="errorHandlerInterceptor"
class="cz.cvut.fit.wst.server.interceptor.ErrorHandlerInterceptor" />
<jaxrs:server address="/rest/">
<jaxrs:serviceBeans>
<ref bean="restService" />
</jaxrs:serviceBeans>
<jaxrs:outFaultInterceptors>
<ref bean="errorHandlerInterceptor" />
</jaxrs:outFaultInterceptors>
</jaxrs:server>
If you're using JAX-RS, why not setup an exception mapper, and then use that mapper to handle the response.
A simple example:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class MyExceptionMapper implements
ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException e) {
return Response.status(Status.NOT_FOUND).build();
}
}
Then you would need to register the provider in the jaxrs serve by adding:
<jaxrs:providers>
<bean class="com.blah.blah.blah.blah.MyExceptionMapper"/>
</jaxrs:providers>
in the server config in the context. With that you have full access to the exception, and can get whatever you want from it.
And here's the other piece of your puzzle. You're already using JAX-RS, so why not use JAX-WS as well?
This thread and this blog post cover mapping Exceptions into SOAP faults. Short and sweet:
The JAX-WS 2.0 specification demands that the exception annotated with #WebFault must have two constructors and one method [getter to obtain the fault information]:
WrapperException(String message, FaultBean faultInfo)
WrapperException(String message, FaultBean faultInfo, Throwable cause)
FaultBean getFaultInfo()
The WrapperException is replaced by the name of the exception, and FaultBean is replaced by the class name that implements the fault bean. The fault bean is a Java bean that contains the information of the fault and is used by the Web service client to know the cause for the fault.
And there's your mapping. Simply specify implementations of the above signatures in the context of #WebFault and your SOAP API should map these happily. Obviously, the links contain more details.
Related
I am aware that swagger-ui can be fully disabled using #Profile on spring-boot application but I still want certain privileged user to be able to access swagger-ui and not fully disabled.
Is there a way to achieve this.
update:
currently I am using interceptor approach but i don't want this approach.
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if(request.getRequestURI().contains("swagger") &&
!request.isUserInRole("XX_YY_ZZ")) {
response.sendError(403, "You are not authorized to access "); }
return super.preHandle(request, response, handler);
}
Without version you use, or codes, it is difficult to help. But I'll try as best as I can.
When you are using swagger-ui, you have an exposed URL to access your docs (usually, /swagger-ui.html).
You are using spring-boot, and talking about user restriction, so I assume you can use spring-boot-starter-security.
With spring-boot-starter-security, you can configure easily what URL you want to protect (regarding user roles for instance).
Here is a sample configuration that works:
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// the rest of your configuration
http.authorizeRequests().mvcMatchers("/swagger-ui.html").hasRole("DEVELOPER")
}
You can secure swagger URLs just like any URLs you expose with your Controllers.
For more information:
A similar issue:
How to configure Spring Security to allow Swagger URL to be accessed without authentication
Spring security configuration:
https://spring.io/guides/gs/securing-web/
I could help more with:
An extract of your security configuration
The version of Spring-boot you're using
I would suggest adding an interceptor or you can handle it in your exiting interceptor if you have any.
In the spring configuration file:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/swager-url" />
<ref bean="swagerInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
<bean id="swagerInterceptor" class="com.security.SwagerInterceptor">
<property name="userService" ref="userService" />
</bean>
The interceptor class can be written similar to this:
public class SwagerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//Take out user information from the request and validate
}
I have a REST service, where i need to send appropriate status codes with the custom json response, which explains the reason for the error.
I have #Controller class which exposes a service /test and while hitting the service, I am throwing a custom exception (extends Exception class)
I have a #ControllerAdvice class which handles the exception. However, whenever i get error i don't get the json response, instead the stack trace is printed.
Checked the blogs and tried the solutions. However could not get it resolved.
#ControllerAdvice(basePackages = "com.test")
public class ResponseExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(value= {MyException.class})
#ResponseStatus(code=HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
ResponseEntity<MyResponse> commonServiceException(MyException ex) {
MyResponse errors = new MyResponse();
errors.setTimeStamp(LocalDateTime.now());
errors.setErrorCode(ex.getErrorCode());
errors.setErrorMessage(ex.getMessage());
errors.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
errors.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
return new ResponseEntity<>(errors, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
SERVICE
#Controller
....
public class test{
..
#GET
#Path("/test")
//#ExceptionHandler(value= {MyException.class})
#Produces(MediaType.APPLICATION_JSON)
public String test() throws MyException{
throw new MyException("123", "message");
}
context.xml
<context:component-scan base-package=" com.test">
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
Expected : JSON Response
Actual : Type Exception Report
Message Service currently unavailable. Error Code : Application Error. Error
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
com.test.MyException: Service currently unavailable. Error Code : Application Error..............................
This was because I was mixing up Jersey and Spring Framework. After doing necessary spring servlet configurations in web.xml and using #RequestMapping the issue is resolved.
I have this soap handler:
#Slf4j
public class ResponseInterceptor implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
try {
SOAPMessage message = context.getMessage();
ByteArrayOutputStream out = new ByteArrayOutputStream();
message.writeTo(out);
String strMsg = new String(out.toByteArray());
} catch (SOAPException | IOException e) {
e.printStackTrace();
}
return false;
}
But I handle Requests. Is there a similar way to handle Responses?
EDIT:
I have next task: I need handle all RAW responces from SOAP service, filter it, and send to apache kafka. I do not want to have unmarshaling operation and I want send RAW responce to kafka
EDIT2:
I write interceptor:
#Slf4j
public class ResponseInterceptor extends AbstractPhaseInterceptor<Message> {
public ResponseInterceptor() {
super(Phase.PRE_UNMARSHAL);
}
#Override
public void handleMessage(Message message) throws Fault {
try {
SOAPMessage soapMessage = message.getContent(SOAPMessage.class);
ByteArrayOutputStream out = new ByteArrayOutputStream();
soapMessage.writeTo(out);
String strMsg = new String(out.toByteArray());
message.getInterceptorChain().abort();
} catch (SOAPException | IOException e) {
e.printStackTrace();
}
}
}
But if I call message.getInterceptorChain().abort(); I get exception in service. But I need just brake this responce and not delivery to web service
CXF interceptors are not "in and of themselves" linked to requests or responses, for at least two reasons :
Many interceptors can work on both sides (e.g. logging the soap payload)
There is a symetry of what is to be done on the request/response side of things, with respect to the client/server nature of the app.
So the way CXF works is that interceptors are bound to "chains", which CXF creates and manages at runtime, and which account for all combinations of the above : IN, OUT, IN_FAULT, OUT_FAULT. You can read all about them here.
If your current interceptor handles "requests" that means one of two things :
If your application is a server, then, your interceptor is bound to the "IN" chain
If your application is a client, then, your interceptor is bound to the "OUT" chain
If you want to handle responses as well as requests, you need to find how/where your custom interceptors are bound to a chain, which is usually in the configuration file in CXF (see : "writing and configuring interceptors" in the abose link).
Many people use CXF with Spring configuration, and therefore, add interceptors at the whole CXF (bus) level like so :
<bean id="MyInterceptor" class="demo.interceptor.MyInterceptor"/>
<!-- We are adding the interceptors to the bus as we will have only one endpoint/service/bus. -->
<cxf:bus>
<cxf:inInterceptors>
<ref bean="MyInterceptor"/>
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref bean="MyInterceptor"/>
</cxf:outInterceptors>
</cxf:bus>
But it can also be done at the enpoint level.
Further reading : How to catch any exceptions in a SOAP webservice method?
Accessing the contents before (un)marshalling
I could expand a lot, but I suggest you look at the org.apache.cxf.interceptor.LoggingInInterceptor (or the respective one for "Out" messages), they are as good an example as you can see of "how to access the raw content without breaking anything".
I'm implementing a SOAP client that interacts with some legacy SOAP services.
All of SOAP body requests have the same format, like this:
<soap:Body>
<execute>
<msg>
</msg>
</execute>
</soap:Body>
As content, the msg element supports a list of any XML tags, so I can send any type of element inside msg: orders, customer, contacts, etc..
All of the requests also have the same action name.
Due to the restrictions/aspects above, if I use spring's PayloadRootAnnotationMethodEndpointMapping, since the root element in the soap body is the same for all the requests, each request will fall in the same method of my Endpoint. If I use spring's SoapActionAnnotationMethodEndpointMapping, since the action is the same for every requests, all of them will fall in the same method again.
The only thing I've got different in the requests, is the requesting URI.
It changes by the name of the operation that I'm invoking. Like:
http://host:port/services/Operation1?Wsdl or
http://host:port/services/Operation2?Wsdl
My idea was to have an endpoint for each type of requests. For example: all request related to products would fit into a "ProductsEndpoint".
I wanted to create a custom endpoint mapping that would extend springs AbstractEndpointMapping. In my implementation I would decide which endpoint to call based on the URI.
But how can I register my custom endpoint mapping in the spring's endpoint mapping chain?
Best regards
If anyone has a request like mine explained above, here goes what I've decided to do...
I've create a class MultipleMarshallersPayloadMethodProcessor extending spring org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor. This is the class responsible for marshalling and unmarshalling arguments. In this class I've defined a java.util.map that will associate a given URL to a specific Marshaller. If the current URL request isn't a key in the map, it would use the default Marshaller supplied by the MarshallingPayloadMethodProcessor class.
To register the class as a spring bean:
<bean id="marshallingPayloadMethodProcessor"
class="br.com.tim.fiber.middleware.services.server.helpers.MultipleMarshallersPayloadMethodProcessor">
<constructor-arg ref="defaultMarshaller" />
<property name="otherMarshallers">
<map>
<entry key="/Operation1?wsdl" value-ref="operation1Marshaller"></entry>
<entry key="/Operation2?wsdl" value-ref="operation2Marshaller"></entry>
</map>
</property>
</bean>
And an example of a marshaller:
<bean id="operation1Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>com.app.dtos.operation1</value>
<value>com.app.dtos.common</value>
</list>
</property>
</bean>
With this setup I was able to marshall and unmarshall any request according to the URL. Then I've used the Facade design pattern to create a SOAP Endpoint with a single method that receives all the requests. That method just inspects the URL and delegates to specific endpoints.
#Endpoint
public class FacadeEndpoint {
private static final String NAMESPACE_URI = "http://my.namespace.com/services";
#Autowired
private RequesEndpointURLExtractor requestUrlExtractor;
#Autowired
private OrdersEndpoint ordersEndpoint;
#SuppressWarnings("unchecked")
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "execute")
#ResponsePayload
public ExecuteResponse dispatch(#RequestPayload Execute request) {
String serviceURL = this.requestUrlExtractor.getCurrentURL();
ExecuteResponse response = null;
if (serviceURL.equals(ServiceRequestsEndpoint.CREATE_ENDPOINT_URI)) {
Operation1DTO serviceRequest = (Operation1DTO) request.getMsg().getAnies().get(0);
}
...
}
The RequestEnpointURLExtractor is just a spring bean that extracts the full URL from the request.
#Component
public class RequesEndpointURLExtractor {
public String getCurrentURL() {
TransportContext ctx = TransportContextHolder.getTransportContext();
HttpServletRequest httpServletRequest = ((HttpServletConnection) ctx.getConnection()).getHttpServletRequest();
String pathInfo = httpServletRequest.getPathInfo();
String queryString = httpServletRequest.getQueryString();
return pathInfo + "?" + queryString;
}
}
I could create a custom annotation that would have a URL associated, and use that annotation on methods that would process the request with the URL configured. That would be a cleaner solution instead of the if/else if ladder that I have.
But, since this was only for a simple mockup server, the if/else if ladder isn't such a big deal.
In an incoming soap request there is a soap:mustUnderstand="1" element in soap header ,how can I handle this in my web service . If soap:mustUnderstand="1" it throws exception when it is 0 (soap:mustUnderstand="0") it runs as expected .
this is my partial soap request is like this
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns="http://www.xxxxxxx/zzzzz-msg/schema/msg-header-1_0.xsd">
<MessageHeader ResponseRequested="true" version="1.0" Terminate="true" Reverse="true" id="0002P559C1" soap:mustUnderstand="1">
.......
......
I am using Apache CXF for web service .
Your service should explicitly tell CXF that the given header has been understood and processed.
One way of doing it is registering a subclass of SOAPHandler responsible for actual processing of you header. In that interface it's important to implement method Set<QName> getHeaders() and return a set of headers' names that your handler takes care about.
CXF will then treat all those headers as understood
Example:
in Spring context XML:
<jaxws:endpoint ...>
<jaxws:handlers>
<bean class="example.MySOAPHandler" />
</jaxws:handlers>
</jaxws:endpoint>
in Java code:
public class MySOAPHandler implements SOAPHandler<SOAPMessageContext> {
public static final String MY_NS_URI = "http://www.xxxxxxx/zzzzz-msg/schema/msg-header-1_0.xsd";
public static final String MY_HEADER_NAME = "MessageHeader";
#Override
public Set<QName> getHeaders() {
// This will tell CXF that the following headers are UNDERSTOOD
return Collections.singleton(new QName(MY_NS_URI, MY_HEADER_NAME));
}
// other handler methods here
}
If a header block is annotated with mustUnderstand="1" and the
receiver wasn't designed to support the given header, the message
shouldn't be processed and a Fault should be returned to the sender
(with a soap:MustUnderstand status code). When mustUnderstand="0" or
the mustUnderstand attribute isn't present, the receiver can ignore
those headers and continue processing. The mustUnderstand attribute
plays a central role in the overall SOAP processing model.
For details kindly refer to this link