Customizing auto generated Swagger definitions - asp.net-core-webapi

I have swagger setup so that it generates the open Api Specification & Swagger Ui on project startup using NSwag based on the controllers in my WebApi.
I would like to enhance the swagger Ui to include
A summary/description for each endpoint
Example parameter inputs for endpoints that require them
Example request body for POST calls
An example access token that can be used only in the swagger documentation to easily authenticate and be able to try everything out (a bit like in this example https://petstore.swagger.io/)
I'm new to NSwag and unsure how to approach adding these enhancements to my code, like where to add them, what i need to use (annotations on controllers? XML comments? another way?) I've tried editing the specification in 'Swagger Editor' but don't see how this can be the way to go since this gets re-generated on every application startup.
I've read the NSwag documentation but that seems to be all about adding the ASP.NET Core middleware, which I already have configured.
Edit:
I now have a description at the top of the page, and have been able to add an example with the remarks tag in XML comments - is there a more elegant way to do this rather than using XML comments?

A description at the top of the page
To customize the API info and description using Nswag, in the Startup.ConfigureServices method, a configuration action passed to the AddSwaggerDocument method adds information such as the author, license, and description:
services.AddSwaggerDocument(config =>
{
config.PostProcess = document =>
{
document.Info.Version = "v1";
document.Info.Title = "ToDo API";
document.Info.Description = "A simple ASP.NET Core web API";
document.Info.TermsOfService = "None";
document.Info.Contact = new NSwag.OpenApiContact
{
Name = "Shayne Boyer",
Email = string.Empty,
Url = "https://twitter.com/spboyer"
};
document.Info.License = new NSwag.OpenApiLicense
{
Name = "Use under LICX",
Url = "https://example.com/license"
};
};
});
The Swagger UI displays the version's information as below:
A summary/description for each endpoint Example parameter inputs for
endpoints that require them Example request body for POST calls An
example access token that can be used only in the swagger
documentation to easily authenticate and be able to try everything out
(a bit like in this example https://petstore.swagger.io/)
You could add the description/example by adding the following elements to the action header.
Use the <summary> element to describe the Endpoint.
Use the <remarks> element to supplements information specified in the <summary> element and provides a more robust Swagger UI. The <remarks> element content can consist of text, JSON, or XML. You could also use it to add sample.
Use the <param> element to add the required parameters. Besides, you could also use the Data annotations attribute with the Model, it will change the UI behavior.
Use the <response> elements to describe response types.
sample code as below:
/// <summary>
/// Creates a TodoItem.
/// </summary>
/// <remarks>
/// Sample request:
///
/// POST /Todo
/// {
/// "id": 1,
/// "name": "Item1",
/// "isComplete": true
/// }
///
/// </remarks>
/// <param name="todoitem"></param>
/// <returns>A newly created TodoItem</returns>
/// <response code="201">Returns the newly created item</response>
/// <response code="400">If the item is null</response>
#region snippet_CreateActionAttributes
[ProducesResponseType(StatusCodes.Status201Created)] // Created
[ProducesResponseType(StatusCodes.Status400BadRequest)] // BadRequest
#endregion snippet_CreateActionAttributes
#region snippet_CreateAction
[HttpPost]
public ActionResult<TodoItem> Create(TodoItem todoitem)
{
_context.TodoItems.Add(todoitem);
_context.SaveChanges();
return CreatedAtRoute("GetTodo", new { id = todoitem.Id }, todoitem);
}
The Swagger UI now looks as below:
More detail information, please check the following tutorials:
Customize API documentation using NSwag
Customize API info and description using Swashbuckle

Figured this out now, ended up using operation processors to configure the Swagger UI/OpenApi endpoint summary, request examples, path parameter example values and the possible UI response codes
There isn't a lot of documentation online for doing it this way (all i could find was the XML comment way of doing it so this took a lot of trial and error to get this working)
Posting my solution here for anyone else who would prefer not to clutter up their controllers with XML comments.
Apply OpenApiOperationProcessor attribute to the controller action
Create the Operation Processor and code the SwaggerUI customizations
Example values for path parameters can be filled out as so

Related

Swashbuckle <summary> alternative

Lets say I have the following Method:
/// <summary>
/// Just a Method
/// </summary>
[HttpGet()]
[Produces("plain/text")]
public IActionResult Foo() => this.Ok("Some Data..");
Swagger is showing me the following:
Can I achieve the same result, without using <summary> ? Is there an attribute or something I could use?
Iam asking because if I run my project on my machine everything works fine, but if i run it on another it doesnt show the description "Just a Method".
It seems like the XML comments get ignored..
I can't post a single peace of the code so please just tell me if theres a work around so i dont have to use <summary>.
Thanks!

BreezeJS modified route not working

My application has two databases with exactly the same schema. Basically, I need to change the DbContext based on what data I'm accessing. Two countries are in one Db and 4 countries in the other. I want the client to decide which context is being used. I tried changing my BreezeWebApiConfig file so that the route looks like this:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{dbName}/{controller}/{action}/{id}",
defaults: new {id=RouteParameter.Optional,dbName="db1"}
);
I added the string to the controller actions:
[HttpGet]
public string Metadata(string dbName="")
{
return _contextProvider.Metadata();
}
And changed the entityManager service Name.
Now when the client spins up, it accesses the corrent metadata action and I get a message:
Error: Metadata query failed for: /breeze/clienthistory/kenya/Metadata. Unable to either parse or import metadata: Type .... already exists in this MetadataStore
When I go to the metadata url from the browser, I get the correct metadata (exactly the same as when I remove the {dbName} segment from the route). If I remove the {dbName} segment from the route I get no error and everything works fine
(I have not started implementing the multiple contexts yet -- I am just trying to make the additional segment work).
Thanks.
I think the problem is that your Breeze client is issuing two separate requests for the same metadata, once under each of the two "serviceNames". Breeze tries to blend them both into the same EntityManager.metadataStore ... and can't because that would mean duplication of EntityType names.
One approach that should work is to begin your application by fetching the metadata immediately upon app start and then adding all the associated "DataServiceNames" to the MetadataStore.
Try something along these lines (pseudo-code):
var manager;
var store = new breeze.MetadataStore();
return store.fetchMetadata(serviceName1)
.then(gotMetadata)
.catch(handleFail);
function gotMetadata() {
// register the existing metadata with each of the other service names
store.addDataService(new breeze.DataService(serviceName2));
... more services as needed ...
manager = new breeze.EntityManager({
dataService: store.getDataService(serviceName1), // service to start
metadataStore: store
});
return true; // return something
}
Alternative
Other approaches to consider don't involve 'db' placeholder in the base URL nor any toying with the Web API routes. Let's assume you stay vanilla in that respect with your basic service name
var serviceName = '/breeze/clienthistory/';
..
For example, you could add an optional parameter to your routes (let's call it db) as needed via a withParameters clause.
Here is a query:
return new breeze.EntityQuery.from('Clients')
.where(...)
.withParameters({db: database1}); // database1 == 'kenya'
.using(manager).execute()
.then(success).catch(failed);
which produces a URL like:
/breeze/clienthistory/Clients/?$filter=...&db=kenya
It makes an implicit first-time-only metadata request that resolves to:
/breeze/clienthistory/Metadata
Your server-side Web API query methods can expect db as an optional parameter:
[HttpGet]
public string Metadata(string db="")
{
... do what is right ...
}
Saves?
I assume that you also want to identify the target database when you save. There are lots of ways you can include that in the save request
in a custom HTTP header via a custom AJAX adapter (you could do this for queries too)
in a query string parameter or hash segment on the saveChanges POST request URL (again via a custom AJAX adapter).
in the tag property of the saveOptions object (easily accessed by the server implementation of SaveChanges)
in the resourceName property of the saveOptions object (see "named save")
You'll want to explore this variety of options on your own to find the best choice for you.

Unit Testing for Websites or Webforms or .Aspx page: code coverage

I have one website which was made in Asp.Net. I want to have the code coverage for the website now.
Can anybody suggest me which are the possible ways to have the code coverage for the website? Suggestions on how to write unit test methods for Webform or .Aspx page are also welcome.
Try following steps to enable code coverage:
- Open the local.testsettings which you can access from Test -> Edit
Test Settings -> Local (local.testsettings)
- List item Select Data and Diagnostics from the list
- Select the Enabled checkbox on the Code Coverage row
- Double-click the Code Coverage row
- Select the assemblies you want to instrument
- Specify a re-signing key file if your assemblies are strong-named
- Click OK
- Click Apply
- Click Close
For unit test you can have seperate class library project in the same solution where you can create unit test method. For example :
/// <summary>
/// Holds test for MapRoles.
/// </summary>
[TestClass]
public class MapRolesTest
{
/// <summary>
/// Perform test on CompareUserRoles.
/// </summary>
[TestMethod]
public void CompareUserRolesTest()
{
//// The class which is inside actual asp.net web application.
MapRoles objMapRoles = new MapRoles();
//// Get role of the user1.
string user1Role = objMapRoles.GetUserRole("Jeet");
//// Get role of the user2.
string user2Role = objMapRoles.GetUserRole("Vishwajeet");
//// Assert.
Assert.AreEqual(user1Role, user2Role);
}
}

How to configure an endpoint in Spring to accept both form data and XML request body?

I have a small question regarding Spring's MVC data binding capabilities.
I do have the following controller class:
#Controller
#RequestMapping("/foo")
public class FooController() {
// … some init stuff //
#RequestMapping(value = "/{id}/edit.{format}", method = RequestMethod.POST)
public ModelAndView editFoo(#RequestBody FooItem foo, #PathVariable("format") String format) {
// some code here to edit the FooItem //
}
}
I want to be able to post form data as well as XML against this method. For that to work I added two message converters to my applicationContext.xml: The default formHttpMessageConverter and an XStream marshaller.
This works fine, but I have a problem, that if I use #RequestBody and post form data against the URL, the server responds with a 415 Error. If I remove this annotation, form data works well and Spring creates the object for me, but if I post XML against it, I get an empty object.
Is there any way around this or do I need to have 2 methods to be able to handle both of the incoming formats?
Thanks in advance!
I think you need two methods.
FormHttpMessageConverter doesn't have the same databinding capabilities as #ModelAttribute provides, it can't bind request to the specified target class, only to MultiValueMap (see javadoc).

How to dynamically load and switch the resource file in the web app (ASP.NET) without recompiling?

I would like to store the resource files (containing texts for labels etc.) for my web application in the database to be able to edit and create them dynamically later (probably in the UI). My idea was to store the whole resx file in an xml column and simply load it on demand - depending on the language and some other attributes of the current user when he is logging into the application or switching the context. The important thing is that the resources do not only depend on the culture info of the user but also on some context information that can be switched by user without logging off and on again (in our case called "group", not having anything to do with a group of users).
Is it possible somehow to load the contents of the resources from an external source and simply switch them without web application being recompiled by the server ? I know that there are some interfaces on which I could hook up and implement a custom resources provider which reads from the database directly but if it could work somehow with the resx files it would probably make things a lot easier..
Pretty late but since there is no answer as of yet.
System.Resources.ResourceReader resourceReader
= new System.Resources.ResourceReader("PathToResourceFile");
That's pretty much it. Now you can create resource files like en.resx or de.resx and load them depending on the users language. Something like
System.Resources.ResourceReader resourceReader
= new System.Resources.ResourceReader(HttpContext.Current.Request.UserLanguages[0]
+ ".resource");
Keep in mind to provide a default language (resource file) for a user with a language you don't support.
Edit:
Take a look at this link.
The question is 6 years old, but I'm still gonna answer it :)
To read .resx files, you need to use System.Resources.ResXResourceReader class from System.Windows.Forms.dll
This is nicely explained here. Just a quick sample for completeness:
using (ResXResourceReader resxReader = new ResXResourceReader(#".\CarResources.resx"))
{
foreach (DictionaryEntry entry in resxReader) {
// ...
}
}
Sure you can do this easily, and it works for straight XML files. You don't need to use a resx file.
/// <summary>
/// Sets or replaces the ResourceDictionary by dynamically loading
/// a Localization ResourceDictionary from the file path passed in.
/// </summary>
/// <param name="resourceDictionaryFile">The resource dictionary to use to set/replace
/// the ResourceDictionary.</param>
private void SetCultureResourceDictionary(String resourceDictionaryFile)
{
// Scan all resource dictionaries and remove, if it is string resource distionary
for ( int index= 0; index < Resources.MergedDictionaries.Count; ++index)
{
// Look for an indicator in the resource file that indicates the resource is
// swappable. For instance in our files the header contains this:
// <sys:String x:Key="ResourceDictionaryName">Resources-en-CA</sys:String>
if (Resources.MergedDictionaries[index].Contains("ResourceDictionaryName"))
{
if ( File.Exists(resourceDictionaryFile) )
{
Resources.MergedDictionaries.Remove(Resources.MergedDictionaries[index]);
}
}
}
// read required resource file to resource dictionary and add to MergedDictionaries collection
ResourceDictionary newResourceDictionary = new ResourceDictionary();
newResourceDictionary.Source = new Uri(resourceDictionaryFile);
Resources.MergedDictionaries.Add(newResourceDictionary);
}

Resources