Instantiation of POCO objects with ServiceStack's IAppSettings is not working - servicestack

I have registered AppSettings within my AppHost as shown below:
container.Register<IAppSettings>(new AppSettings());
I have added following settings within my web.config file:
<appSettings>
<add key="baseaddress" value="http://example.com" />
<add key="credential" value="{Username:foo,Password:bar}" />
</appSettings>
That's my service:
public class ExampleService : Service
{
public IAppSettings Settings { get; set; }
public void Post(ExampleRequest request)
{
// NOT WORKING -> always NULL!
var credentials = Settings.Get<Credential>("credential", null);
// OK
var baseUrl = Settings.GetString("baseaddress");
// more code...
}
}
This is my Credential class:
public class Credential
{
public string Username { get; set; }
public string Password { get; set; }
}
The baseUrl is always set but the credentials variable is always NULL and I don't know why...

In the latest v4.0.31 of ServiceStack there's First Class support for AppSettings so you don't have to register it yourself as it's already registered for you:
container.Register<IAppSettings>(new AppSettings());
But I've tested this and it works as expected where it deserializes into a populated Credential instance with:
var credential = AppSettings.Get<Credential>("credential", null);
Are you sure you're using the exact key names? as your last sentence uses baseUrl and credentials but your appSettings says baseaddress and credential.

You would need something like this in your config <security mode="TransportWithMessageCredential"> to automatically add a security header to your post. Or you need to add and parse your own headers. You don't show your full web config or a sample call to your "Post" so I am running on some assumptions here.

Related

Trying to follow .AddService & .UseService pattern

In my Minimal API, I use and integrate with Kofax TotalAgility WCF endpoints. I wanted to implement this integration properly, so I added a remote assembly and added the WCF contract in it along with the service interface and implementation:
Service Interface:
public interface IKofaxService
{
public Task<string> CreateJob(long letterId);
public Task ActionHandler(PortalActionRequest request);
}
Service implementation:
public class KofaxService : IKofaxService
{
private readonly ILogger<KofaxService> logger;
private readonly KofaxSetup config;
private readonly KtaJob.IJobService jobService;
private readonly KtaActivity.IActivityService activityService;
public KofaxService(ILogger<KofaxService> inLogger, KofaxSetup inConfig)
{
logger = inLogger;
// Here is the problem: THe constructor's parameter should be IOptions<Kofaxsetup> instead of just KofaxSetup and this below line will become:
// config = inConfig.Value;
config = inConfig;
//WCF Generated Stuff within this remote assembly
jobService = new KtaJob.JobServiceClient(GetBinding(), GetEndpointAddress(config.KtaUrlApiJob));
activityService = new KtaActivity.ActivityServiceClient(GetBinding(), GetEndpointAddress(config.KtaUrlApiActivity));
}
public async Task<string> CreateJob(long letterId)
{
...
}
public async Task ActionHandler(PortalActionRequest request)
{
...
}
}
In order to have a Servces.AddKofaxTotalAgility() like fluent API, I added the extension method like so (in the remote assembly):
Service extension method:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddKofaxTotalAgility(this IServiceCollection services)
{
services.AddScoped<IKofaxService, KofaxService>();
return services;
}
}
Also in the remote assembly, I have a class representing the setting object from appSetting's section:
Config class:
public class KofaxSetup
{
public string KtaUrlApiActivity { get; set; } = string.Empty;
public string KtaUrlApiJob { get; set; } = string.Empty;
public string SessionId { get; set; } = string.Empty;
public string ProcessId { get; set; } = string.Empty;
}
Back in the Minimal API project, I added a reference to the remote assembly and also have the settings in appSettings.json file:
appSettings.json:
{
...
"KofaxSetup": {
"KtaUrlApiActivity": "https://kofax.somewhere.com/TotalAgility/Services/SDK/ActivityService.svc",
"KtaUrlApiJob": "https://kofax.somewhere.com/TotalAgility/Services/SDK/JobService.svc",
"SessionId": "7DB87F70018D4770BF6114B1C9BA6041",
"ProcessId": "66EC6EED5D024E7AB0013D60F7A04A1A"
},
...
}
Lastly, modifications to Program.cs are as follows:
Minimal API Program.cs
var builder = WebApplication.CreateBuilder(args);
...
// Trigger KofaxSetting object from AppSetting's section
builder.Services.Configure<KofaxSetup>(builder.Configuration.GetSection(nameof(KofaxSetup)));
...
// Add the service to the DI
builder.Services.AddKofaxTotalAgility();
...
All of this just results in this exception at startup:
Exception # var app = builder.Build();
System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: DACRL.Integrations.Kofax.IKofaxService Lifetime: Scoped ImplementationType: DACRL.Integrations.Kofax.KofaxService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.) (Error while validating the service descriptor 'ServiceType: DACRL.Application.Core.Services.ILetterService Lifetime: Transient ImplementationType: DACRL.Api.Services.LetterService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.) (Error while validating the service descriptor 'ServiceType: DACRL.Application.Core.Services.ILetterService Lifetime: Transient ImplementationType: DACRL.Api.Services.LetterService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.)'
1/2:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: DACRL.Integrations.Kofax.IKofaxService Lifetime: Scoped ImplementationType: DACRL.Integrations.Kofax.KofaxService': Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.
2/2:
InvalidOperationException: Unable to resolve service for type 'DACRL.Integrations.Kofax.Configs.KofaxSetup' while attempting to activate 'DACRL.Integrations.Kofax.KofaxService'.
Note that the ILetterService is working properly, and this is the service that internally attempts to receive the IKofaxService from DI in its parameter. I'm thinking the error has something to do with the object KofaxSetup
Is there a best practice that I'm missing here? Am I supposed to have a parameter-less constructor somewhere? Is the Logger<KofaxService> injection within the service's implementation not valid?
I actually sorted the issue out but didn't want to waste a well-written question.
The problem was fact, the KofaxSetup class. I was receiving it as its type directly in the Service's constructor. I had to use IOptions<KofaxSetup> instead to solve the issue.

