Cannot change port when migrating to .net core 3.1.3 from 2.2.0 - asp.net-core

On 2.2.0, I have the following to create a WebHostBuilder. I then call .Build().Run() on it
public IWebHostBuilder CreateWebHostBuilder(CommandLineOptions commandLineOptions, Type startupType, IConfiguration configuration, ILogger logger)
{
if (builder == null)
{
builder = WebHost.CreateDefaultBuilder(commandLineOptions?.Args)
.ConfigureAppConfiguration(configBuilder =>
{
configBuilder.Sources.Clear();
configBuilder.AddConfiguration(configuration);
})
.UseEnvironment(configuration.GetValue<string>("Environment"))
.UseStartup(startupType)
.UseUrls(configuration.GetValue<string>("HostUrl")) //"http://*:8000/"
.UseSerilog(logger);
}
return builder;
}
After upgrading to 3.1.3, it still works. I then changed to the below based on the migration doc
public IHostBuilder CreateHostBuilder(CommandLineOptions commandLineOptions, Type startupType, IConfiguration configuration, ILogger logger)
{
if (builder == null)
{
builder = Host.CreateDefaultBuilder(commandLineOptions?.Args)
.ConfigureAppConfiguration(configBuilder =>
{
configBuilder.Sources.Clear();
configBuilder.AddConfiguration(configuration);
})
.UseEnvironment(configuration.GetValue<string>("Environment"))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup(startupType);
webBuilder.UseUrls(configuration.GetValue<string>("HostUrl")); //"http://*:8000/"
})
.UseSerilog(logger);
}
return builder;
}
However, now no matter what I do it listens on the default ports of http://localhost:5000 and https://localhost:5001.
I don't have a hostsettings.json file, launchSettings.json also has nothing set, there's no setting in my enviroment variables. I've run from the command line specifying the desired url, but nothing works.
EDIT: Thanks to Martin Staufcik's suggestion to minimize the configuration, I've noticed that changing ConfigureAppConfiguration to ConfigureHostConfiguration has resolved the issue. I also noticed that if I left it as ConfigureAppConfiguration and remove the configBuilder.Sources.Clear(); line, it works too.
I've looked up the difference between the two, but I still don't understand what is happening here.

I lost a couple of hours working with .UseUrls myself. I finally got it working after noticing that the order of code lines was important! What worked in my case was calling UseUrls before UseStartup.
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls(configuration.GetValue<string>("HostUrl")); //"http://*:8000/"
webBuilder.UseStartup(startupType);
})

Related

Where is implemented/configured/declared that Identity UI /Account/Manage pages require authentication?

I've scaffolded all Identity UI pages to my ASP.NET Core (razor pages) project. The /Account/Manage pages should be restricted to authorized users, which is work perfectly, however I can not find where this restriction is implemented/configured/declared.
I mean no traces of [Authorize] attribute. I've also took a look into the original sources both 5.0.12 and 6.0.0 and the original UI source code also do not have such attribute
Question
Where is this authorization requirement (which is working, and are in effect) implemented, coded/declared?
I haven't followed the whole call stack from startup code:
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
but in IdentityDefaultUIConfigureOptions:
https://github.com/dotnet/aspnetcore/blob/v5.0.12/src/Identity/UI/src/IdentityDefaultUIConfigureOptions.cs
there is the code:
options.Conventions.AuthorizeAreaFolder(IdentityUIDefaultAreaName, "/Account/Manage");
options.Conventions.AuthorizeAreaPage(IdentityUIDefaultAreaName, "/Account/Logout");
which uses: https://github.com/dotnet/aspnetcore/blob/0bc3c376f0cd14335e8b3afca69596acabeb1f80/src/Mvc/Mvc.RazorPages/src/DependencyInjection/PageConventionCollectionExtensions.cs#L407
that adds the attributes dynamically:
public static PageConventionCollection AuthorizeAreaFolder(
this PageConventionCollection conventions,
string areaName,
string folderPath,
string policy)
{
if (conventions == null)
{
throw new ArgumentNullException(nameof(conventions));
}
if (string.IsNullOrEmpty(areaName))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(areaName));
}
if (string.IsNullOrEmpty(folderPath))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, nameof(folderPath));
}
conventions.AddAreaFolderApplicationModelConvention(areaName, folderPath, model =>
{
if (conventions.MvcOptions.EnableEndpointRouting)
{
model.EndpointMetadata.Add(new AuthorizeAttribute(policy));
}
else
{
model.Filters.Add(new AuthorizeFilter(policy));
}
});
return conventions;
}

