Swashbukle doesn't show the Odata routes in the Swagger UI - odata

I am using Net6 web api with odata support. I am not using any apicontroller in the code and instead i am inheriting from ODataController and swagger UI is not showing the routes in the UI and event i am not able to browse those endpoints separately. Below is my samplecode
public class ValuesController : ODataController
{
[EnableQuery(PageSize = 5)]
public IQueryable<Note> Get()
{
return _context.Notes.AsQueryable();
}
}
Middleware configuration
builder.Services.AddControllers()
.AddOData(opt =>
{
opt.Conventions.Remove(opt.Conventions.OfType<MetadataRoutingConvention>()
.First());
opt.AddRouteComponents(GetEdmModel())
.Select()
.Expand()
.Count()
.Filter()
.OrderBy().SetMaxTop(100).TimeZone = TimeZoneInfo.Utc;
}).AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("Notes",
new Microsoft.OpenApi.Models.OpenApiInfo { Title = "Notes API", Version = "v1", });
});
Please note I have one controller with same config and it is showing in the swagger UI, if I add new controllers inheriting from ODataController it is not working. any help appreciated.
Thanks,
Suresh

Related

.Net 6 Web Api Swagger Versioning problem

As the title suggests, i have a .net 6 web api that I'm trying to add versioning to but swagger (swashbuckle) does not seem to understand whats going on.
Program.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddApiVersioning(setup =>
{
setup.DefaultApiVersion = new ApiVersion(1, 0);
setup.AssumeDefaultVersionWhenUnspecified = true;
setup.ReportApiVersions = true;
});
ConfigureServices(builder.Services);
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/swagger/v1/swagger.json", $"v1");
c.SwaggerEndpoint($"/swagger/v2/swagger.json", $"v2");
});
app.Run();
void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore();
services.AddApiVersioning(options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = false;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
});
services.AddSwaggerGen();
}
I have annotated my controllers like so:
[ApiVersion("1.0")]
[Route("api/v1/[controller]")]
[ApiController]
public class MessageController : ControllerBase
[ApiVersion("2.0")]
[Route("api/v2/[controller]")]
[ApiController]
public class MessageController : ControllerBase
The swagger document that is generated looks like this:
And if i select v2 from the drop down, I get this:
Nuget packages and versions installed are:
Can anyone tell me where I'm going wrong.
You'll need to add the swagger documents themselves, not just the UI for them. In your AddSwaggerGen method, add something like:
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "My API" });
c.SwaggerDoc("v2", new OpenApiInfo { Version = "v2", Title = "My API" });
});
This article seems to have covered all the aspect what you are looking for.
Please have a look.
https://referbruv.com/blog/integrating-aspnet-core-api-versions-with-swagger-ui/
Besides passing the configuration here:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API - V1", Version = "v1" });
c.SwaggerDoc("v2", new OpenApiInfo { Title = "My API - V2", Version = "v2" });
});
According to the docs you have to specify the GroupName of the controller:
[ApiVersion("2.0")]
[Route("api/v2/[controller]")]
[ApiController]
[ApiExplorerSettings(GroupName = "v2")]
public class MessageController : ControllerBase
Also, the docs show ways to customize and add conventions.

Swagger UI - authentication only for some endpoints

