Using Swashbuckle in conjuntion with c.MultipleApiVersions((apiDesc, version) =>... the result is our swagger file resides at eg: https://host/api/swagger/docs/{version}. I would like to actually have the swagger file at https://host/api/{version}/swagger. Is it possible the I can set this up in my SwaggerConfig .EnableSwagger()?
This would allow for the following Urls :
http://host/api/v1/swagger/
http://host/api/v2/swagger/
Appreciate the help.
To do that way, you can update the swaggerconfig file as shown below:
.EnableSwagger("{apiVersion}/swagger", c =>
{
c.MultipleApiVersions(
(vc) =>
{
vc.Version("v2", "Swashbuckle Dummy API V2");
vc.Version("v1", "Swashbuckle Dummy API V1");
});
});
Just for further references in asp.net core 2.2 it will lokk like this
app.UseSwagger(options =>
{
options.RouteTemplate = "docs/{documentName}/docs.json";
});
app.UseSwaggerUI(options =>
{
//Build a swagger endpoint for each discovered API version
foreach (var description in provider.ApiVersionDescriptions.OrderByDescending(x=>x.ApiVersion).AsList())
{
options.SwaggerEndpoint($"/docs/{description.GroupName}/docs.json", description.GroupName);
}
options.RoutePrefix = "docs";
}
);
in which provider is IApiVersionDescriptionProvider injected by DI
Related
We use SwashBuckle to configure our AspNet Core (Service fabric) project to generate Swagger json and UI.
Then we use NSwagStudio to generate typescript and C# clients out of nswag.json template with swagger generated by Swashbuckle.
Recently there has been changes in latest version of NSwagStudio that generates clients with null checks added to response objects. We have existing Controllers returning null responses so the clients have stopped working.
Our sample controller endpoint declaration:
[HttpPost]
[ODataRoute]
[Produces("application/json")]
[ProducesResponseType(typeof(IActionResult), Status200OK)]
public Task PostXXX() => ...;
Our generated client (before)
//----------------------
// <auto-generated>
// Generated using the NSwag toolchain v13.6.2.0 (NJsonSchema v10.1.23.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------
var status_ = ((int)response_.StatusCode).ToString();
if (status_ == "200")
{
var objectResponse_ = await ReadObjectResponseAsync<IActionResult>(response_, headers_).ConfigureAwait(false);
return objectResponse_.Object;
}
Our generated client (after)
//----------------------
// <auto-generated>
// Generated using the NSwag toolchain v13.7.0.0 (NJsonSchema v10.1.24.0 (Newtonsoft.Json v12.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<Void>(response_, headers_).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new Exception("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
The question is how do we tell SwashBuckle to mark that this controller endpoint can return nullable response?
In other words, am looking for something similar to what's available in NSwag here in Swashbuckle world:
https://github.com/RicoSuter/NSwag/wiki/AspNetCoreOpenApiDocumentGenerator#response-nullability
More details in this github issue
https://github.com/RicoSuter/NSwag/issues/3011
Please let me know if you need more information.
I've just updated a webjob to version 3 of the azure-sdk. The job has a TimerTrigger and as an alternative to create an appsettings.json file with the connectionstring I was hoping to use the TimerOptions class to set the ConnectionString but to my surprise the class is empty?!.
I stumbled across this https://github.com/Azure/azure-webjobs-sdk/issues/2178
I found a workaround to the above scenario regarding the TimerTrigger. In the ConfigureHostConfiguration we can use the AddInMemoryCollection to set the connectionString "AzureWebJobsStorage".
Dictionary<string, string> connectionStrings =
new Dictionary<string, string>
{
{ "AzureWebJobsStorage", ConfigurationManager.ConnectionStrings["AzureWebJobsStorage"].ConnectionString},
};
builder.ConfigureHostConfiguration(config =>
{
config.AddInMemoryCollection(connectionStrings);
});
Sadly this will not work on ServiceBusTriggers. But with ServiceBusTriggers we have an ServiceBusOptions that is not empty and can therefore be used.
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddServiceBus(options =>
{
options.MessageHandlerOptions.AutoComplete = false;
options.ConnectionString =
ConfigurationManager.ConnectionStrings["AzureWebJobsServiceBus"].ConnectionString;
});
b.AddTimers();
});
I'm trying to add localization support for a card built for the Laravel Nova dashboard.
I already created a folder in /resources/lang containing the JSON language files in a format like en.json. The files get published (copied) with the publish command but the loadJsonTranslationsFrom() does not seem to do anything:
class CardServiceProvider extends ServiceProvider
{
public function boot()
{
$this->publishes(
[__DIR__ . '/../resources/lang' => resource_path('lang/vendor/my-custom-card')],
'my-custom-card-lang'
);
$this->loadJsonTranslationsFrom(resource_path('lang/vendor/my-custom-card'));
}
}
This is how the markup in Card.vue looks like:
{{__('Title')}}
How can I test if the JSON files are loaded correctly? What am I missing?
The question is how do I support localization for cards in Laravel Nova?
Card localization has been solved in Laravel Nova 2.
To localize strings use the __ helper within your Vue components and load the according translation files within your NovaServiceProvider:
Nova::serving(function (ServingNova $event) {
Nova::script('{{ component }}', __DIR__.'/../dist/js/card.js');
Nova::style('{{ component }}', __DIR__.'/../dist/css/card.css');
Nova::translations(__DIR__.'/../resources/lang/en/card.json');
});
An exemplary implementation can be found on GitHub.
Further information is now available in the documentation.
I'm having the same issue, but for a tool, also in Nova 2.0.
I found a somewhat elegant solution - maybe it helps someone nonetheless.
Create en.json in /nova-components/{your-tool}/resources/lang/
In /nova-components/{your-tool}/resources/js/tool.js add Vue.mixin(require('./translation'));.
It should look something like this:
Nova.booting((Vue, router, store) => {
router.addRoutes([
{your-routes}
]);
Vue.mixin(require('./translation')); <-------------- add this line!
});
Create the /nova-components/{your-tool}/resources/js/translation.js, it should look like this:
module.exports = {
methods: {
__(key, replace) {
var translations = _.merge(window.config.translations, window.config['tool-translations']);
var translation = translations[key]
? translations[key]
: key;
_.forEach(replace, (value, key) => {
translation = translation.replace(':' + key, value)
});
return translation;
}
}
};
Now you have to add the following to the Nova::serving() function inside the boot() function of your /nova-components/{your-tool}/src/ToolServicePrivoder.php file:
Nova::provideToScript([
'tool-translations' => $this->getTranslations(),
]);
Now add below said boot() function the following:
private static function getTranslations()
{
$translationFile = __DIR__ . '/../resources/lang/' . app()->getLocale() . '.json';
if (!is_readable($translationFile)) {
return [];
}
return json_decode(file_get_contents($translationFile), true);
}
We've implemented Swashbuckle on our Web Api project and i'm noticing that the SwaggerUI test harness displays the exact same information for any version specified in the address. More specifically it displays the swagger information for which ever VersionInfoBuilder comes first within the SwaggerConfig.
So for example if i navigate to "/preview/swagger/index" - the UI displayed is all v1 information not related to version 'preview'.
What am I doing wrong here?
public class SwaggerConfig
{
private SwaggerConfig() { }
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger("{apiVersion}/swagger", c =>
{
c.MultipleApiVersions(
(apiDesc, version) =>
{
var path = apiDesc.RelativePath.Split('/');
var pathVersion = path[0];
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(pathVersion, version, CompareOptions.IgnoreCase) >= 0;
},
vc =>
{
vc.Version("v1", "Api - v1");
vc.Version("preview", "Api - Preview");
});
})
.EnableSwaggerUi("{apiVersion}/swagger/{*assetPath}", c =>
{
c.DisableValidator();
});
}
}
I don't think the configuration is wrong, it is just that your expectations are not correct.
You do not have to navigate to another Swagger UI (at preview/docs/index) but you have to point Swagger UI at another specification. Just enter http://yourserver:yourport/preview/swagger in the input box in the header and press Explore. Swagger UI will now load and display the preview specification.
I'm uploading files using the ASP.NET Web API. I've done this before the RC but for some reason the file is being saved as "BodyPart_3ded2bfb-40be-4183-b789-9301f93e90af" instead of the file name. The filename variable below returns this bodypart string too instead of the file name. I can't seem to figure out where I'm going wrong. Any help is appreciated.
Client code:
function upload() {
$("#divResult").html("Uploading...");
var formData = new FormData($('form')[0]);
$.ajax({
url: 'api/files/uploadfile?folder=' + $('#ddlFolders').val(),
type: 'POST',
success: function (data) {
$("#divResult").html(data);
},
data: formData,
cache: false,
contentType: false,
processData: false
});
};
Controller:
public Task<HttpResponseMessage> UploadFile([FromUri]string folder)
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.UnsupportedMediaType));
}
// Save file
MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(HttpContext.Current.Server.MapPath("~/Files"));
Task<IEnumerable<HttpContent>> task = Request.Content.ReadAsMultipartAsync(provider);
return task.ContinueWith<HttpResponseMessage>(contents =>
{
string filename = provider.BodyPartFileNames.First().Value;
return new HttpResponseMessage()
{
Content = new StringContent(string.Format("File saved in {0}.", folder))
};
}, TaskScheduler.FromCurrentSynchronizationContext());
The files are looking like:
That was a concious change we made -- it was considered a security risk to take the file name provided in the Content-Disposition header field and so instead we now compute a file name which is what you are seeing.
If you want to control the server local file name yourself then you can derive from MultipartFormDataStreamProvider and override GetLocalFileName to provide whatever name you want. Note though that there may be security considerations doing so.
Hope this helps,
Henrik
I updated the code for the tutorial to make it work with ASP.NET Web API RC. Indeed, as Henrik mentioned Content-Disposition is no longer used a file name. See the source files at the bottom of the post - http://www.strathweb.com/2012/04/html5-drag-and-drop-asynchronous-multi-file-upload-with-asp-net-webapi/
Please note, that there are further changes to MultipartFormDataStreamProvider that didn't make the cut to the RC, so it's now even more flexible. Henrik blogged about those here - http://blogs.msdn.com/b/henrikn/archive/2012/04/27/asp-net-web-api-updates-april-27.aspx.
EDIT: I have blogged about new and improved way of uploading files in Web API RTM, so that should hopefully help gets things organized - http://www.strathweb.com/2012/08/a-guide-to-asynchronous-file-uploads-in-asp-net-web-api-rtm/
Here, this work for me
In API Controller
// We implement MultipartFormDataStreamProvider to override the filename of File which
// will be stored on server, or else the default name will be of the format like Body-
// Part_{GUID}. In the following implementation we simply get the FileName from
// ContentDisposition Header of the Request Body.
public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public CustomMultipartFormDataStreamProvider(string path) : base(path) { }
public override string GetLocalFileName(HttpContentHeaders headers)
{
return headers.ContentDisposition.FileName.Replace("\"", string.Empty);
}
}
Then
string root = HttpContext.Current.Server.MapPath("~/App_Data");
CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(root);
Thanks,