1

Investigating Springfox and Swagger UI, but I am facing an issue. I am using the Spring Boot REST example project as the foundation for my PoC. I'm running JDK 8 and the project leverages Gradle.

First, here are the file contents for the project:

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'

jar {
    baseName = 'gs-rest-service'
    version =  '0.1.0'
}

repositories {
    mavenCentral()
    jcenter()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("io.springfox:springfox-swagger2:2.2.2")
    compile("io.springfox:springfox-swagger-ui:2.2.2")
    testCompile("junit:junit")
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.3'
}

GreetingController.java

package hello;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(),
                            String.format(template, name));
    }
}

Greeting.java

package hello;

public class Greeting {

    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}

Application.java

package hello;

import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;

import java.time.LocalDate;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.async.DeferredResult;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import com.fasterxml.classmate.TypeResolver;

@SpringBootApplication
@EnableSwagger2
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Autowired
    private TypeResolver typeResolver;

    @Bean
    public Docket greetingApi() {
        return new Docket(DocumentationType.SPRING_WEB)
            .select()
            .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.any())
            .build()
            .pathMapping("/")
            .directModelSubstitute(LocalDate.class, String.class)
            .genericModelSubstitutes(ResponseEntity.class)
            .alternateTypeRules(newRule(typeResolver.resolve(DeferredResult.class,
                typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
                typeResolver.resolve(WildcardType.class)))
            .useDefaultResponseMessages(false)
            .globalResponseMessage(RequestMethod.GET,
                newArrayList(new ResponseMessageBuilder()
                    .code(500)
                    .message("500 message")
                    .responseModel(new ModelRef("Error"))
                    .build()))
            .enableUrlTemplating(true);
    }

}

Here is the issue I am facing. When I build and run the application, I can successfully navigate to the Swagger UI page (http://localhost:8080/swagger-ui.html). When I expand greeting-controller, I see the different methods and expand "get /greeting{?name}". The Get section has the following content:

Response Class (Status 200)

Model

{
  "content": "string",
  "id": 0
}

Response Content Type: */*

Parameters
parameter = name, value = World, parameter type = query, data type = string

When I click the "Try It Out" button, I see the following:

curl = curl -X GET --header "Accept: */*" "http://localhost:8080/greeting{?name}?name=World"

request url = http://localhost:8080/greeting{?name}?name=World

repsonse body = {
  "timestamp": 1446418006199,
  "status": 404,
  "error": "Not Found",
  "message": "No message available",
  "path": "/greeting%7B"
}

response code = 404

response headers = {
  "server": "Apache-Coyote/1.1",
  "content-type": "application/json;charset=UTF-8",
  "transfer-encoding": "chunked",
  "date": "Sun, 01 Nov 2015 22:46:46 GMT"
}

At first glance, it looks like for some reason that Springfox/Swagger is not correctly replacing the placeholder for {?name}. My question is, how do I configure it to do so, if that is in fact the issue, so that I can successfully test out the service from the Swagger UI page?

1
  • yep as you see in the response body, it is trying to open the url /greeting%7B with %7B being the { just before ?name}. Try using enableUrlTemplating(false) when you create the Docket (you are using a RequestParam not PathVariable) Commented Nov 1, 2015 at 23:07

1 Answer 1

5

In your Application class changing the enableUrlTemplating to false will fix your problem.

@Bean
public Docket greetingApi() {
    return new Docket(DocumentationType.SPRING_WEB)
        //...
        .enableUrlTemplating(false);
}

Just a little bit of background on that flag. That flag is to support RFC 6570 without which operations that differ only by query string parameters will not show up correctly per spec. In the next iteration of the swagger spec there are plans to address that issue. That is the reason for enableUrlTemplating to be marked as an incubating feature.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.