¿Why OAuth bearer auth not working in a MVC Web API when publishing to an API App in Azure?

I'm trying to use OAuth bearer authentication in a Web API application, everything works fine on my local IIS, I'm able to get the token as you can see here:
But when I publish my project to a ApiApp in Azure it doesn't work at all. I get the response:
{
"message": "No HTTP resource was found that matches the request URI 'https://mysite.azurewebsites.net/API/login'.",
"messageDetail": "No type was found that matches the controller named 'login'."
}
As shown in here:
My Startup.Auth class looks like:
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/login"),
Provider = new ApplicationOAuthProvider(),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
//#if DEBUG
AllowInsecureHttp = true
//#endif
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthAuthorizationServer(OAuthOptions);
}
}
I hope you can tell me what I'm doing wrong.
I've just find out that I was using an incorrect url. I was using /api/login instead of just /login.

context error- The entity type ApplicationUser is not part of the model for the current context

Well, I have tried many things without any help.
The situation is if I have my connection string as a regular connection string for example:
<add name="MyConnectionString" connectionString="Data Source=mydatasource;Connection Lifetime=10;Max Pool Size=800;Initial Catalog=mydb;Persist Security Info=True;MultipleActiveResultSets=True;User ID=myuser;Password=mypass" providerName="System.Data.SqlClient" />
I have this error:
The context is being used in Code First mode with code that was generated from an EDMX file for either Database First or Model First development. This will not work correctly. To fix this problem do not remove the line of code that throws this exception. If you wish to use Database First or Model First, then make sure that the Entity Framework connection string is included in the app.config or web.config of the start-up project. If you are creating your own DbConnection, then make sure that it is an EntityConnection and not some other type of DbConnection, and that you pass it to one of the base DbContext constructors that take a DbConnection. To learn more about Code First, Database First, and Model First see the Entity Framework documentation here: http://go.microsoft.com/fwlink/?LinkId=394715
and the error comes from here:
public partial class myConnectionString : DbContext
{
public myConnectionString()
: base("name=myConnectionString")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();// error comes from here
}
public virtual DbSet<AspNetRoles> AspNetRoles { get; set; }
public virtual DbSet<AspNetUserClaims> AspNetUserClaims { get; set; }
public virtual DbSet<AspNetUserLogins> AspNetUserLogins { get; set; }
public virtual DbSet<AspNetUsers> AspNetUsers { get; set; }
public virtual DbSet<ConnectedUsers> ConnectedUsers { get; set; }
}
If I have the EF connection string. for example:
<add name="myConnectionString" connectionString="metadata=res://*/HdkDb.csdl|res://*/HdkDb.ssdl|res://*/HdkDb.msl;provider=System.Data.SqlClient;provider connection string="data source=mydatasource;initial catalog=mydb;user id=myuser;password=mypass;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
I have this error:
The entity type ApplicationUser is not part of the model for the current context.
and the error comes from here:
var user = UserManager.Find(email, password);
this is what I have in my startup.auth.cs
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
Things to be noted:
I have Entity framework (EDMX) in one project, Authentication (IdentityModel, Usermanager) in another project. And I have using it from another MVC 5 project(where I have the startup.auth.cs).
In all of the projects app/web config, I have the same connection string.
How would I solve this? any help !!

I get a 500 page instead of a ResponseStatus from exceptions within ServiceStack Service

