Swashbuckle Swagger url from http to https .NET - asp.net-mvc

My API is an HTTP API (not HTTPS) developped in ASP.NET.
Nevertheless, when deployed in the DMZ, there is something, somewhere on the network, not under my responsability, that only accept HTTPS and act as a reverse proxy to my HTTP API.
This leads to the swagger UI misbahaving.
The URL to access the swagger UI is "https://external_public_name/api/swagger/ui/index#/" but he one in the text field in the green ribbon is "http://external_public_name/api/swagger/docs/v1".
Therefore, the page is blank, with the error at the console
Mixed Content: The page at
'https://external_public_name/api/swagger/ui/index' was loaded over
HTTPS, but requested an insecure XMLHttpRequest endpoint
'http://external_public_name/api/swagger/docs/v1'. This request has
been blocked; the content must be served over HTTPS.
Replacing, manually the URL in the textbox with HTTPS is working fine.
Where should I change something to update that URL (maybe at runtime)?
P.S.: I already had to rewrite the SwaggerConfig.cs as SwaggerConfig.vb to be able to use it.
Imports System.IO
Imports System.Reflection
Imports System.Web.Http
Imports System.Web.Http.Description
Imports Swashbuckle.Application
Imports Swashbuckle.Swagger
<Assembly: PreApplicationStartMethod(GetType(SwaggerConfig), "Register")>
Public Class SwaggerConfig
Public Class KnownSubTypesDocumentFilter
Implements IDocumentFilter
Public Sub Apply(swaggerDoc As SwaggerDocument, schemaRegistry As SchemaRegistry, apiExplorer As IApiExplorer) Implements IDocumentFilter.Apply
schemaRegistry.GetOrRegister(GetType(BookController.BookList))
End Sub
End Class
Public Shared Sub Register()
Dim thisAssembly = GetType(SwaggerConfig).Assembly
GlobalConfiguration.Configuration.EnableSwagger(
Function(c)
c.Schemes({"https"})
c.SingleApiVersion("v1", "api")
c.DocumentFilter(Of KnownSubTypesDocumentFilter)()
Return c
End Function).EnableSwaggerUi(
Function(ui)
ui.DocumentTitle("Skippy's API")
ui.DocExpansion(DocExpansion.List)
ui.SupportedSubmitMethods()
Return ui
End Function)
End Sub
End Class

Related

Springfox Swagger UI behind reverse proxy

I have configured a Spring Boot application with Swagger API documentation and configured Swagger UI.
I also run my backend application behind a reverse proxy that maps all requests from host:port/api to backend_host:port/, when running locally on localhost I map localhost:8082/api. In production a similar mapping is applied.
When I open the Swagger UI from localhost:8082/api/swagger-ui.html it shows the following lines below the title:
[ Base URL: localhost:8080 ]
http://localhost:8082/api/v2/api-docs
When I invoke any rest operation swagger always tries to perform it against localhost:8080 which then fails due to the same origin policy.
I am aware of using pathProvider but it only affects the base URL's path part, not the domain and port. So I can only use it to change the base URL to localhost:8080/api but I would need it to change to localhost:8082/api. Is there a way to set the host dynamically to the current host and port that is active in the browser?
.pathProvider (new RelativePathProvider (servletContext) {
#Override
public String getApplicationBasePath() {
return "/api";
}
})
In my case with a spring-boot:2.2.6 application with springdoc-openapi-ui:1.3.0 (that also has embedded the swagger UI), I solved the proxy problem setting the server URL in this way:
#Configuration
public class OpenApiConfig {
#Value("${server}")
private String url;
#Bean
#Profile("prod")
public OpenAPI customConfiguration() {
return new OpenAPI()
.servers(Collections
.singletonList(new Server().url(url))) //real public URL
.components(new Components())
.info(new Info().title("Dummy API Docs")
.description("Dummy REST API documentation"));
}
}
This change is reflected in the contract (https://real-domain.com/api-docs):
And in the Swagger UI (https://real-domain.com/swagger-ui/index.html?configUrl=/api-docs/swagger-config)
I think in your case you need to configure your proxy to set HTTP Header
(which will be forwarded to your target backend)
to "notify" Swagger endpoints to return custom URL in /apidocs endpoint.
Please configure proxy to set header X-Forwarded-Host to value from Host request header
Explanation:
In your browser when you will visit a url eg. https://my.domain.com/api/swagger-ui.html
the proxy should create and forward header X-Forwarded-Host: my.endpoint.com
to your backend localhost:8082/api/swagger-ui.html
-> so the Swagger /apidocs enpoint could take this header into consideration in response JSON.
My own case - in Microsoft IIS:
I needed to configure Microsoft IIS to serve Swagger IU from Apache Tomcat on 8080 port on HTTPS domain,
so I needed to have following configuration:
<serverVariables>
<set name="HTTP_X_FORWARDED_HOST" value=“{HTTP_HOST}” />
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
</serverVariables>
JuanMorenos answer helped me, however, if anyone is using Springboot and annotations with OpenAPI you can define the URL in your main class
#SpringBootApplication
#OpenAPIDefinition(info = #Info(
version = "2.0",
title = "Swagger - My application",
description = "A description of the application"),
servers = #Server(
url = "http://yourhost:yourport",
description = "A description of the Server "
))

