Where to set API host schema in Swashbuckle? - swagger

I'm trying to figure out how to fix an issue I have going on with Swagger displaying on our STAGE and PROD servers. I believe my issue is the fact that we have Load Balancer that uses HTTPS to get to but once you get there all the sites are served over HTTP. I believe that is the reason I'm getting
Can't read from server. It may not have the appropriate access-control-origin settings.
So I found the following on Swagger and I'm thinking I just need to set my Schema to HTTP but I'm not 100% sure where to make this change? Do I add it to the SwaggerConfig.cs file?
https://swagger.io/docs/specification/2-0/api-host-and-base-path/
This is what I have in my SwaggerConfig.cs file:
public class SwaggerConfig
{
//public static void Register(HttpConfiguration config)
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.ApiKey("Api-Token")
.Description("API Key for accessing secure APIs")
.Name("Api-Token")
.In("header");
c.SingleApiVersion("v1", "WorkdayAPI");
c.IncludeXmlComments(string.Format(#"{0}\bin\WorkdayApi.XML",
System.AppDomain.CurrentDomain.BaseDirectory));
// If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option.
c.PrettyPrint();
})
.EnableSwaggerUi(c =>
{
// If your API supports ApiKey, you can override the default values.
// "apiKeyIn" can either be "query" or "header
c.EnableApiKeySupport("Api-Token", "header");
});
}
}

Related

Using dynamic URL for Spring ReactiveFeignClient

I'm using ReactiveFeignClient from Playtika
I need to use dynamic URL especially for the host part because I want to use the same interface for several services that have the same request and response formats, but different host. The URLs on each service can have different host name and prefix, but all have the same suffix.
For example:
http://localhost:3001/games/purchase
http://localhost:3002/gadgets/phone/purchase
Actually I don't know whether it has the same behavior as non-reactive feign client. I follow the suggestion on How can I change the feign URL during the runtime?.
Here's the client interface.
#ReactiveFeignClient(
name = "dummy",
configuration = TransactionClient.Configuration.class
)
public interface TransactionClient {
// #PostMapping("/purchase") // Using #PostMapping and #RequestLine both don't work
#RequestLine("POST /purchase")
Mono<PurchaseResponseDto> doPurchase(
URI baseUrl,
#Valid #RequestBody PurchaseRequestDto requestDTO
);
#RequiredArgsConstructor
class Configuration {
#Bean
public ReactiveStatusHandler reactiveStatusHandler() {
return new CustomStatusHandler();
}
}
}
And here's the auto configuration
#Configuration
public class TransactionClientServiceAutoConfiguration {
#Bean
public Contract useFeignAnnotations() {
return new Contract.Default();
}
#Bean
#LoadBalanced
public TransactionClient botRemoteClient() {
return Feign.builder().target(Target.EmptyTarget.create(TransactionClient.class));
}
}
However, I got error indicating that no service with name dummy. It's just a dummy name indeed, because the name parameter is required for #ReactiveFeignClient annotation and I want to use the interface for multiple services.
How to make dynamic url possible for #ReactiveFeignClient
From reactive feign github I found this snippet:
IcecreamServiceApi client =
WebReactiveFeign //WebClient based reactive feign
.<IcecreamServiceApi>builder()
.target(IcecreamServiceApi.class, "http://www.myUrl.com")
You can change the url by creating a new instance of the client. Found no other way. Also, I added both #PostMapping and #RequestLine("POST") to the feign interface since I couldn't get the contracts option to work.
Sharing this for posterity or until a better version comes along.

Strip prefix to remove the environment passed in URL