Using ServiceStack 4.0.33 and given the following simplified DTOs...
[Route("/products")]
[Route("/products/{Id}")]
public class Product : IReturn<ProductResponse>
{
[PrimaryKey]
public string Id { get; set; }
public string Description { get; set; }
}
public class ProductResponse
{
public Product Product { get; set; }
}
with the following simplified service...
public class ProductService : Service
{
public object Post(Product product)
{
Db.Insert<Product>(product);
return new ProductResponse() { Product = product };
}
}
and calling it via this in my ProductsController
using (var productService = ResolveService<ProductService>())
{
var result = productService.Post(product);
if (result.IsErrorResponse())
return View(product);
else
return RedirectToAction("Index");
}
If I try to post a new Product with an intentional duplicate primary key, I get a 500 error HTML-style page instead of the ResponseStatus getting populated and returned...
I've seen lots of different StackOverflow posts about different reasons that ResponseStatus won't get populated, but I've tried several things to no avail. Am I missing something (hopefully simple)?
Where do you define the behavior that a duplicate key would be handled differently than HTTP 500. What other response would you expect? ResponseStatus can contain HTTP 500 which merely indicates a server-side error.
You have to specifically define that you want to handle the error differently and if so what type of error you want (which error code, which message) as explained here.
For instance:
public object Get(User request)
{
throw HttpError.NotFound("User {0} does not exist".Fmt(request.Name));
}
Check out the ServiceStack tutorial for more.
The error page you're getting is a custom ASP.NET Error page which hijacks any custom 500 HTTP error responses returned by your ASP.NET web application.
It can be disabled with:
<system.web>
...
<customErrors mode="Off" />
</system.web>
<system.webServer>
<httpErrors errorMode="Detailed" />
</system.webServer>
Here's another alternative of disabling IIS errors by setting TrySkipIisCustomErrors = true which in ServiceStack can be set with:
GlobalRequestFilters.Add((req, res, dto) =>
{
((HttpResponse)res.OriginalResponse).TrySkipIisCustomErrors = true;
});

How do I configure the Users context/table?

With the new ASP.NET MVC 5 Preview released, how do I configure the Users context/table?
In MVC 4 I would just use my own User class and then point the WebSecurity initialize to it, tike this:
WebSecurity.InitializeDatabaseConnection(connectionString, "System.Data.SqlClient", userTableName, userIdColumn, userNameColumn, autoCreateTables);
I wish to add additional properties to the Users class - how?
I think, this can solve your issue:
In Models \ IdentityModels.cs you can redefine your own User model:
public class ApplicationUser : IdentityUser
{
/* identity field from database */
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Required]
public bool Internal { get; set; }
public string UserFullName { get; set; }
public string UserEmail { get; set; }
public ApplicationUser()
: base()
{
Internal = false;
}
public ApplicationUser(string userName)
: base(userName)
{
Internal = false;
}
}
now you can change mapping of defaults AspNet tables using OnModelCreating() overridding and ToTable() methode:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Change the name of the table to be Users instead of AspNetUsers
modelBuilder.Entity<IdentityUser>().ToTable("User");
modelBuilder.Entity<ApplicationUser>().ToTable("User");
modelBuilder.Entity<IdentityRole>().ToTable("Role");
modelBuilder.Entity<IdentityUserClaim>().ToTable("User_Claim");
modelBuilder.Entity<IdentityUserLogin>().ToTable("User_Login");
modelBuilder.Entity<IdentityUserRole>().ToTable("User_Role");
}
}
Finally you will see in the database the following tables:
User, Role, User_Role, User_Claim, User_Login instead of AspNetUsers, AspNetRoles, AspNetUsersRoles, AspNetUsersClaims, AspNetUserLogins.
Of course the User table will contain additional fields: UserId (int identity), Internal, UserFullName and UserEmail.
The UserStore and User classes are there to make EF based implementations easier, but you can always drop down and implement your own custom IUserStore and pass in your own DbContext.
I can provide a more detailed example if you need.
You can download a sample from https://github.com/rustd/AspnetIdentitySample. This is based on the ASP.NET MVC template that shipped with ASP.NET and Web Tools 2013 Preview Refresh (Supports English version of VS2013 Preview only) Once you have this Preview Refresh installed you can do the same for ASP.NET Web Forms and SPA applications.
Following are the steps to Run this project
Open the solution
Build and run
Register a user ---- Notice that the user registration field only has user name and password
Let's ask for a birthdate option from the user while registering an account.
Goto Nuget Package Manager console and run "Enable-Migrations"
Goto Models\AppModel.cs and uncomment BirthDate property in the MyUser class
Goto Models\AccountViewModels.cs and uncomment BirthDate property in RegisterViewModel
Goto AccountController and in Register Action and have the following code var user = new MyUser() { UserName = model.UserName,BirthDate=model.BirthDate }; //var user = new MyUser() { UserName = model.UserName };
Goto Views\Account\Register.cshtml and uncomment the HTML markup to add a BirthDate column
Goto Nuget Package Manager console and run "Add-Migration BirthDate"
Goto Nuget Package Manager console and run "Update-Database"
Run the application
When you register a user then you can enter BirthDate as well

Resources