Monday, September 30, 2013

Swagger: make developers love working with your REST API

As JAX-RS API is evolving, with version 2.0 released earlier this year under JSR-339 umbrella, it's becoming even more easy to create REST services using excellent Java platform.

But with great simplicity comes great responsibility: documenting all these APIs so other developers could quickly understand how to use them. Unfortunately, in this area developers are on their own: the JSR-339 doesn't help much. For sure, it would be just awesome to generate verbose and easy to follow documentation from source code, and not asking someone to write it along the development process. Sounds unreal, right? In certain extent, it really is, but help is coming in a form of Swagger.

Essentially, Swagger does a simple but very powerful thing: with a bit of additional annotations it generates the REST API descriptions (HTTP methods, path / query / form parameters, responses, HTTP error codes, ...) and even provides a simple web UI to play with REST calls to your APIs (not to mention that all this metadata is available over REST as well).

Before digging into implementation details, let's take a quick look what Swagger is from API consumer prospective. Assume you have developed a great REST service to manage people. As a good citizen, this REST service is feature-complete and provides following functionality:

  • lists all people (GET)
  • looks up person by e-mail (GET)
  • adds new person (POST)
  • updates existing person (PUT)
  • and finally removes person (DELETE)
Here is the same API from Swagger's perspective:

It looks quite pretty. Let's do more and call our REST service from Swagger UI, here this awesome framework really shines. The most complicated use-case is adding new person (POST) so this one will be looked closely.

As you can see on the snapshot above, every piece of REST service call is there:

  • description of the service
  • relative context path
  • parameters (form / path / query), required or optional
  • HTTP status codes: 201 CREATED and 409 CONFLICT
  • ready to go Try it out! to call REST service immediately (with out-of-the box parameters validation)