Hybris + swagger integration swagger-ui.html UnknownResourceError

I'm trying to integrate swagger in MYcommercewebservices.
I read post and done all steps listed on it, but still having this error.
https://localhost:9002/mycommercewebservices/v2/v2/api-docs working fine. https://localhost:9002/mycommercewebservices/v2/swagger-ui.html - return UnknownResourceError.
Furthermore - if I navigate to https://localhost:9002/mycommercewebservices/swagger-ui.html (without 'v2') it'll show me this message (javascript alert):
Unable to infer base URL. This is common when using dynamic servlet
registration or when the API is behind an API Gateway. The base URL is
the root of where all the swagger resources are served. For e.g. if
the API is available at http://example.org/api/v2/api-docs then the
base URL is http://example.org/api/. Please enter the location
manually:
I found this controller, and probably part of the problem was in it because it was throwing an exception when I navigated to https://localhost:9002/mycommercewebservices/v2/swagger-ui.html
#Controller
public class DefaultController
{
#RequestMapping
public void defaultRequest(final HttpServletRequest request)
{
throw new UnknownResourceException("There is no resource for path " + YSanitizer.sanitize(request.getRequestURI()));
}
}
Now I disabled controller, but still having the same exception, but now it's in json format instead of .xml.
Thank you!
The main problem was in DefaultController (in MYcommercewebservices)
#Controller
public class DefaultController
{
#RequestMapping
public void defaultRequest(final HttpServletRequest request)
{
throw new UnknownResourceException("There is no resource for path " + YSanitizer.sanitize(request.getRequestURI()));
}
}
It was catching my request and throwing the exception.
When I disabled this controller, I continued to receive an exception, but now it was in json format(before it was in xml).
Than I added this to springmvc-v2-servlet.xml
<mvc:default-servlet-handler/>
<mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/"/>
<mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/>
Now UI works fine!
Also there were another manipulation before all this, but you can find them in hybris experts(quite big post).

How to use swagger with OAuth API?

Is it possible to use swagger as a documentation/testing tool for APIs that use OAuth2? I don't see anything on the swagger site (or anywhere else for that matter). Every usage I've seen uses either an API key, HTTP basic, or cookies.
I have been working along the same lines. Swagger will accept any header or URL defined api key or token. Adding a validation helper to the api and app is a standard approach.
Oauth does require a HTML review and or login to start the handshake aouth process. This means that a swagger api will need to support a web interface for a standard login and scope acceptance. Rolling oauth into swagger results in a few logic loops, which long term are not easy to support.
A different approach we are exploring is the option to let the api handle and store access tokens for a number of different oauth providers; GitHub, twitter and Facebook. This might result in login loops as well.
late to the party here but oAuth support is now in 1.3.0-RC1 of swagger-core. The javascript library which can support oAuth was released yesterday in swagger-js. Finally, the swagger-ui is in develop phase, and will soon have a oAuth implicit and server flow.
the blog´s post http://developers-blog.helloreverb.com/enabling-oauth-with-swagger/ cited by #fehguy shows an example of java code to include the authorization data in json generated by swagger, however my question was where it should be included with app with Spring, JAXRS and CXF. I didn´t find it in CXF + JAXRS Sample :https://github.com/swagger-api/swagger-core/tree/master/samples/java-jaxrs-cxf
However, looking for a bit more and gotcha !
https://github.com/swagger-api/swagger-core/blob/master/samples/java-jersey-spring/src/main/resources/beans-asset-ws.xml
Is necessary include a Bean with a class called Bootstrap (extends HttpServlet) and a static block !
Opinion: Maybe it would be more “spring-friendly” loaded from annotations by SwaggerConfig Scanner in Rest class instead a static block in a servlet.
#Configuration
public class SwaggerConfiguration {
#Bean
#DependsOn("jaxRsServer") //org.apache.cxf.endpoint.Server bean
public ServletContextInitializer initializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
BeanConfig scanner = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = scanner.getSwagger();
servletContext.setAttribute("swagger", swagger);
}
};
}
#Bean
public Feature swaggerFeature() {
XSwagger2Feature feature = new XSwagger2Feature();
return feature;
}
#Bean
public FilterRegistrationBean swaggerApiFilter() {
ApiOriginFilter filter = new ApiOriginFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registrationBean;
}
public static class XSwagger2Feature extends Swagger2Feature {
#Override
protected void addSwaggerResource(Server server) {
super.addSwaggerResource(server);
BeanConfig scanner = (BeanConfig) ScannerFactory.getScanner();
Swagger swagger = scanner.getSwagger();
swagger.securityDefinition("api_key", new ApiKeyAuthDefinition("api_key", In.HEADER));
swagger.securityDefinition("petstore_auth",
new OAuth2Definition()
.implicit("http://petstore.swagger.io/api/oauth/dialog")
.scope("read:pets", "read your pets")
.scope("write:pets", "modify pets in your account"));
}
}
}
IOdocs from mashery seems to support OAuth, but it's quite different from swagger (redis, node, etc.). It's available on github.

