Search code examples
c#asp.net-coreswaggerswashbuckleswashbuckle.aspnetcore

Dynamic head content in Swashbuckle/Swagger


To change the head content of the Swagger page, we can write

applicationBuilder.UseSwaggerUI(options => 
    options.HeadContent = ...
);

However, this is called only once during startup and thus stays static. Is it possible to add dynamic additional content to the Swagger page or dynamically change the head content at a later stage? (Simple toy example: show the current time or the value of a counter.)


Solution

  • You can see this line in SwaggerUIOptions:

    /// <summary>
    /// Gets or sets additional content to place in the head of the swagger-ui page
    /// </summary>
    public string HeadContent { get; set; } = "";
    

    HeadContent is actually a string type and is received by %(HeadContent) on the default index page of Swagger. So if you pass a value through HeadContent, it seems like it can only be rendered as static content.

    You can customize Swagger's index.html and replace %(HeadContent) in it, and then render it dynamically through JavaScript.

    app.UseSwaggerUI(config =>
    {
        //I add index.html in the root directory, please pay attention to your path
        config.IndexStream = () => Assembly.GetExecutingAssembly()
            .GetManifestResourceStream("YourProjectName.index.html");
    }
    

    Set index.html as Embedded resource:

    <ItemGroup>
        <EmbeddedResource Include="index.html">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </EmbeddedResource>
    </ItemGroup>
    

    To get started, you should base your custom index.html on the default version.

    Take displaying the current time as an example, below is my index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>%(DocumentTitle)</title>
        <link rel="stylesheet" type="text/css" href="./swagger-ui.css">
        <link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
        <link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
        <style>
    
            html {
                box-sizing: border-box;
                overflow: -moz-scrollbars-vertical;
                overflow-y: scroll;
            }
    
            *,
            *:before,
            *:after {
                box-sizing: inherit;
            }
    
            body {
                margin: 0;
                background: #fafafa;
            }
    
            #wrapper {
                font-size: 50px;
                font-weight: bold;
            }
        </style>
    
    
        //Replace %(HeadContent) here
        <div id="wrapper"></div>
        <!--%(HeadContent)-->
    
    </head>
    
    <body>
        <div id="swagger-ui"></div>
     </body>
    <script>
            if (window.navigator.userAgent.indexOf("Edge") > -1) {
                console.log("Removing native Edge fetch in favor of swagger-ui's polyfill")
                window.fetch = undefined;
            }
        </script>
    
        <script src="./swagger-ui-bundle.js"></script>
        <script src="./swagger-ui-standalone-preset.js"></script>
        <script>
            /* Source: https://gist.github.com/lamberta/3768814
             * Parse a string function definition and return a function object. Does not use eval.
             * @param {string} str
             * @return {function}
             *
             * Example:
             *  var f = function (x, y) { return x * y; };
             *  var g = parseFunction(f.toString());
             *  g(33, 3); //=> 99
             */
    
             //Here is my JavaScript code:
             function test(){
                var date=new Date();  
                var year = date.getFullYear(); 
                var month = date.getMonth()+1;  
                var day = date.getDate();  
                var hour = date.getHours();  
                var minute = date.getMinutes();  
                var second = date.getSeconds(); 
                    
                if(month<10){
                    month = "0"+month;
                }
                if(day<10){
                    day = "0"+day;
                }
                if(hour<10){
                    hour = "0"+hour;
                }
                if(minute<10){
                    minute = "0"+minute;
                }
                if(second<10){
                    second = "0"+second;
                }
                    
                document.getElementById("wrapper").innerHTML = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
            }
            setInterval("test()",1000);
    
    
            function parseFunction(str) {
                if (!str) return void (0);
    
                var fn_body_idx = str.indexOf('{'),
                    fn_body = str.substring(fn_body_idx + 1, str.lastIndexOf('}')),
                    fn_declare = str.substring(0, fn_body_idx),
                    fn_params = fn_declare.substring(fn_declare.indexOf('(') + 1, fn_declare.lastIndexOf(')')),
                    args = fn_params.split(',');
    
                args.push(fn_body);
    
                function Fn() {
                    return Function.apply(this, args);
                }
                Fn.prototype = Function.prototype;
    
                return new Fn();
            }
    
            window.onload = function () {
                var configObject = JSON.parse('%(ConfigObject)');
                var oauthConfigObject = JSON.parse('%(OAuthConfigObject)');
    
                // Workaround for https://github.com/swagger-api/swagger-ui/issues/5945
                configObject.urls.forEach(function (item) {
                    if (item.url.startsWith("http") || item.url.startsWith("/")) return;
                    item.url = window.location.href.replace("index.html", item.url).split('#')[0];
                });
    
                // If validatorUrl is not explicitly provided, disable the feature by setting to null
                if (!configObject.hasOwnProperty("validatorUrl"))
                    configObject.validatorUrl = null
    
                // If oauth2RedirectUrl isn't specified, use the built-in default
                if (!configObject.hasOwnProperty("oauth2RedirectUrl"))
                    configObject.oauth2RedirectUrl = (new URL("oauth2-redirect.html", window.location.href)).href;
    
                // Apply mandatory parameters
                configObject.dom_id = "#swagger-ui";
                configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset];
                configObject.layout = "StandaloneLayout";
    
                // Parse and add interceptor functions
                var interceptors = JSON.parse('%(Interceptors)');
                if (interceptors.RequestInterceptorFunction)
                    configObject.requestInterceptor = parseFunction(interceptors.RequestInterceptorFunction);
                if (interceptors.ResponseInterceptorFunction)
                    configObject.responseInterceptor = parseFunction(interceptors.ResponseInterceptorFunction);
    
                // Begin Swagger UI call region
    
                const ui = SwaggerUIBundle(configObject);
    
                ui.initOAuth(oauthConfigObject);
    
                // End Swagger UI call region
    
                window.ui = ui
            }
        </script>
    </body>
    </html>
    

    Test Result:

    enter image description here