To complete the demo part, let me show yet another example, where REST resource is being involved (in our case, it's a simple class Person). Swagger is able to provide its properties and meaningful description together with expected response content type(s).

Looks nice! Moving on to the next part, it's all about implementation details. Swagger supports seamless integration with JAX-RS services, with just couple of additional annotations required on top of existing ones. Firstly, every single JAX-RS service which supposed to be documented should be annotated with @Api annotation, in our case:

@Path( "/people" ) 
@Api( value = "/people", description = "Manage people" )
public class PeopleRestService {
    // ...
}

Next, the same approach is applicable to REST service operations: every method which supposed to be documented should be annotated with @ApiOperation annotation, and optionally with @ApiResponses/@ApiResponse. If it accepts parameters, those should be annotated with @ApiParam annotation. Couple of examples here:

@Produces( { MediaType.APPLICATION_JSON } )
@GET
@ApiOperation( 
    value = "List all people", 
    notes = "List all people using paging", 
    response = Person.class, 
    responseContainer = "List"
)
public Collection< Person > getPeople(  
        @ApiParam( value = "Page to fetch", required = true ) 
        @QueryParam( "page") @DefaultValue( "1" ) final int page ) {
    // ...
}

And another one:

@Produces( { MediaType.APPLICATION_JSON } )
@Path( "/{email}" )
@GET
@ApiOperation( 
    value = "Find person by e-mail", 
    notes = "Find person by e-mail", 
    response = Person.class 
)
@ApiResponses( {
    @ApiResponse( code = 404, message = "Person with such e-mail doesn't exists" )    
} )
public Person getPeople( 
        @ApiParam( value = "E-Mail address to lookup for", required = true ) 
        @PathParam( "email" ) final String email ) {
    // ...
}

REST resource classes (or model classes) require special annotations: @ApiModel and @ApiModelProperty. Here is how our Person class looks like:

@ApiModel( value = "Person", description = "Person resource representation" )
public class Person {
    @ApiModelProperty( value = "Person's first name", required = true ) 
    private String email;
    @ApiModelProperty( value = "Person's e-mail address", required = true ) 
    private String firstName;
    @ApiModelProperty( value = "Person's last name", required = true ) 
    private String lastName;

    // ...
}

The last steps is to plug in Swagger into JAX-RS application. The example I have developed uses Spring Framework, Apache CXF, Swagger UI and embedded Jetty (complete project is available on Github). Integrating Swagger is a matter of adding configuration bean (swaggerConfig), one additional JAX-RS service (apiListingResourceJson) and two JAX-RS providers (resourceListingProvider and apiDeclarationProvider).

package com.example.config;

import java.util.Arrays;

import javax.ws.rs.ext.RuntimeDelegate;

import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;

import com.example.resource.Person;
import com.example.rs.JaxRsApiApplication;
import com.example.rs.PeopleRestService;
import com.example.services.PeopleService;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import com.wordnik.swagger.jaxrs.config.BeanConfig;
import com.wordnik.swagger.jaxrs.listing.ApiDeclarationProvider;
import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON;
import com.wordnik.swagger.jaxrs.listing.ResourceListingProvider;

@Configuration
public class AppConfig {
    public static final String SERVER_PORT = "server.port";
    public static final String SERVER_HOST = "server.host";
    public static final String CONTEXT_PATH = "context.path";  
 
    @Bean( destroyMethod = "shutdown" )
    public SpringBus cxf() {
        return new SpringBus();
    }
 
    @Bean @DependsOn( "cxf" )
    public Server jaxRsServer() {
        JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint( jaxRsApiApplication(), JAXRSServerFactoryBean.class );
        factory.setServiceBeans( Arrays.< Object >asList( peopleRestService(), apiListingResourceJson() ) );
        factory.setAddress( factory.getAddress() );
        factory.setProviders( Arrays.< Object >asList( jsonProvider(), resourceListingProvider(), apiDeclarationProvider() ) );
        return factory.create();
    }
 
    @Bean @Autowired
    public BeanConfig swaggerConfig( Environment environment ) {
        final BeanConfig config = new BeanConfig();

        config.setVersion( "1.0.0" );
        config.setScan( true );
        config.setResourcePackage( Person.class.getPackage().getName() );
        config.setBasePath( 
            String.format( "http://%s:%s/%s%s",
                environment.getProperty( SERVER_HOST ),
                environment.getProperty( SERVER_PORT ),
                environment.getProperty( CONTEXT_PATH ),
                jaxRsServer().getEndpoint().getEndpointInfo().getAddress() 
            ) 
        );
  
        return config;
    }

    @Bean
    public ApiDeclarationProvider apiDeclarationProvider() {
        return new ApiDeclarationProvider();
    }
 
    @Bean
    public ApiListingResourceJSON apiListingResourceJson() {
        return new ApiListingResourceJSON();
    }
 
    @Bean
    public ResourceListingProvider resourceListingProvider() {
        return new ResourceListingProvider();
    }
 
    @Bean 
    public JaxRsApiApplication jaxRsApiApplication() {
        return new JaxRsApiApplication();
    }
 
    @Bean 
    public PeopleRestService peopleRestService() {
        return new PeopleRestService();
    }
   
    // ... 
}

In order to get rid of any possible hard-coded configuration, all parameters are passed through named properties (SERVER_PORT, SERVER_HOST and CONTEXT_PATH). Swagger exposes additional REST endpoint to provide API documentation, in our case it is accessible at: http://localhost:8080/rest/api/api-docs. It is used by Swagger UI which itself is embedded into final JAR archive and served by Jetty as static web resource.

The final piece of the puzzle is to start embedded Jetty container which glues all those parts together and is encapsulated into Starter class:

package com.example;

import org.apache.cxf.transport.servlet.CXFServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

import com.example.config.AppConfig;

public class Starter {
    private static final int SERVER_PORT = 8080;
    private static final String CONTEXT_PATH = "rest";
 
    public static void main( final String[] args ) throws Exception {
        Resource.setDefaultUseCaches( false );
  
        final Server server = new Server( SERVER_PORT );  
        System.setProperty( AppConfig.SERVER_PORT, Integer.toString( SERVER_PORT ) );
        System.setProperty( AppConfig.SERVER_HOST, "localhost" );
        System.setProperty( AppConfig.CONTEXT_PATH, CONTEXT_PATH );    

        // Configuring Apache CXF servlet and Spring listener  
        final ServletHolder servletHolder = new ServletHolder( new CXFServlet() );      
        final ServletContextHandler context = new ServletContextHandler();   
        context.setContextPath( "/" );
        context.addServlet( servletHolder, "/" + CONTEXT_PATH + "/*" );     
        context.addEventListener( new ContextLoaderListener() ); 
     
        context.setInitParameter( "contextClass", AnnotationConfigWebApplicationContext.class.getName() );
        context.setInitParameter( "contextConfigLocation", AppConfig.class.getName() );
   
        // Configuring Swagger as static web resource
        final ServletHolder swaggerHolder = new ServletHolder( new DefaultServlet() );
        final ServletContextHandler swagger = new ServletContextHandler();
        swagger.setContextPath( "/swagger" );
        swagger.addServlet( swaggerHolder, "/*" );
        swagger.setResourceBase( new ClassPathResource( "/webapp" ).getURI().toString() );

        final HandlerList handlers = new HandlerList();
        handlers.addHandler( context );
        handlers.addHandler( swagger );
   
        server.setHandler( handlers );
        server.start();
        server.join(); 
    }
}

Couple of comments make thing a bit more clear: our JAX-RS services will be available under /rest/* context path while Swagger UI is available under /swagger context path. The one important note concerning Resource.setDefaultUseCaches( false ): because we are serving static web content from JAR file, we have to set this property to false as workaround for this bug.

Now, let's build and run our JAX-RS application by typing:

mvn clean package
java -jar target/jax-rs-2.0-swagger-0.0.1-SNAPSHOT.jar

In a second, Swagger UI should be available in your browser at: http://localhost:8080/swagger/

As a final note, there are a lot more to say about Swagger but I hope this simple example shows the way to make our REST services self-documented and easily consumable with minimal efforts. Many thanks to Wordnik team for that.

Source code is available on Github.

47 comments:

Orest Ivasiv said...

Nice post. Really useful.

Do you have any experience using swagger with REST+security (OAuth, basic, digest, etc.) Does swagger play nice with secured REST APi?

Stanislav Doktorovich said...

Great post and finding!

Really useful documentation layer on top of REST layer.

Couple of questions:
* is it possible to export all generated documentation to the company official portal? Or to access the documentation the app should be up and running.
* Does this library knows something about API versioning?

Andriy Redko said...

Hi Orest,

Thank you very much for your comments. Swagger does support the security in some extent as covered in this manual: https://github.com/wordnik/swagger-core/wiki/authorizations (personally, I haven't used this feature yet but it's a very important use case to cover).

Thank you.

Best Regards,
Andriy Redko

Andriy Redko said...

Hi Stan,

Thanks a lot for your comments. Yes, Swagger does have a Maven plugin and can generate API documentation at build time (https://github.com/kongchen/swagger-maven-plugin). With respect to API versioning, Swaggers support API version (https://github.com/wordnik/swagger-core/wiki/API-Declaration) though its usage is somewhat limited and is up to developer.

Thank you.

Best Regards,
Andriy Redko

atg roxx said...

I too tried to use the swagger with camel cxfrx, but was not able to do so.

Have you tried to integrate the Camel cxfrs with Swagger.

If you have could you kindly post the steps ..

Thanks in advance for your help.

Andriy Redko said...

Hi!

Thanks a lot for your comments. Unfortunately, I do not have hands-on experience with Apache Camel and cannot answer your question. However, if you could outline some details about the problem you are having, I might suggest to try something out.

Thank you.

Best Regards,
Andriy Redko

Rajeev Sathyanarayana said...

Excellent post.Really helped me to get started with Swagger.
For the TryitOut button on the SwaggerUI, how do it enable it for https calls?

Thanks,
Rajeev S

Andriy Redko said...

Hi Rajeev,

Thank you very much for your comment. To answer your question, it is quite easy, in class AppConfig, line 54, replace please String.format( "http://%s:%s/%s%s", with String.format( "https://%s:%s/%s%s", (or better just parametrize the protocol as well). Hope it helps!

Thank you!

Best Regards,
Andriy Redko

dhiru said...

Very nice and comprehensive article on using Swagger.

Thanks

TheCollegeChef said...

Nice post. I have no experience with creating/deploying/documenting an API. I was wondering if you could help me with intefrating swagger into an Eclipse project that has already been started for me by my internship. They have created a REST Api and they want us to use swagger for documentation/annotation. I am completely lost at what to do. I have done some research, but can't seem to find anything on using it with an eclipse project.

Andriy Redko said...

Hi!

Thanks a lot for your comment. Yeah, sure, I would be happy to help you out. Would you mind to share your project in GitHub so we can work together on Swagger integration?

Thank you!

Best Regards,
Andriy Redko

TheCollegeChef said...

Unfortunately I cannot do that. It belongs to a company I am working at for an internship this summer. Im just trying to figure out what files i need to put into my project and where. Even some sort of direction would be appreciated.

Andriy Redko said...

Hi!

Gotcha, no problem. Let me try to guide you through than. So Swagger consist of two parts (as for this blog post concerns): API description and UI (optional). API description part in general depends on what kind of REST framework you are using, could you please hint me with that? Swagger UI is simple, self-contained and could be just pulled from GitHub: https://github.com/swagger-api/swagger-ui. It is a static set of web resources. So I think once we figure out the API part, we can glue everything together. Waiting for your response!

Best Regards,
Andriy Redko

TheCollegeChef said...

Yes, I have the swagger files downloaded from GitHub. We are using Jersey and it looks like Spring as well. This is the first time I have ever worked on a web service / servlet, or API so this is all new to me, kinda confused as to what Jersey and Spring does exactly but I can certainly read up on that on my own. If it helps at all I'm using Eclipse to work on the project and it is a DynamicWeb project. The API has already been written, me and my partner just have to go through and annotate the stuff so that we can use the UI for deployment.

Pragnya said...

hello Andi,
great article , more over it is exactly what i have been looking for.
Ran into a jetty issue while running it. so updated the jetty version to 9.1 to get it working.
Now when I try to look for localhost:8080/swagger i am getting 404. My rest API is just coming fine and the document also. But not sure how to resolve the 404.

Andriy Redko said...

Hi Pragnya,

Thank you very much for your comment. Yes, you are right, I updated Jetty to 9.1+ and there is 404 returned. It seems like the handlers are managed a bit differently by Jetty now, could you please just change the order of handlers in Starter.java like that, instead of:

handlers.addHandler( context );
handlers.addHandler( swagger );


Please change the order to:

handlers.addHandler( swagger );
handlers.addHandler( context );


It should be fine with that. Please let me know if it does not work for you.
Thank you!

Best Regards,
Andriy Redko

Andriy Redko said...

I will try to prepare the Jersey + Swagger example and push it on GitHub so you could use it as a guide. Will keep you posted!

Thanks.

Best Regards,
Andriy Redko

TheCollegeChef said...

Andriy,

Thank you, that would be a big help.

Andriy Redko said...

Hi!

I have created a sample, Jersey 2.0 based project branch for you on GitHub: https://github.com/reta/jax-rs-2.0-swagger/tree/jersey-2.0

It is a little bit hard to figure out how exactly you run your application but I hope it will give you a basic idea. The key class is JaxRsApiApplication which extends Jersey's ResourceConfig class and register appropriate Swagger (and JAX-RS beans). Once you run the Starter class in Eclipse, you should be able to open Swagger UI on http://localhost:8080/swagger. Please give it a try and let me know it was helpful or not.

Thank you!

Best Regards,
Andriy Redko

TheCollegeChef said...

Andriy,

Thank you I will take a look at it over the next couple of days and get back to you. I appreciate you taking the time to create it.

TheCollegeChef said...

Andriy,

Sorry it has taken me a while to get back to you. I have sucessfuly deployed my swagger project and documented two API's. Horray! I was wondering if you know anything about using Basic Auth with swagger-UI? Currently when I hit the "try it out!" button when testing the api I have to login to access the API, I am trying to set it up so that a header is sent so that I don't have to log in everytime. I have looked at the documentation, but everything I have tried so far has been unsucessful.

Thanks again!

Andriy Redko said...

Hi!

Glad to hear you pulled it off! I haven't used Basic Auth with Swagger UI yet, but it seems like it could be added manually (some JavaScript coding required). There is an issue to watch: https://github.com/swagger-api/swagger-ui/issues/764.

Thank you.

Best Regards,
Andriy Redko

Hj said...

Hi,
I'm new to swagger and want to learn it.But i dont know how to get started with it.Please help me with it.
Im interested in swagger with jersey at this link https://github.com/reta/jax-rs-2.0-swagger/tree/jersey-2.0.

If i pull the code form github into eclipse what do i have to do to get it running
Your step by step help is highly appreciated.
Thanks!

Andriy Redko said...

Hi,

Thanks a lot for your interest. It is very easy: once you pull the code to Eclipse, you just right-click on a project, select [Run As] / [Java Application] and select Starter class. That's it, once run, point your browser at http://localhost:8080/swagger/

Thank you!

Best Regards,
Andriy Redko

Hj said...

Hi,
Thanks alot Andriy for your help.When i right click the project i can only see the Run configurations.. option.There is no Run as option.

To be sure i followed correct steps to pull the code to Eclipse,i'm listing the steps i followed followed:

File-> import-> projects from git-> clone URI-> source git repository (under URI pasted the HTTPS clone URL from github)-> branch selection (selected all listed branches ->Local destination..(under directory selected my workspace path) ->import as general project-> project name (jax-rs-2.0-swagger)

Thanks.

Andriy Redko said...

Hi,

Got it, you have two choices here:
- right-click on the project, select [Configure], then [Convert To Maven Project]
- reimport the project as [Check out Maven Projects from SCM], using the same Github repository

Whatever is easier for you.
Thanks.

Best Regards,
Andriy Redko

Hj said...

Hi;

Thanks Andriy for the useful post.I opted for the first choice and its working.

Now i would like to integrate swagger(at https://github.com/reta/jax-rs-2.0-swagger/tree/jersey-2.0)

into my existing jersey2 rest application.For that how do i get started?

Thanks.

Andriy Redko said...

Hi,

You basically need two update your Jersey configuration (see please JaxRsApiApplication class). It is very hard to give you the right advice because there could quite a few ways to create Jersey applications and integrate them with Swagger. If you could share your project @ Github, I would be happy to help you out.

Thank you!

Best Regards,
Andriy Redko

Hj said...

Hi,

I have shared the project on Github at the below URL:
https://github.com/HJ786/TutorialsDesk.git

Thnaks!

Andriy Redko said...

Hi,

Submitted PR: https://github.com/HJ786/TutorialsDesk/pull/1
Please take a look, it is the easiest way to start playing with Swagger and Swagger UI.
Just start with adding more annotations to your Tasks REST service!
Thanks!

Best Regards,
Andriy Redko

Hj said...

Hi,

I did a merge online then pulled the code from github into my project in eclipse.Swagger is still not running at my end on the given URL http://localhost:8080/TutorialsDesk.Rest.CRUDWebServices/swagger-ui/

Thanks.

Hj said...

Hi,
To elaborate my problem i would like to mention that in Eclipse in the git repositories window when i right click the project(TutorialsDesk.Rest.CRUDWebServices) and select pull..it says Nothing to update-eveyrthing uptodate.

In the project explorer,when i right click the project(TutorialsDesk.Rest.CRUDWebServices)and select Run As-> Run on Server(select the server) i cannot see swagger changes integrated to it.

Neither can i see the newly added files nor any changes made to my existing files in my project(TutorialsDesk.Rest.CRUDWebServices)under project explorer or git repositories window.

Your help in this reagrd is highly appreciated.

Thanks.

Andriy Redko said...

Hi,

Please check your repo commit history, https://github.com/HJ786/TutorialsDesk/commits/master. I am not sure exactly what you are trying to do but you merged and reverted the merge at least 2 times. Right now there is no changes from PR in your repo. You may reset your HEAD to this commit: https://github.com/HJ786/TutorialsDesk/commit/40cebd3f60121a734c438e1c6258efe21d21a7d8 (please consult Github documentation how to do that, it is very good).

Thanks.

Best Regards,
Andriy Redko

Hj said...

Hi,
I reset the HEAD to the commit 40cebd3f60121a734c438e1c6258efe21d21a7d.Now i can view all changes(swagger integrated) made to my Tasks REST service.

Thanks alot for your help uptil now!

Now i will start working on adding more annotations to my Tasks REST service.

Thanks.

Andriy Redko said...

Hi,

Awesome, good luck!
Thanks for you comments.

Best Regards,
Andriy Redko

Jess said...

Hi!

I apologize in advance because I'm not too familiar with creating, working with or documenting an API. Is it possible to use Swagger UI to document a REST API that isn't dependent on a framework? The API I'm trying to document is written in C and according to my coworkers was not developed using a framework. I've done research but I can't seem to figure out how or if it's possible to use Swagger UI to document it. Any help would really be appreciated!

Andriy Redko said...

Hi Jess,

Thank you very much for the comment. Essentially, Swagger is just an API description specification (https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md). If your REST API, written in any language, is able to return its own description according to specification, Swagger UI will be happy to show it off! On the other side, I am not aware of any implementation which could generate the API documentation in C (I would suspect it may be not possible as C has no reflection support). However, the one way to dial with that is by manually creating swagger.json file with API description and returning it from your C API (f.e. /api-specs/swagger.json), that will require some work but I do not see another option at the moment. If your API is not changing, that would be one time thing. I hope it will help.

Thank you.

Best Regards,
Andriy Redko

Jess said...

Hi Andriy,

Thanks for the reply and sorry for the slow response! Could you clarify why C having no reflection support would disallow it from generating the documentation?

Thanks!

Andriy Redko said...

Hi Jess,

My apologies for giving you a confusing answer. I would like to disambiguate it a bit: reflection simplifies a lot of things and as such makes certain use cases much simpler to implement. By no means I intended to let you think that "no reflection support would disallow it from generating the documentation", probably more routine work is required.

Thank you.

Best Regards,
Andriy Redko

Hj said...

Hi Andriy,
I tried adding annotations to my application.But i'm getting the blow errors for each operation.

Response Body
Apache Tomcat/7.0.61 - Error report HTTP Status 403 -


Response Code
403

Response Headers
{
"date": "Wed, 05 Aug 2015 21:08:46 GMT",
"server": "Apache-Coyote/1.1",
"content-length": "961",
"content-language": "en",
"content-type": "text/html;charset=utf-8"
}

Please tell me where am i going wrong?
Thanks!

Hj said...

*below

Andriy Redko said...

Hi!

403 usually means forbidden access (authorization).
Did you configure security for your application?
Thanks.

Best Regards,
Andriy Redko

Hj said...

Hi,

I don't think i did that.I would appreciate if you guide me how to do it.

Thanks!

Hj said...

Hi,

In some operations Response Code is 404.

Thanks!

Hj said...
This comment has been removed by the author.
Hj said...

Hi,
Now when i start server i'm getting below error on console

SEVERE: Servlet [Jersey2Config] in web application [/TutorialsDesk.Rest.CRUDWebServices] threw load() exception
java.lang.ClassNotFoundException: io.swagger.jaxrs.config.DefaultJaxrsConfig
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1720)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1571)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:506)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:488)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:115)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1148)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1087)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5266)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5554)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1575)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1565)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

In the browser when i type the swagger url id says
Can't read swagger JSON from http://localhost:8080/TutorialsDesk.Rest.CRUDWebServices/rest/swagger.json

Thanks!

Andriy Redko said...

Hi!

Let us move this discussion to your github repo.
Thanks.

Best Regards,
Andriy Redko