Custom OData Formatter for netcore 3.1 WebApi Issues

So i am building a service that has to be consumed through OData and i am having a really difficult time figuring how to add custom formatters to it. I need my OData serializer to ignore null values when serializing data. I have created these 2 to achieve that :
public class SmartODataSerializerProvider : DefaultODataSerializerProvider
{
private readonly SmartODataEntityTypeSerializer _entityTypeSerializer;
public SmartODataSerializerProvider(IServiceProvider rootContainer)
: base(rootContainer)
{
_entityTypeSerializer = new SmartODataEntityTypeSerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
{
// Support for Entity types AND Complex types
if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
return _entityTypeSerializer;
else
return base.GetEdmTypeSerializer(edmType);
}
}
And
public class SmartODataEntityTypeSerializer : ODataResourceSerializer
{
public SmartODataEntityTypeSerializer(ODataSerializerProvider provider)
: base(provider) { }
/// <summary>
/// Only return properties that are not null
/// </summary>
/// <param name="structuralProperty">The EDM structural property being written.</param>
/// <param name="resourceContext">The context for the entity instance being written.</param>
/// <returns>The property be written by the serilizer, a null response will effectively skip this property.</returns>
public override Microsoft.OData.ODataProperty CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
{
var property = base.CreateStructuralProperty(structuralProperty, resourceContext);
return property.Value != null ? property : null;
}
}
These were provided on another stack overflow question. However, the issue arises when i try to use this serializer. I already have an odata endpoint that's working (it just serializes everything with null) and when i apply the following configuration to it i keep getting '404 Not Found' on the same EP that works without it.
app.UseEndpoints(endpoints =>
{
endpoints.MapODataRoute("odata", "odata", a =>
{
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => GetEdmModel(app.ApplicationServices));
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), sp => new SmartODataSerializerProvider(sp));
});
endpoints.EnableDependencyInjection();
//endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));
endpoints.MapControllers();
});
This is the endpoints settings. I commented out the line that makes it work but without custom formatters. Here's the IEdmModel function used in the setup :
private static IEdmModel GetEdmModel(IServiceProvider services)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder(services);
builder.Namespace = "RS";
builder.EntitySet<PropertyIndexODataModel>("Properties");
builder.EntitySet<ReportResultModel>("Reports");
var function = builder.EntityType<ReportResultModel>().Collection.Function("Generate");
function.Parameter<int>("listId");
function.CollectionParameter<string>("functionsToRun");
function.ReturnsCollectionFromEntitySet<ReportResultModel>("Reports");
return builder.GetEdmModel();
}
So when i apply this odataroute i keep getting the 404. When i remove it and go back to 'endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));' it works without problems.
This seems like a very trivial thing but i searched everywhere and i still couldn't get it to work. I am using OData 7.4 and netcore 3.1. Thanks in advance!
I think what's happening here is that the MapODataRoute is missing the routing configuration. Try adding the following after the SmartODataSerializerProvider registration:
a.AddService<IEnumerable<IODataRoutingConvention>>(Microsoft.OData.ServiceLifetime.Singleton, sp =>
ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", endPoints.ServiceProvider));
I had the same problem and this fixed it for me. See this issue for more details.

DryIoc: Enabling 'throwIfUnresolved' always throw an exception

I want to know name of dependencies that are missed (not injected)!
I Use this extensions:
DryIoc.dll v4.0.7
DryIoc.WebApi.dll v4.0.0
DryIoc.Web.dll v4.0.0
and I tried to use throwIfUnresolved parameter to solve the problem
Like this:
public static void Register(HttpConfiguration config)
{
var container = new Container().WithWebApi(config, throwIfUnresolved: type => true);
//...
}
but after that I always get this exception even if there is no dependency!
DryIoc.ContainerException: 'Unable to resolve ModelMetadataProvider IsResolutionCall
from Container with ambient ScopeContext DryIoc.AsyncExecutionFlowScopeContext without Scope
Where no service registrations found
and no dynamic registrations found in 0 of Rules.DynamicServiceProviders
and nothing found in 0 of Rules.UnknownServiceResolvers'
Use this:
public static void Register(HttpConfiguration config)
{
var container = new Container()
.WithWebApi(
config,
throwIfUnresolved: type => type.IsController());
//...
}

FluentValidation errors to Logger

I'm registering my FluentValidation validators as follows:
services.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<CustomRequestValidator>());
How can I get a handle of any validation errors to log them via my logging implementation, e.g. Serilog?
For users stumbling into this problem (like me) I'd like to provide the solution I found.
The actual problem is that ASP.NET Core, and APIs in particular, make Model State errors automatically trigger a 400 error response, which doesn't log the validation errors.
Below that same documentation they include instructions on how to enable automatic logging for that feature which I believe should be the correct solution to this problem.
To jump onto what #mdarefull said, I did the following:
public void ConfigureServices(IServiceCollection services)
{
// Possible other service configurations
services.AddMvc()
.ConfigureApiBehaviorOptions(options =>
{
options.InvalidModelStateResponseFactory = context =>
{
if (!context.ModelState.IsValid)
{
LogAutomaticBadRequest(context);
}
return new BadRequestObjectResult(context.ModelState);
};
});
// Possible other service configurations
}
The LogAutomaticBadRequest method is as follows:
private static void LogAutomaticBadRequest(ActionContext context)
{
// Setup logger from DI - as explained in https://github.com/dotnet/AspNetCore.Docs/issues/12157
var loggerFactory = context.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger(context.ActionDescriptor.DisplayName);
// Get error messages
var errorMessages = string.Join(" | ", context.ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
var request = context.HttpContext.Request;
// Use whatever logging information you want
logger.LogError("Automatic Bad Request occurred." +
$"{System.Environment.NewLine}Error(s): {errorMessages}" +
$"{System.Environment.NewLine}|{request.Method}| Full URL: {request.Path}{request.QueryString}");
}

Formula Mapping in NHibernate to Functions in SQLite or Override Mapping

i found the following article http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx to help me unit test my NHibernate application. Most of my tests are working fine but a strange error has been thrown. From looking at the SQL generated i believe the following fluent mapping is causing the problems:
Map(x => x.IsValid).Formula("CASE WHEN dbo.NumPosts(UserID) = 5 THEN 1 ELSE 0 END");
You'll notice the call to dbo.NumPosts which is a user defined function within my database. I know there's other ways i could've mapped this property but this is just used as an example. Basically i need to know how to map this using SQLite.
Edit:
After further thoughts would, is it possible to override the mapping for this field in my unit testing project? Here's my current configuration:
private static ISessionFactory CreateSessionFactory() {
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
.Mappings(m => m.FluentMappings
.AddFromAssembly(typeof(Role).Assembly)
.Conventions.AddFromAssemblyOf<EnumConvention>())
.ExposeConfiguration(c => _configuration = c)
.BuildSessionFactory();
}
I don't wish to re-define all the mappings as this would take some time and would become un-maintainable.
I'd appreciate the help. Thanks
In reference to the answer in your comment, I was able to get a static bool property to work.
Still I didn't like relying on a global bool variable so I decided to dig further. In NHibernate 3.0 they added events to the Configuration object. In particular I was able to take advantage of the new BeforeBind event on the Configuration object.
I rewrote slightly your CreateSessionFactory() method to show how I did it.
NOTE: In my formula's I always write them as dbo.NumPosts, and the event handler will remove the 'dbo.' if we are running within a SQLite Dialect.
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
.Mappings(m => m.FluentMappings
.AddFromAssembly(typeof (Role).Assembly)
.Conventions.AddFromAssemblyOf<EnumConvention>())
.ExposeConfiguration(c =>
{
c.BeforeBindMapping += BeforeBindMappingHandler;
_configuration = c;
})
.BuildConfiguration()
.BuildSessionFactory();
}
private static void BeforeBindMappingHandler(object sender, BindMappingEventArgs e)
{
if (!(e.Dialect is SQLiteDialect)) return;
var properties = e.Mapping.RootClasses
.SelectMany(r => r.Properties)
.Where(p => p is HbmProperty)
.Cast<HbmProperty>()
.Where(p => p.Formulas.Any());
foreach (var hbmProperty in properties)
hbmProperty.formula = hbmProperty.formula.ToLower().Replace("dbo.", "");
}
Problem solved! I was able to say:
[SQLiteFunction(Name = "NumPosts", Arguments = 1, FuncType = FunctionType.Scalar)]
public class NumPosts : SQLiteFunction {
public override object Invoke(object[] args) {
...
}
}
Now all i had to do was add a setting to add the dbo prefix infront of my functions. This is then set to blank in the test project.