My frontend application attaches an environment attribute as a prefix in the URI -- I am having difficulty getting Zuul to strip this prefix.
For example, the frontend application is making a request to Zuul proxy locally http://localhost:9080/local/domains/metrs/subdomains/medicare/base-templates/. Zuul needs to strip out the environment "local" as seen in the URL and then forward to the web service located (with the environment "local" stripped) at http://localhost:9090/icews/admin/domains/metrs/subdomains/medicare/base-templates/
Unfortunately, I get a 404 error because the prefix "local" is not being stripped -- when reading the logs on the server hosting the web service, I still see the environment "local" forwarded in the call.
Here is my Zuul configuration:
ice.ws.local.url=http://localhost:9090/icews/admin
ice.ws.dev.url=http://developmentserver/icews/admin
zuul.routes.base-templates.path=/local/**/base-templates/**
zuul.routes.base-templates.url=${ice.ws.local.url}
zuul.routes.base-templates.strip-prefix=true
zuul.routes.base-templates.path=/dev/**/base-templates/**
zuul.routes.base-templates.url=${ice.ws.dev.url}
zuul.routes.base-templates.strip-prefix=true
UPDATE
I went ahead and used the approach as outlined here: https://github.com/spring-cloud/spring-cloud-netflix/issues/1893 and https://github.com/spring-cloud/spring-cloud-netflix/issues/2408
I did not use the strip-prefix in the application.properties but instead followed the approach outlined here https://github.com/spring-cloud/spring-cloud-netflix/issues/1893
Specifically for my case where I have a frontend that needs to route on different environments (each being a different microservice), I have another Zuul filter that runs in order 6 (PRE_DECORATION_FILTER_ORDER + 1):
#Component
public class PostDecorFilter extends ZuulFilter {
#Value("${zuul.PostSendResponseCustomFilter.post.disable:false}")
private boolean isDisableFilter;
#Override
public int filterOrder() {
// run this filter after zuul..PreDecorationFilter
return PRE_DECORATION_FILTER_ORDER + 1;
}
#Override
public String filterType() {
return PRE_TYPE;
}
#Override
public boolean shouldFilter() {
return !isDisableFilter;
}
/**
* Add custom response processing here
*
* #return
*/
#Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
// environment is added by frontend Angular app, e.g. dev, dev-t, qa, qa-t
String environment = RequestContext.getCurrentContext().getRequest().getRequestURI().split("/")[1];
if ( "dev".equals(environment) || "dev-t".equals(environment) || "qa".equals(environment) || "qa-t".equals(environment) ) {
// e.g. /dev/some/resource/
String requestUriWithEnvironment = RequestContext.getCurrentContext().getRequest().getRequestURI();
// e.g. /some/resource/
String requestUriWithoutEnvironment =
requestUriWithEnvironment.substring(environment.length() + 1);
context.put(REQUEST_URI_KEY, requestUriWithoutEnvironment);
}
return null;
}
I also had to use a CustomRouteLocator as outlined here: https://github.com/spring-cloud/spring-cloud-netflix/issues/2408
The reason is that the paths in application.properties are
ice.ws.local.url=http://localhost:9090/icews/admin
ice.ws.dev.url=http://developmentserver/icews/admin
zuul.routes.base-templates.path=/local/**/base-templates/**
zuul.routes.base-templates.url=${ice.ws.local.url}
zuul.routes.base-templates.path=/dev/**/base-templates/**
zuul.routes.base-templates.url=${ice.ws.dev.url}
Essentially, I was experiencing the same behavior as outlined here: "it seems that SimpleRouteLocator iterates over all routes, not in the order they're in config file and just pick the first match" https://github.com/spring-cloud/spring-cloud-netflix/issues/2408

Issue Using Custom Index.Html in Swagger / Swashbuckle for .NET Core

I am having difficulty using a custom index.html and other assets with swashbuckle. Swashbuckle/Swagger do not seem to recognizing or using them at all. I do have app.UseDefaultFiles() and app.UseStaticFiles() set. I am trying to understand what I am doing incorrectly.
I have attempted to set up my configuration somewhat similar to what is defined on the Microsoft article without success. (https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio)
I am presently using the files from the dist folder referenced in the article (https://github.com/swagger-api/swagger-ui/tree/2.x/dist) along with the custom css file provided.
My index.html file is located under /wwwroot/swagger/ui
The custom css file is located under /wwwroot/swagger/ui/css (as custom.css)
Here is my Startup.cs class.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(options =>
{
// Swagger - Format JSON
options.SerializerSettings.Formatting = Formatting.Indented;
});
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.DescribeAllEnumsAsStrings();
c.DescribeStringEnumsInCamelCase();
// c.DescribeAllParametersInCamelCase();
c.SwaggerDoc("v1",
new Info
{
Title = "My Web API - v1",
Version = "v1",
Description = "New and improved version. A simple example ASP.NET Core Web API. "
}
);
c.SwaggerDoc("v2",
new Info
{
Title = "My Web API - v2",
Version = "v2",
Description = "New and improved version. A simple example ASP.NET Core Web API. "
}
);
// Set the comments path for the Swagger JSON and UI.
var basePath = AppContext.BaseDirectory;
var xmlPath = Path.Combine(basePath, "ApiTest.xml");
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
string swaggerUIFilesPath = env.WebRootPath + "\\swagger\\ui";
if (!string.IsNullOrEmpty(swaggerUIFilesPath))
{
app.UseDefaultFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(swaggerUIFilesPath),
RequestPath = new PathString("/api-docs"),
});
}
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(c =>
{
c.RouteTemplate = "api-docs/{documentName}/swagger.json";
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
//c.ShowJsonEditor();
c.RoutePrefix = "api-docs";
c.SwaggerEndpoint("/api-docs/v1/swagger.json", "My Web API - V1 ");
c.SwaggerEndpoint("/api-docs/v2/swagger.json", "My Web API - V2 ");
c.DocumentTitle("My Web API");
});
app.UseMvc();
}
}
My ultimate objective is to be able to use something like the slate style theme available here (https://github.com/omnifone/slate-swagger-ui). For right now, I am just trying to get Swashbuckle/Swagger to use the customized files referenced in the Microsoft documentation before trying to make the other files work.
I really do NOT want to try and convert my assets to embedded resources--since there will many of them. I just want to reference a normal index.html file and be able to use all of its referenced files.
What am I doing wrong?
Relevant Software Versions
.Net Core Version: 2.0.3
Swashbuckle.AspNetCore: 1.2.0
Windows 10 Enterprise Build 1703
Visual Studio 2017 Enterprise 15.5.2
Here is the minimum action I found to be necessary to replace SwashBuckle's index.html in a .NET Core project:
Get a copy of the original index.html from here: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.SwaggerUI/index.html
Place that copy in some sub-folder of your project.
The file may have a different name, I chose:
\Resources\Swagger_Custom_index.html
Right-click that file in Solution Explorer, select 'Properties', select 'Configuration Properties' in left pane. Under 'Advanced' in right pane find entry 'Build Action' and set it to 'Embedded resource'. Click Ok.
In Startup.cs add the following line to your app.UseSwaggerUI() call:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//...
app.UseSwaggerUI(c =>
{
c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("Your.Default.Namespace.Resources.Swagger_Custom_index.html");
});
//...
}
The identifier for the file resource in the above GetManifestResourceStream method is composed of:
your default namespace (i.e. 'Your.Default.Namespace')
the sub-path of your resource (i.e. 'Resources')
the filename of your resource (i.e. 'Swagger_Custom_index.html')
All three parts are concatenated using dots (NO slashes or backslashes here).
If you don't use a sub-path but have your resource in root, just omit part 2.
For people who separate ApplicationBuilder config methods on ASP.NET Core:
If the separated method/class is static, it is not possible to call GetType() because an object reference is required.
In that case, switch GetType() to MethodBase.GetCurrentMethod().DeclaringType
c.IndexStream = () => MethodBase.GetCurrentMethod().DeclaringType.Assembly.GetManifestResourceStream("xxx.index.html");

Swagger 2.0 where to declare Basic Auth Schema

How do I define basic authentication using Swagger 2.0 annotations and have it display in swagger UI.
In the resource I have:
#ApiOperation(value = "Return list of categories", response=Category.class, responseContainer="List", httpMethod="GET", authorizations = {#Authorization(value="basicAuth")})
public Response getCategories();
I looked here:
https://github.com/swagger-api/swagger-core/wiki/Annotations#authorization-authorizationscope
And it says "Once you've declared and configured which authorization schemes you support in your API, you can use these annotation to note which authorization scheme is required on a resource or a specific operation" But I can't find anything that talks about where to declare and configure the authorization schemes.
Update:
I found code on how to declare the schema, but I still do not see any information about the authentication schema in the UI. I'm not sure what I am missing
#SwaggerDefinition
public class MyApiDefinition implements ReaderListener {
public static final String BASIC_AUTH_SCHEME = "basicAuth";
#Override
public void beforeScan(Reader reader, Swagger swagger) {
}
#Override
public void afterScan(Reader reader, Swagger swagger) {
BasicAuthDefinition basicAuthDefinition = new BasicAuthDefinition();
swagger.addSecurityDefinition(BASIC_AUTH_SCHEME, basicAuthDefinition);
}
}
Using Springfox 2.6 annotations, you must first define Basic authentication as one of the security schemes when you set up the Docket in your configuration, like this:
List<SecurityScheme> schemeList = new ArrayList<>();
schemeList.add(new BasicAuth("basicAuth"));
return new
Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo)
.securitySchemes(schemeList)
...
Then you can use the Springfox annotations in your service to set Basic Auth for the operation for which you want to require authentication:
#ApiOperation(value = "Return list of categories", response=Category.class, responseContainer="List", httpMethod="GET", authorizations = {#Authorization(value="basicAuth")})
public Response getCategories();
I struggeled with this as well. In my case i used the swagger-maven-plugin. To solve this i added this within the maven plugin:
<securityDefinitions>
<securityDefinition>
<name>basicAuth</name>
<type>basic</type>
</securityDefinition>
</securityDefinitions>
After that i was able to add it on my resource like this:
#Api(value = "My REST Interface", authorizations = {#Authorization(value="basicAuth")})
The generated json included the security element for each endpoint:
"security":[{
"basicAuth" : []
}]
And the security definition:
"securityDefinitions" : {
"basicAuth" : {
"type" : "basic"
}
}
I hope this helps others as well.
You can use the #SwaggerDefinition
http://swagger.io/customizing-your-auto-generated-swagger-definitions-in-1-5-x/
or you can configure the swagger object directly, here's an example
http://www.programcreek.com/java-api-examples/index.php?source_dir=rakam-master/rakam/src/main/java/org/rakam/WebServiceRecipe.java

Multi-tenant ServiceStack API, same deployment to respond to requests on different hostnames?

We're creating APIs using ServiceStack that are multi-tenant. We want to do DNS-based load-balancing and routing, rather than stitch things up via a reverse proxy (like nginx or haproxy).
We have Request DTOs that have a Tenant parameter. ServiceStack (and its SwaggerFeature) allow us to define custom routes, and document the DTOs such that we can read values from path, query, headers, or body.
How do we (best) wire things so that DTO properties can read values from a hostname pattern as well? So, make the Route take values from matching out of the hostname as well as the path?
We'd like to have URLs like
https://{tenant}.{DNS zone for environment}/{rest of path with tokens}
Also - out DNS zone will vary depending which environment we're in - for non-production we use (say) testing-foobar.com, and production we use real-live.com. Ideally we'd be able to support both with a single route declaration (and we prefer decorating the Request DTO instead of imperative declaration at run-time AppHost.Init).
I solved this just this week, on a existing multi-tenant system which uses .NET security principals to deal with the user permissions and tenants. I used a custom ServiceRunner to select the tenant and set up the security. Your approach to multi-tenant is different, but using a ServiceRunner still seems a valid approach.
You'd end up with something like this:
public class MyServiceRunner<T> : ServiceRunner<T>
{
public MyServiceRunner(IAppHost appHost, ActionContext actionContext)
: base(appHost, actionContext)
{}
public override void BeforeEachRequest(IRequestContext requestContext, T request)
{
// Set backend authentication before the requests are processed.
if(request instanceof ITenantRequest)
{
Uri uri = new Uri(requestContext.AbsoluteUri);
string tenant = uri.Host; // Or whatever logic you need...
((ITenantRequest).Tenant = tenant;
}
}
}
public class MyAppHost : AppHostBase
{
public MyAppHost() : base("My Web Services", typeof(MyService).Assembly) { }
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new MyServiceRunner<TRequest>(this, actionContext);
}
public override void Configure(Container container)
{
...
}
}
Perhaps the Requests filtering approach is somehow better, but this does the job for us.

Resources