I am using Swagger in a .NET COre API project.
Is there a way to apply JWT Authentication in Swagger UI only for some endpoints?
I put [Authorize] Attribute only on a few calls (also have tried putting [AllowAnonymous] on the calls that don't need authentication), but when I open the Swagger UI page, the lock symbol is on all the endpoints.
You'll have to create an IOperationFilter to only add the OpenApiSecurityScheme to certain endpoints. How this can be done is described in this blog post (adjusted for .NET Core 3.1, from a comment in the same blog post).
In my case, all endpoints defaults to [Authorize] if not [AllowAnonymous] is explicitly added (also described in the linked blog post). I then create the following implementation of IOperationFilter:
public class SecurityRequirementsOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (!context.MethodInfo.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) &&
!(context.MethodInfo.DeclaringType?.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) ?? false))
{
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "bearer"
}
}, new string[] { }
}
}
};
}
}
}
You'll have to tweak the if statement if you don't default all endpoints to [Authorize].
Finally, where I call services.AddSwaggerGen(options => { ... } (usually in Startup.cs) I have the following line:
options.OperationFilter<SecurityRequirementsOperationFilter>();
Note that the above line will replace the (presumably) existing call to options.AddSecurityRequirement(...) in the same place.

How to create Web API that can be imported into Azure API Management Portal

so I have fiddled around a bit with Azure API Management Portal. I have followed the tutorial on how the import the conference api and managed to get it to work.
Then I created a WebApi app that uses swagger. My configuration is as follows:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});
...
}
public void Configure(IApplicationBuilder app,
IServiceProvider services,
IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Address Service API");
});
app.UseHttpsRedirection();
app.UseMvc();
}
If I run this and navigate to https://my-api/swagger, I can see the swagger UI and I can also see the specification when I click on the link on the swagger UI or visit the url https://my-api.azurewebsites.net/swagger/v1/swagger.json
So my problem is, I have no idea on how to actually import this into AAMP. I can publish it to a app service and it works from there, but if I try to import the url https://my-api.azurewebsites.net/swagger/v1/swagger.json into the AAMP, I get an error:
So I wait an hour and try again, only the be greeted by the same error and I think I am missing something because when I imported the conference api specification, it had a different url than mine, yet I cannot find anything or I am searching for the wrong things. Can anybody please give me a heads up here?
I have also tried searching for the sources of the conference API so I can deduct what I am doing wrong but I didn't have any luck on finding those.
Importing Swagger document into APIM is pretty straight forward by following this Azure document. There’s no issue when you import Swagger 1.2 documents. However, if you’re intending to import Swagger 2.0 ones, you might be facing these kind of issue
If you’re building an API app with .NET Framework 4.5+, using Swashbuckle library, it would be fine. However, if you’re building the app with ASP.NET Core, it does bring you a headache. Firstly, look at your Startup.cs file. The ConfigureService method looks like:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen();
services.ConfigureSwaggerDocument(
options =>
{
options.SingleApiVersion(new Info() { Version = "v1", Title = "Swagger UI" });
options.IgnoreObsoleteActions = true;
options.OperationFilter(new ApplyXmlActionComments(GetXmlPath(appEnv)));
});
services.ConfigureSwaggerSchema(
options =>
{
options.DescribeAllEnumsAsStrings = true;
options.IgnoreObsoleteProperties = true;
options.CustomSchemaIds(type => type.FriendlyId(true));
options.ModelFilter(new ApplyXmlTypeComments(GetXmlPath(appEnv)));
});
...
}
private static string GetXmlPath(IApplicationEnvironment appEnv)
{
var assembly = typeof(Startup).GetTypeInfo().Assembly;
var assemblyName = assembly.GetName().Name;
var path = $#"{appEnv.ApplicationBasePath}\{assemblyName}.xml";
if (File.Exists(path))
{
return path;
}
var config = appEnv.Configuration;
var runtime = $"{appEnv.RuntimeFramework.Identifier.ToLower()}{appEnv.RuntimeFramework.Version.ToString().Replace(".", string.Empty)}";
path = $#"{appEnv.ApplicationBasePath}\..\..\artifacts\bin\{assemblyName}\{config}\{runtime}\{assemblyName}.xml";
return path;
}
In addition to this, the Configure method might look like:
public void Configure(IApplicationBuilder app)
{
...
app.UseSwaggerGen();
app.UseSwaggerUi();
...
}
Wen need to include two additional properties – host and schemes. Swagger specification clearly declares that both are NOT required. However, APIM DOES require both properties to be included in the swagger.json document.
So, how can we sort this out?
For your app in .NET 4.5+, just make sure that your SwaggerConfig.cs has activated those options with proper settings:
SwaggerDocsConfig.Schemes(new[] { “http”, “https” });
SwaggerDocsConfig.RootUrl(req => GetRootUrlFromAppConfig());
In your ASP.NET Core app, it might be tricky as you should implement the IDocumentFilter interface. Here’s a sample code:
public class SchemaDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
swaggerDoc.Host = "localhost:44321";
swaggerDoc.BasePath = "/";
swaggerDoc.Schemes = new List<string>() { "https" };
}
}
And this SchemaDocumentFilter should be added into your ConfigureService method in Startup.cs:
public static void ConfigureServices(this IServiceCollection services)
{
...
services.AddSwaggerGen();
services.ConfigureSwaggerDocument(
options =>
{
options.SingleApiVersion(new Info() { Version = "v1", Title = "Swagger UI" });
options.IgnoreObsoleteActions = true;
options.OperationFilter(new ApplyXmlActionComments(GetXmlPath(appEnv)));
options.DocumentFilter<SchemaDocumentFilter>();
});
services.ConfigureSwaggerSchema(
options =>
{
options.DescribeAllEnumsAsStrings = true;
options.IgnoreObsoleteProperties = true;
options.CustomSchemaIds(type => type.FriendlyId(true));
options.ModelFilter(new ApplyXmlTypeComments(GetXmlPath(appEnv)));
});
...
}
Once you complete this, then import your swagger.json to APIM then it should work.
Reference:
Hope it helps.

