I want to display two REST API endpoints in Swagger ui: /cart and /post.
When I specify either /cart or /post works fine but with both showing me error as
No operations defined in spec!
in swagger-ui
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.ant("/cart"))
.paths(PathSelectors.ant("/post"))
.build();
}
Another option is to use .paths(PathSelectors.any()) instead of .paths(PathSelectors.ant("/cart")) and .paths(PathSelectors.ant("/post"))
With Spring boot 2.6.x you also need:
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
It because you use AND condition
public ApiSelectorBuilder paths(Predicate<String> selector) {
pathSelector = pathSelector.and(selector);
return this;
}
you can combine conditions by using OR clause
.paths(PathSelectors.ant("/cart").or(PathSelectors.ant("/post")))
Please add package xxxx as root package so It can scan swagger config in all class
public String getApplicationBasePath() {
return swaggerPath;
}
}).select().apis(RequestHandlerSelectors.basePackage("xxxx")).build()
.securitySchemes(Arrays.asList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext())).apiInfo(metaData());
Related
I am using Springfox 3.0 with a simple REST controller. In Swagger UI, only GET methods are showing up, but not POST methods.
I am also struggling to get my context path in Swagger UI. Actually the context path is appending after the endpoint not before. So I placed everything in tomcat root folder without context.
<servlet-mapping>
<servlet-name>SwaggerRest</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.host("localhost:8080")
.select()
.apis(RequestHandlerSelectors.basePackage("test.maventest")).paths(PathSelectors.ant("/**"))
.build();
-------------
#Component
// make sure it runs after the default plugin springfox.documentation.swagger2.web.WebMvcBasePathAndHostnameTransformationFilter
#Order(Ordered.HIGHEST_PRECEDENCE + 1000)
public class CustomMvcPathHostFilter implements WebMvcSwaggerTransformationFilter {
#Override
public Swagger transform(SwaggerTransformationContext<HttpServletRequest> context) {
Swagger swagger = context.getSpecification();
context.request().ifPresent(servletRequest -> {
UriComponents uriComponents = HostNameProvider.componentsFrom(servletRequest, swagger.getBasePath());
swagger.host(uriComponents.getHost() + (uriComponents.getPort() > 0 ? ":" + uriComponents.getPort() : ""));
// set Swagger base path
String basePath = StringUtils.isEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath();
swagger.basePath(basePath);
System.out.println("baseoath "+basePath);
// rewrite paths to remove the leading base path from each endpoint
final Map<String, Path> newPaths = swagger.getPaths().entrySet().stream().collect(Collectors.toMap(
entry -> entry.getKey().replaceAll("^" + basePath, ""), Map.Entry::getValue));
swagger.setPaths(newPaths);
});
return swagger;
}
//Not showing in swagger ui
#PostMapping("/saveInfo")
public ResponseEntity <?> saveinFo(#RequestBody Student student) throws Exception {
System.out.println("Saving studinfo Response at ==>444444444" );
return new ResponseEntity <List<Student>> (studservice.save(student),HttpStatus.OK);
}
Try using the springdoc instead of springfox. It worked for me.
org.springdoc:springdoc-openapi-ui:1.5.13
I am new to the Spring Integration project, now I need to create a flow with Java DSL and test it.
I came up with these flows. First one should run by cron and invoke second one, which invokes HTTP endpoint and translates XML response to POJO:
#Bean
IntegrationFlow pollerFlow() {
return IntegrationFlows
.from(() -> new GenericMessage<>(""),
e -> e.poller(p -> p.cron(this.cron)))
.channel("pollingChannel")
.get();
}
#Bean
IntegrationFlow flow(HttpMessageHandlerSpec bulkEndpoint) {
return IntegrationFlows
.from("pollingChannel")
.enrichHeaders(authorizationHeaderEnricher(user, password))
.handle(bulkEndpoint)
.transform(xmlTransformer())
.channel("httpResponseChannel")
.get();
}
#Bean
HttpMessageHandlerSpec bulkEndpoint() {
return Http
.outboundGateway(uri)
.httpMethod(HttpMethod.POST)
.expectedResponseType(String.class)
.errorHandler(new DefaultResponseErrorHandler());
}
Now I want to test flow and mock HTTP call, but struggling to mock HTTP handler, I tried to do it like that:
#ExtendWith(SpringExtension.class)
#SpringIntegrationTest(noAutoStartup = {"pollerFlow"})
#ContextConfiguration(classes = FlowConfiguration.class)
public class FlowTests {
#Autowired
private MockIntegrationContext mockIntegrationContext;
#Autowired
public DirectChannel httpResponseChannel;
#Autowired
public DirectChannel pollingChannel;
#Test
void test() {
final MockMessageHandler mockHandler = MockIntegration.mockMessageHandler()
.handleNextAndReply(message -> new GenericMessage<>(xml, message.getHeaders()));
mockIntegrationContext.substituteMessageHandlerFor("bulkEndpoint", mockHandler);
httpResponseChannel.subscribe(message -> {
assertThat(message.getPayload(), is(notNullValue()));
assertThat(message.getPayload(), instanceOf(PartsSalesOpenRootElement.class));
});
pollingChannel.send(new GenericMessage<>(""));
}
}
But I am always getting an error, that on line:
mockIntegrationContext.substituteMessageHandlerFor("bulkEndpoint", mockHandler);
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'bulkEndpoint' is expected to be of type 'org.springframework.integration.endpoint.IntegrationConsumer' but was actually of type 'org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler'
Am I doing something wrong here? I am assuming I have a problem with IntegrationFlow itself, or maybe my testing approach is a problem.
The error is correct. The bulkEndpoint is not an endpoint by itself. It is really a MessageHandler. The endpoint is created from the .handle(bulkEndpoint).
See docs: https://docs.spring.io/spring-integration/docs/current/reference/html/overview.html#finding-class-names-for-java-and-dsl-configuration and https://docs.spring.io/spring-integration/docs/current/reference/html/testing.html#testing-mocks.
So, to make it working you need to do something like this:
.handle(bulkEndpoint, e -> e.id("actualEndpoint"))
And then in the test:
mockIntegrationContext.substituteMessageHandlerFor("actualEndpoint", mockHandler);
You also probably need to think to not have that pollerFlow to be started when you test it sine you send the message into pollingChannel manually. So, there is no conflicts with what you'd like to test. For this reason you also add a id() into your e.poller(p -> p.cron(this.cron)) and use #SpringIntegrationTest(noAutoStartup) to have it stopped before your test. I see you try noAutoStartup = {"pollerFlow"}, but this is not going to help for static flows. You indeed need to have stopped an actual endpoint in this case.
I'm using the following code (from this answer) to configure headers to be logged on WebClient requests:
ExchangeStrategies exchangeStrategies = ExchangeStrategies.withDefaults();
exchangeStrategies
.messageWriters().stream()
.filter(LoggingCodecSupport.class::isInstance)
.forEach(writer -> ((LoggingCodecSupport)writer).setEnableLoggingRequestDetails(true));
client = WebClient.builder()
.exchangeStrategies(exchangeStrategies)
This works, but causes my Jackson configuration to be lost. In my application.properties I have:
spring.jackson.default-property-inclusion=non-null
spring.jackson.deserialization.accept-empty-string-as-null-object=true
which gets overwritten by the above code. Here is my workaround:
#Autowired ObjectMapper objectMapper;
#Bean
WebClientCustomizer webClientCustomizer() {
return (WebClient.Builder builder) -> {
builder
.exchangeStrategies(createExchangeStrategiesWhichLogHeaders())
};
}
private ExchangeStrategies createExchangeStrategiesWhichLogHeaders() {
ExchangeStrategies exchangeStrategies =
ExchangeStrategies.builder()
.codecs(
clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer
.defaultCodecs()
.jackson2JsonEncoder(
new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer
.defaultCodecs()
.jackson2JsonDecoder(
new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
})
.build();
exchangeStrategies
.messageWriters()
.stream()
.filter(LoggingCodecSupport.class::isInstance)
.forEach(writer -> ((LoggingCodecSupport) writer).setEnableLoggingRequestDetails(true));
return exchangeStrategies;
}
This works, but feels a bit strange. The question is: do I need to include the jackson/objectMapper configuration like this, or is there a simpler way to avoid the Spring objectMapper configuration being overwritten?
As of Spring Boot 2.1.0, you can achieve this by enabling the following property:
spring.http.log-request-details=true
If you're on a previous Spring Boot version, you should be able to customize this without overwriting or rebuilding the whole configuration, like this:
#Configuration
static class LoggingCodecConfig {
#Bean
#Order(0)
public CodecCustomizer loggingCodecCustomizer() {
return (configurer) -> configurer.defaultCodecs()
.enableLoggingRequestDetails(true);
}
}
I am unable to get a valid json in my spring boot application, when i call the swagger /v2/api-docs endpoint.
Chrome error message:
This page contains the following errors: error on line 1 at
column 1330: xmlParseEntityRef: no name Below is a rendering of the
page up to the first error.
With the developer tools i see that the swagger.json is wrapped in xml tags,
also the content type is set to application/xhtml+xml.
The response look like this:
<Json>{"swagger":"2.0",..............</Json>
I am using Spring Boot 2.0.0.RELEASE, Spring 5.0.4.RELEASE and
for XML mapping the jackson-dataformat-xml dependency version 2.9.4.
Is there a way that i can send application/json as content type or configure the jackson dependency, that spring loads it not as the first in classpath?
Or is there any other way to fix it?
For Jackson we only used the import, no seperate configuration.
Swagger Configuration:
#SpringBootApplication (exclude = {SecurityAutoConfiguration.class})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
#ConditionalOnWebApplication
#EnableSwagger2
public static class SwaggerConfig {
#Bean
public Docket springfoxDocket() {
StringVendorExtension vendorExtension = new StringVendorExtension("x-wso2-security", Wso2Config.serialize());
List<VendorExtension> vendorExtensions = new ArrayList<>();
vendorExtensions.add(vendorExtension);
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com"))
.build()
.protocols(protocolSet())
.apiInfo(apiInfo())
.extensions(vendorExtensions)
.directModelSubstitute(LocalDateTime.class, Date.class)
.useDefaultResponseMessages(false).enableUrlTemplating(false)
;
}
private ApiInfo apiInfo() {
return new ApiInfo(
"Event",
"Creates, deletes, reads events",
"2.0",
"",
null,
null, null, Collections.emptyList());
}
private Set<String> protocolSet() {
Set<String> protocolsSet = new HashSet<>();
protocolsSet.add("http");
protocolsSet.add("https");
return protocolsSet;
}
}
}
Since you have jackson-dataformat-xml dependency spring boot will default the content type to application/xml.
If you want application\jsonas content type then configure restTemplate to do so.
Please follow the question Swagger 2 accept xml instead of json
I am using Spring Integration, Java DSL (release 1.1.3)
I have my org.springframework.integration.dsl.IntegrationFlow defined as follows
return IntegrationFlows.from(messageProducerSpec)
.handle(handler)
.handle(aggregator)
.handle(endpoint)
.get();
}
messageProducerSpec is instance of org.springframework.integration.dsl.amqp.AmqpBaseInboundChannelAdapterSpec
I would like my integration flow to consume messages from TWO separate messageProducerSpecs (two separate SimpleMessageListenerContainers, each using diffrent ConnectionFactory). How is it possible to construct integrationFlow from more than one messageProducerSpec? I see no integration component that is able to consume messages from multiple sources.
There is no reason to do that in Spring Integration.
You always can output different endpoints to the same MessageChannel.
Therefore you should have several simple IntegrationFlows for all those messageProducerSpec and finish them with the same channel, where also should be the main flow which will listen from that channel:
#Bean
public IntegrationFlow producer1() {
return IntegrationFlows.from(messageProducerSpec1)
.channel("input")
.get();
}
#Bean
public IntegrationFlow producer2() {
return IntegrationFlows.from(messageProducerSpec2)
.channel("input")
.get();
}
...
#Bean
public IntegrationFlow mainFlow() {
return IntegrationFlows.from("input")
.handle(handler)
.handle(aggregator)
.handle(endpoint)
.get();
}