Web Service client generated by wsdl not working with Deployed web service

I have generated a WSDL from a java class using axis2 java2wsdl utility as follows;
java2wsdl -o C:\temp -cn com.temenos.webservices.customer.CustomerServiceWS
Then I have deployed the same web service within an Application Server (say jBoss) in axis2 and I can browse the wsdl on http:// 127.0.0.1:8080/axis2/services/CustomerServiceWS?wsdl and call the methods on this service via standard client like SoapUI etc.
The problem is now that when I generated a client using standard java tooling 'wsimport' by providing a WSDL location as C:\temp (Generated WSDL from java2wsdl utility), my client is unable to communicate with the Deployed Web Service. I am using following code to access the web service;
// Initialise WS
CustomerServiceWS service = null;
CustomerServiceWSPortType servicePort = null;
try {
URL wsdlLocation = new URL("http://127.0.0.1:8080/axis2/services/CustomerServiceWS?wsdl");
QName serviceName = new QName("http://customer.webservices.temenos.com", "CustomerServiceWS");
service = new CustomerServiceWS(wsdlLocation, serviceName);
servicePort = service.getCustomerServiceWSHttpSoap12Endpoint();
} catch (MalformedURLException murle) {
murle.printStackTrace();
return;
}
But while creating an (service Port) Endpoint I am getting following error;
Exception in thread "main" javax.xml.ws.WebServiceException: An attempt was made to construct the ServiceDelegate object with an service name that is not valid: {http://customer.webservices.temenos.com}CustomerServiceWS.
at org.apache.axis2.jaxws.ExceptionFactory.createWebServiceException(ExceptionFactory.java:173)
at org.apache.axis2.jaxws.ExceptionFactory.makeWebServiceException(ExceptionFactory.java:70)
at org.apache.axis2.jaxws.ExceptionFactory.makeWebServiceException(ExceptionFactory.java:118)
at org.apache.axis2.jaxws.spi.ServiceDelegate.<init>(ServiceDelegate.java:218)
at org.apache.axis2.jaxws.spi.Provider.createServiceDelegate(Provider.java:59)
at javax.xml.ws.Service.<init>(Service.java:56)
at com.temenos.webservices.customer.CustomerServiceWS.<init>(CustomerServiceWS.java:42)
at com.temenos.services.customer.client.Client.testGetLanguage(Client.java:32)
at com.temenos.services.customer.client.Client.main(Client.java:21)
I have tried many things but it does not seems to like anything. Am I missing anything?
Thanks,
--
SJunejo
The problem was that I had axis2 in lib path because of that the call happend to org.apache.axis2.jaxws.spi.Provider.createServiceDelegate (Axi2 Provider) instead of Java WS Provider. I removed the axis2 libs from classpath and it seems to be working ok now. (though I am still unable to call my web service via client)
See the description of WSDL file and check the targetNamespace for the url to be given in QName(). Also import necessary packages.

Using codec file extensions with OpenRasta returns 404

When using codec uri file extensions with OpenRasta, OR can't resolve the uri and returns a 404. Without the file extension all works ok.
The codecs are defined for the object resource and I'm using both XmlDataContract and JsonDataContract. Using neither the .xml or .json extension works, this is for both InMemoryHost (which we're using for testing) and ASP.Net (IIS7, integrated mode).
Codec configuration:
ResourceSpace.Has.ResourcesOfType<object>()
.WithoutUri
.AsXmlDataContract()
.And.AsJsonDataContract();
Is there anything else that needs to be done to make uri file extensions work?
You need to register the ContentTypeExtensionUriDecorator as a UriDecorator in OpenRasta in order to expose the .xml, .json functionallity.
The below example should allow you to make http requests to:
GET /home.json
GET /home.xml
public class RastaConfig : IConfigurationSource
{
public void Configure()
{
using(OpenRastaConfiguration.Manual)
{
ResourceSpace.Uses.UriDecorator<ContentTypeExtensionUriDecorator>();
ResourceSpace.Has.ResourceOfType<Home>()
.AtUri("/home")
.HandledBy<HomeHandler>()
.AsXmlDataContract()
.And.AsJsonDataContract();
}
}
}
This is because noramlly the client will add an HTTP Accept header to define the content types it supports and is interested in.
For more information you can read about Content Negotiation (often referred to as conneg) on the web.
OpenRasta will then select the return content type based on the client 's preference in the HTTP Accept header.
Hope this helps.

Resources