Example ASP.Net Core Breeze server with angular/breeze client code?

Is there example code of a breeze/angular client app using ASP.Net Core Breeze server?
It looks like there are the following Nuget packages:- Breeze.AspNetCore.NetCore and Breeze.Composite.AspNetCore.EF6
It would be really helpful to have the TempHire example using this technology.
Can you point me in the right direction? re. frontend/backend code example
Any help appreciated.
This is a bit of a journey because right now there are a lot of moving parts. I have had some success in getting this to work but there are some limitations for example i cannot use .expand('entityName') on the client.
I am using .NET CORE 3.0 preview with Entity Framework Core. I have attached an image of all the dependencies i have installed. Not all of them are required but probably used in a API project.
The most important parts of the below code snippet are to setup the NewtonsoftJson settings.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
//THE BELOW LINE IS IMPORTANT OTHERWISE IT WILL CAMELCASE TO THE SERVER
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
//THE BELOW LINE PREVENTS LOOPING ENTITY REFs
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var connection = #"Server=tcp:XXXXX.database.windows.net,1433;Initial Catalog=DBNAME;Persist Security Info=False;User ID=XXX;Password=XXXXX;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
//THIS IS WHERE YOU ARE GOING TO MAKE YOUR CONTEXT INJECTABLE
services.AddDbContext<YOURCONTEXTContext>(options => options.UseSqlServer(connection, x => x.UseNetTopologySuite()));
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// configure jwt authentication
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Token);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Then you need to setup your breeze controller:
using Microsoft.AspNetCore.Authorization;
using System.Linq;
using HB.Data.Models;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using System;
using Microsoft.AspNetCore.Mvc;
using Breeze.AspNetCore;
using HB.API.Manager;
using HB.BusinessFacade.Business;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using Breeze.Persistence;
using System.Threading.Tasks;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
namespace HB.API.Controllers
{
[BreezeQueryFilter]
[Route("api/[controller]/[action]")]
public class BreezeController : ControllerBase
{
private YOURCONTEXTContext _context;
private hbPersistenceManager PersistenceManager;
string UserID;
public BreezeController(YOURCONTEXTContext context, IHttpContextAccessor httpContextAccessor)
{
this._context = context;
PersistenceManager = new hbPersistenceManager(context);
//this.UserID = httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
}
[HttpGet]
public string Metadata()
{
return PersistenceManager.Metadata();
}
}
And then just a little helper that i use for the PersistenceManager
using HB.Data.Models;
using Breeze.Persistence.EFCore;
namespace HB.API.Manager
{
public class hbPersistenceManager : EFPersistenceManager<YOURCONTEXTContext>
{
public hbPersistenceManager(YOURCONTEXTContext dbContext) : base(dbContext) { }
}
}
Please see this example: https://github.com/Breeze/northwind-demo
It is a full working example with a .NET Core 2.2 backend and Angular 8 front end, and includes TypeScript class generation so that the client-side TypeScript model matches the server-side C# model and database.
The example repo includes steps to create the entire app from scratch.

How to show c# validation attributes on query parameters in Swagger

I'm using Swagger with ASP.Net Core 2.1 Web API project. Here's an example controller action method:
[HttpGet]
public string GetString([Required, MaxLength(20)] string name) =>
$"Hi there, {name}.";
And here's what I get in the Swagger documentation. As you can see, Swagger shows the Required attribute, but not the MaxLength one:
If I use Required and MaxLength attributes on a DTO class that's the argument of a POST action method, then Swagger shows them both:
How can I get Swagger to show MaxLength (and other) validation attributes for query parameters?
Note: I have tried to replace the string name argument with a class that has one string property called name - Swagger produces exactly the same documentation.
In .NET Core, you can use ShowCommonExtensions = true, with given sequence (ConfigObject on top).
public static IApplicationBuilder UseR6SwaggerDocumentationUI(
this IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
//Allow to add addition attribute info on doc. like [MaxLength(50)]
c.ConfigObject = new ConfigObject
{
ShowCommonExtensions = true
};
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Asptricks.net API");
c.RoutePrefix = "api_documentation/index";
c.InjectStylesheet("/swagger-ui/custom.css");
c.InjectJavascript("/swagger-ui/custom.js");
c.SupportedSubmitMethods( new[] { SubmitMethod.Patch });
//Collapse model near example.
c.DefaultModelExpandDepth(0);
//Remove separate model definition.
c.DefaultModelsExpandDepth(-1);
});
return app;
}

Resources