Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Startup.cs - ConfigureServices and Configure #65

Open
Misiu opened this issue Apr 28, 2022 · 8 comments
Open

Support for Startup.cs - ConfigureServices and Configure #65

Misiu opened this issue Apr 28, 2022 · 8 comments

Comments

@Misiu
Copy link

Misiu commented Apr 28, 2022

I have a .NET 6 project, but I'm using Program.cs and 'Startup.cs' approach.

There is a method to add InstantAPI using services.AddInstantAPIs();, but no way to configure it inside Configure(IApplicationBuilder app)

https://github.com/csharpfritz/InstantAPIs/blob/main/Fritz.InstantAPIs/WebApplicationExtensions.cs#L18 supports IEndpointRouteBuilder but a method that supports IApplicationBuilder is missing.

Any plans to add support for it?

@verbedr
Copy link
Contributor

verbedr commented Apr 28, 2022

Don't you use the app.UseEndpoints? If you are, you can do.

app.UseEndpoints(x =>
{
	x.MapInstantAPIs<MyContext>(config =>
	{
		config.IncludeTable(db => db.Contacts, ApiMethodsToGenerate.All, "addressBook");
	});
	x.MapControllers(); // example of what you are already mapping. 
});

@Misiu
Copy link
Author

Misiu commented Apr 29, 2022

@verbedr thank you for the reply. Will try that ASAP

@Misiu
Copy link
Author

Misiu commented May 4, 2022

@verbedr this works well. It would be useful to add this example to the readme. What do you think?

@Misiu
Copy link
Author

Misiu commented May 4, 2022

@verbedr quick update: this works partially.
When I create a new project using .NET 6 and add everything as shown in readme, I'm able to filter DBSets, using below snippet:

app.MapInstantAPIs<MyContext>(config =>
{
    config.IncludeTable(db => db.Contacts, ApiMethodsToGenerate.All, "addressBook");
});

but when I do the same in Progam.cs and Startup.cs approach (a .NET 3.1 project which is converted to .NET 6), nothing is filtered out - I get all the DBSets.

Here is my entire Startup.cs file:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Fritz.InstantAPIs;

namespace Test2
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Test2", Version = "v1" });
            });

            services.AddInstantAPIs();
        }

        // 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();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Test2 v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();

                endpoints.MapInstantAPIs<MyContext>(config =>
                {
                    config.IncludeTable(db => db.Contacts, ApiMethodsToGenerate.Get, "addressBook");
                });
            });
        }
    }
}

MyContext.cs

using Microsoft.EntityFrameworkCore;

public class MyContext : DbContext
{
    public MyContext(DbContextOptions<MyContext> options) : base(options) { }

    public DbSet<Contact> Contacts => Set<Contact>();
    public DbSet<Address> Addresses => Set<Address>();

}

public class Contact
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

Quick way to reproduce:

  1. Create a .NET 5 ASP.NET Core Web API
  2. Change TargetFramework to net6.0
  3. Update Swashbuckle.AspNetCore to 6.3.1
  4. Add Fritz.InstantAPIs
  5. Follow steps in readme

You will get this result:
image
Instead of:
image
when running from Program.cs approach:

using Fritz.InstantAPIs;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSqlite<MyContext>("Data Source=contacts.db");
builder.Services.AddInstantAPIs();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.MapInstantAPIs<MyContext>(config =>
{
    config.IncludeTable(db => db.Contacts, ApiMethodsToGenerate.Get, "addressBook");
});

app.Run();

@csharpfritz
Copy link
Owner

csharpfritz commented May 4, 2022 via email

@Misiu
Copy link
Author

Misiu commented May 4, 2022

@csharpfritz I agree on that, but with Progam.cs and Startup.cs approach the filtering (IncludeTable and ExcludeTable) isn't working.

@Misiu
Copy link
Author

Misiu commented May 4, 2022

In WebApplicationExtensions.cs I've changed

public static IEndpointRouteBuilder MapInstantAPIs<D>(this IEndpointRouteBuilder app, Action<InstantAPIsConfigBuilder<D>> options = null) where D : DbContext
{
	switch (app)
	{
		case IApplicationBuilder applicationBuilder:
			AddOpenAPIConfiguration(app, options, applicationBuilder);
			break;
		case IEndpointRouteBuilder routeBuilder:
			AddOpenAPIConfiguration(routeBuilder, options);
			break;
	}

	// Get the tables on the DbContext
	var dbTables = GetDbTablesForContext<D>();

	var requestedTables = !Configuration.Tables.Any() ?
			dbTables :
			Configuration.Tables.Where(t => dbTables.Any(db => db.Name.Equals(t.Name, StringComparison.OrdinalIgnoreCase))).ToArray();

	MapInstantAPIsUsingReflection<D>(app, requestedTables);

	return app;
}

and added:

private static void AddOpenAPIConfiguration<D>(IEndpointRouteBuilder routeBuilder, Action<InstantAPIsConfigBuilder<D>> options) where D : DbContext
{
	// Check if AddInstantAPIs was called by getting the service options and evaluate EnableSwagger property
	var serviceOptions = routeBuilder.ServiceProvider.GetRequiredService<IOptions<InstantAPIsServiceOptions>>().Value;
	if (serviceOptions == null || serviceOptions.EnableSwagger == null)
	{
		throw new ArgumentException("Call builder.Services.AddInstantAPIs(options) before MapInstantAPIs.");
	}

	//var webApp = (WebApplication)app;
	if (serviceOptions.EnableSwagger == EnableSwagger.Always || (serviceOptions.EnableSwagger == EnableSwagger.DevelopmentOnly /*&& webApp.Environment.IsDevelopment()*/))
	{
		//routeBuilder.UseSwagger();
		//routeBuilder.UseSwaggerUI();
	}

	var ctx = routeBuilder.ServiceProvider.CreateScope().ServiceProvider.GetService(typeof(D)) as D;
	var builder = new InstantAPIsConfigBuilder<D>(ctx);
	if (options != null)
	{
		options(builder);
		Configuration = builder.Build();
	}
}

Things that need to be added:
-checking if IsDevelopment()
-enabling SwaggerUI.

I'm not sure if InstantAPIs should enable and configure Swagger.
It's up to the developer to setup it, besides that, it's enabled by default in new projects.

@Misiu
Copy link
Author

Misiu commented May 6, 2022

@verbedr @csharpfritz thoughts on this? I can create PR showing my changes. Not everyone (including me) can migrate Startup and Program to .NET 6 preferred approach.

As I mentioned before when creating new projects Swagger UI is enabled by default (unless we uncheck a checkbox during project creation in VS2022), so we should only detect if it is enabled and add the correct endpoints.

Ideally Swashbuckle dependency should be removed, because some may prefer and use NSwag (ref: https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-6.0)
But this would require adding two more packages - one for Swagger and one for NSwag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants