ServiceStack Validation Not Always Firing - c#

So I was trying to build a End To End integration testing suite with RavenDB and ServiceStack but I ran into a really weird issue where the validation doesn't run on some requests. This is really strange and I am not sure what I am doing wrong. I am using NCrunch. Sometimes the test passes, sometimes it fails.
Hope this is an easy fix and something bone headed I'm doing.
You can download the whole project on http://github.com/khalidabuhakmeh/endtoend
You don't need anything other than VS2012 and NuGet Package Restore enabled.
UPDATE: I decided to run this in both NCrunch and Resharper Test Runner and both give the same result [see image below].
UPDATE UPDATE: I thought it could be XUnit, so I tried to use NUnit. Nope still the same problem.
**Another Update: Put in console writes as per user1901853's request. This was the result."
Latest Update: the RequestFilters are getting wiped out and I'm not sure why. It seems like it could be a threading issue, but I can't see where.
My AppHost is using AppHostListenerBase.
using EndToEnd.Core;
using Funq;
using Raven.Client;
using ServiceStack.ServiceInterface.Validation;
using ServiceStack.WebHost.Endpoints;
namespace EndToEnd
{
public class TestAppHost
: AppHostHttpListenerBase
{
private readonly IDocumentStore _documentStore;
public TestAppHost(IDocumentStore documentStore)
: base("Test AppHost Api", typeof(TestAppHost).Assembly)
{
_documentStore = documentStore;
}
public override void Configure(Container container)
{
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
// Register RavenDB things
container.Register(_documentStore);
container.Register(c =>
{
var db = c.Resolve<IDocumentStore>();
return db.OpenSession();
}).ReusedWithin(ReuseScope.Request);
Plugins.Add(new ValidationFeature());
container.RegisterValidators(typeof(CreateWidgetValidator).Assembly);
// todo: register all of your plugins here
AuthConfig.Start(this, container);
}
}
}
My base test class for all of my tests looks like this:
using Raven.Client;
using Raven.Client.Indexes;
using Raven.Tests.Helpers;
using ServiceStack.Authentication.RavenDb;
using ServiceStack.ServiceClient.Web;
using ServiceStack.ServiceInterface.Auth;
namespace EndToEnd
{
public abstract class ServiceStackTestBase
: RavenTestBase
{
protected IDocumentStore DocumentStore { get; set; }
protected TestAppHost Host { get; set; }
protected JsonServiceClient Client { get; set; }
protected const string ListeningOn = "http://localhost:1337/";
protected string Username { get { return "testuser"; } }
protected string Password { get { return "password"; } }
protected ServiceStackTestBase()
{
DocumentStore = NewDocumentStore();
IndexCreation.CreateIndexes(typeof(ServiceStackTestBase).Assembly, DocumentStore);
IndexCreation.CreateIndexes(typeof(RavenUserAuthRepository).Assembly, DocumentStore);
Host = new TestAppHost(DocumentStore);
Host.Init();
Host.Start(ListeningOn);
Client = new JsonServiceClient(ListeningOn)
{
AlwaysSendBasicAuthHeader = true,
UserName = Username,
Password = Password
};
RegisterUser();
WaitForIndexing(DocumentStore);
}
private void RegisterUser()
{
Client.Send(new Registration
{
UserName = Username,
Password = Password,
DisplayName = "Test User",
Email = "test#test.com",
FirstName = "test",
LastName = "user"
});
}
public override void Dispose()
{
DocumentStore.Dispose();
Host.Dispose();
}
}
}
my test class looks like this:
using System;
using EndToEnd.Core;
using FluentAssertions;
using ServiceStack.FluentValidation;
using ServiceStack.ServiceClient.Web;
using ServiceStack.ServiceInterface.Auth;
using Xunit;
namespace EndToEnd
{
public class RegistrationTests
: ServiceStackTestBase
{
[Fact]
public void Throws_validation_exception_when_bad_widget()
{
var validator = Host.Container.Resolve<IValidator<CreateWidget>>();
validator.Should().NotBeNull();
try
{
var response = Client.Post(new CreateWidget
{
Name = null
});
// It get's here every once in a while
throw new Exception("Should Not Get Here!");
}
catch (WebServiceException wex)
{
wex.StatusCode.Should().Be(400);
wex.ErrorMessage.Should().Be("'Name' should not be empty.");
}
}
}
}
My code looks like this for the service:
using System;
using Raven.Client;
using ServiceStack.FluentValidation;
using ServiceStack.ServiceHost;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.ServiceModel;
namespace EndToEnd.Core
{
[Authenticate]
public class WidgetsService
: Service
{
private readonly IDocumentSession _session;
public WidgetsService(IDocumentSession session)
{
_session = session;
}
public CreateWidgetResponse Post(CreateWidget input)
{
var widget = new Widget { Name = input.Name };
_session.Store(widget);
_session.SaveChanges();
return new CreateWidgetResponse { Widget = widget };
}
}
[Route("/widgets", "POST")]
public class CreateWidget : IReturn<CreateWidgetResponse>
{
public string Name { get; set; }
}
public class CreateWidgetResponse
{
public CreateWidgetResponse()
{
ResponseStatus = new ResponseStatus();
}
public Widget Widget { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
public class Widget
{
public Widget()
{
Created = DateTimeOffset.UtcNow;
}
public string Id { get; set; }
public string Name { get; set; }
public DateTimeOffset Created { get; set; }
}
public class CreateWidgetValidator : AbstractValidator<CreateWidget>
{
public CreateWidgetValidator()
{
RuleFor(m => m.Name).NotEmpty();
}
}
}

I don't have the ability to duplicate your environment but while running in VS2010, using .NET 4, NUnit and ReSharper Test Runner I have not been able to reproduce your issue of 'Validation not firing'. I have run your tests 30+ times. A couple of reasons I can think of Validation not firing would be not having the plugin added or the plugin not registering the validation filters. The 2 if statements below might give you some 'introspection' if either of the cases I listed are the issue. Hope this is somewhat helpful.
if (!TestAppHost.Instance.Plugins.Any(x => x.GetType() == typeof(ValidationFeature)))
{
Console.Write("Validation Plugin is not added");
//TestAppHost.Instance.Plugins.Add(new ValidationFeature());
}
if (!TestAppHost.Instance.RequestFilters.Any(x => x.Target.ToString() == "ServiceStack.ServiceInterface.Validation.ValidationFilters"))
{
Console.Write("No validation request filter");
//TestAppHost.Instance.Container.RegisterValidators(typeof(CreateWidgetValidator).Assembly);
}
Below is my packages.config so you can see the differences in our environments.
<packages>
<package id="FluentAssertions" version="2.0.1" targetFramework="net40" />
<package id="NUnit" version="2.6.2" targetFramework="net40" />
<package id="RavenDB.Client" version="2.0.2261" targetFramework="net40" />
<package id="RavenDB.Database" version="2.0.2261" targetFramework="net40" />
<package id="RavenDB.Embedded" version="2.0.2261" targetFramework="net40" />
<package id="RavenDB.Tests.Helpers" version="2.0.2261" targetFramework="net40" />
<package id="ServiceStack" version="3.9.38" targetFramework="net40-Client" />
<package id="ServiceStack.Authentication.RavenDB" version="3.9.35" targetFramework="net40" />
<package id="ServiceStack.Common" version="3.9.38" targetFramework="net40-Client" />
<package id="ServiceStack.OrmLite.SqlServer" version="3.9.39" targetFramework="net40-Client" />
<package id="ServiceStack.Redis" version="3.9.38" targetFramework="net40-Client" />
<package id="ServiceStack.Text" version="3.9.38" targetFramework="net40-Client" />
<package id="xunit" version="1.9.1" targetFramework="net40" />
</packages>

Related

Unittesting a ServiceStack service that uses AutoMapper

We are using ServiceStack for our .NET backend and I am trying to work on getting unit testing into the project. However there are some automated tools within ServiceStack that makes it a bit complicated to isolate the units so I could really use some advice. In the example below I would like to unit test a simple service that basically does the following:
Takes a request DTO
Passes the DTO to the repository
Gets back a domain model
If the model exists, it maps it to a responseDTO using Automapper and returns it as a part of an IHTTPResult
So the problem I have is that it seems like Automapper is automatically added to the ServiceStack application and in the application the mapper are registered by just calling:
AutoMapping.RegisterConverter().
So how could I inject this into the service to be able to do the unittest?
Example test:
using AutoMapper;
using FluentAssertions;
using NSubstitute;
namespace Api.Services.Tests.Unit;
public class OrderApiServiceTests
{
private readonly OrderApiService _sut;
private readonly IOrderApiRepository accountApiRepository = Substitute.For<IOrderApiRepository>();
public OrderApiServiceTests()
{
_sut = new OrderApiRepository(orderApiRepository);
var config = new MapperConfiguration(cfg => ApiDtoMapping.Register());
var mapper = config.CreateMapper();
}
[Fact]
public async Task Get_ShouldReturnAccount_WhenAccountExistsAsync()
{
// Arrange
var order = new Order
{
Name = "MyOrder",
Value = 1000,
};
var expectedResponse = new OrderApiDto
{
Name = "MyOrder",
Value = 1000,
};
orderApiRepository.GetAsync(Arg.Any<GetOrder>()).Returns(order);
// Act
var result = await _sut.Get(new GetOrder());
// Assert
result.StatusCode.Should().Be(System.Net.HttpStatusCode.OK);
result.Response.Should().BeEquivalentTo(expectedResponse);
}
}
Added a full example including all files:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// 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.UseHttpsRedirection();
}
app.UseServiceStack(new AppHost());
app.Run();
// Configure.AppHost.cs
using Funq;
using ssUnitTests.ServiceInterface;
[assembly: HostingStartup(typeof(ssUnitTests.AppHost))]
namespace ssUnitTests;
public class AppHost : AppHostBase, IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services =>
{
});
public AppHost() : base("ssUnitTests", typeof(MyServices).Assembly) { }
public override void Configure(Container container)
{
container.RegisterAutoWiredAs<OrderRepository, IOrderRepository>().ReusedWithin(ReuseScope.None);
// Configure ServiceStack only IOC, Config & Plugins
SetConfig(new HostConfig
{
UseSameSiteCookies = true,
});
Mappings.RegisterConverters();
}
}
// Mappings.cs
using ssUnitTests.ServiceModel;
namespace ssUnitTests;
public static class Mappings
{
public static void RegisterConverters()
{
AutoMapping.RegisterConverter((Order from) =>
{
var to = from.ConvertTo<OrderDto>();
to.DtoProperty = from.BaseProperty + "Dto";
return to;
});
}
}
// IOrderRepository.cs
using ssUnitTests.ServiceModel;
namespace ssUnitTests.ServiceInterface;
public interface IOrderRepository
{
Order GetOrder();
}
// Order.cs
namespace ssUnitTests.ServiceModel;
public class Order
{
public string Name { get; set; }
public string BaseProperty { get; set; }
}
// OrderDto.cs
namespace ssUnitTests.ServiceModel;
public class OrderDto
{
public string Name { get; set; }
public string DtoProperty { get; set; }
}
// OrderRequest.cs
using ServiceStack;
namespace ssUnitTests.ServiceModel;
[Route("/order")]
public class OrderRequest : IReturn<OrderDto>
{
public int Id { get; set; }
}
// UnitTest.cs
using NSubstitute;
using NUnit.Framework;
using ssUnitTests.ServiceInterface;
using ssUnitTests.ServiceModel;
namespace ssUnitTests.Tests;
public class UnitTest
{
private readonly MyServices _sut;
private readonly IOrderRepository _repository = Substitute.For<IOrderRepository>();
public UnitTest()
{
_sut = new MyServices(_repository);
}
[Test]
public void Get_ShouldReturn_OrderDto()
{
var order = new Order
{
Name = "MyName",
BaseProperty = "MyBaseProperty"
};
_repository.GetOrder().Returns(order);
var response = (OrderDto)_sut.Any(new OrderRequest { Id = 1 });
Assert.That(response.Name.Equals(order.Name));
Assert.That(response.DtoProperty.Equals(order.BaseProperty + "Dto"));
}
}
ServiceStack.dll does not have any dependencies to any 3rd Party Libraries, e.g. it's built-in AutoMapping is a completely different stand-alone implementation to AutoMapper.
If you're using AutoMapper you can ignore ServiceStack's AutoMapping which is completely unrelated.

Servicestack Test: Method not found: 'Int32 ServiceStack.DataAnnotations.CustomFieldAttribute.get_Order()

Trying to build integration test with connection to db in ServiceStack.
My ServiceStack app is working fine, but when I run simple test I got this error message in line:22
System.MissingMethodException: 'Method not found: 'Int32 ServiceStack.DataAnnotations.CustomFieldAttribute.get_Order()'.'
There is a lite cod:
using ServiceStack;
using ServiceStack.OrmLite;
using ServiceStack.Data;
using NUnit.Framework;
using ServiceStack.DataAnnotations;
using System.Collections.Generic;
namespace oth.Tests.IntegrationTests
{
public class AppHost2 : AppSelfHostBase
{
public AppHost2() : base("Customer REST Example", typeof(CustomerService).Assembly) { }
public override void Configure(Container container)
{
var connectionString = "Host=localhost;Port=5432;Database=test_1234;Username=postgres;Password=local";
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, PostgreSqlDialect.Provider));
using var db = container.Resolve<IDbConnectionFactory>().Open();
db.CreateTableIfNotExists<Customer>();
}
}
public class Customer
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
[Route("/customers", "GET")]
public class GetCustomers : IReturn<GetCustomersResponse> { }
public class GetCustomersResponse
{
public List<Customer> Results { get; set; }
}
public class CustomerService : Service
{
public object Get(GetCustomers request)
{
return new GetCustomersResponse { Results = Db.Select<Customer>() };
}
}
public class CustomerRestExample
{
const string BaseUri = "http://localhost:2000/";
ServiceStackHost appHost;
public CustomerRestExample()
{
//Start your AppHost on TestFixture SetUp
appHost = new AppHost2()
.Init()
.Start(BaseUri);
}
[OneTimeTearDown]
public void OneTimeTearDown() => appHost.Dispose();
/* Write your Integration Tests against the self-host instance */
[Test]
public void Run_Customer_REST_Example()
{
var client = new JsonServiceClient(BaseUri);
var all = client.Get(new GetCustomers());
Assert.That(all.Results.Count, Is.EqualTo(0));
}
}
}
Anytime you see a missing type or missing method exceptions when using the MyGet pre-release packages it means you have a dirty installation (i.e. using pre-release packages from different build times).
In which case you'd need to Clear your Nuget packages cache and download the latest packages again, which ensures all your packages are from the latest same build:
$ dotnet nuget locals all -clear

FCM (FirebaseMessagingService) OnNewToken not being hit with Android

I'm following the instructions on this page to create the push notifications. I've actually done it once before and was able to get it to work (a few weeks back), took some time away, and figured I'd do the tutorial again as a refresher only now, for some reason, I can't even get the code to hit the OnNewToken method to generate my token and register the device with the notification hub.
I've watched dozens of videos, read other tutorials, and they're all saying / showing pretty much the same thing so I think I need a new pair of eyes to show me what I'm missing this 2nd time around.
I've tried to pull out specific information but still keep it as readable as I could.
Installed NuGet packages:
Xamarin.Firebase.Messaging - v71.1740.0
Xamarin.GooglePlayServices.Base - v71.1610.0
Xamarin.Forms - v4.4.0.991640
Files in Android project
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="(my firebase package / project package name)" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" />
<uses-permission android:name="android.permission.INTERNET" />
<!--
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
-->
<application>
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
</application>
</manifest>
AppConstants
public static class AppConstants
{
public static string NotificationChannelName { get; set; } = "XamarinNotifyChannel";
public static string NotificationHubName { get; set; } = "(my azure notification hub name)";
public static string ListenConnectionString { get; set; } = "(my default listen shared access signature from azure portal)";
public static string DebugTag { get; set; } = "XamarinNotify";
public static string[] SubscriptionTags { get; set; } = { "default" };
public static string FCMTemplateBody { get; set; } = "{\"data\":{\"message\":\"$(messageParam)\"}}";
public static string APNTemplateBody { get; set; } = "{\"aps\":{\"alert\":\"$(messageParam)\"}}";
}
FirebaseService
[Service(Name = "(my package name).MyFirebaseMessagingService")]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseService : FirebaseMessagingService
{
public override void OnNewToken(string token)
{
base.OnNewToken(token);
Console.WriteLine("NEW_TOKEN", token);
SendRegistrationToServer(token);
}
void SendRegistrationToServer(string token)
{
NotificationHub hub = new NotificationHub(AppConstants.NotificationHubName, AppConstants.ListenConnectionString, this);
// register device with Azure Notification Hub using the token from FCM
Registration reg = hub.Register(token, AppConstants.SubscriptionTags);
// subscribe to the SubscriptionTags list with a simple template.
string pnsHandle = reg.PNSHandle;
hub.RegisterTemplate(pnsHandle, "defaultTemplate", AppConstants.FCMTemplateBody, AppConstants.SubscriptionTags);
}
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
string messageBody = string.Empty;
if (message.GetNotification() != null)
{
messageBody = message.GetNotification().Body;
}
else
{
messageBody = message.Data.Values.First();
}
try
{
MessagingCenter.Send(messageBody, "Update");
}
catch (Exception e)
{ }
SendLocalNotification(messageBody);
}
void SendLocalNotification(string body)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
intent.PutExtra("message", body);
//Unique request code to avoid PendingIntent collision.
var requestCode = new Random().Next();
var pendingIntent = PendingIntent.GetActivity(this, requestCode, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new NotificationCompat.Builder(this)
.SetContentTitle("XamarinNotify Message")
.SetSmallIcon(Resource.Drawable.ic_launcher)
.SetContentText(body)
.SetAutoCancel(true)
.SetShowWhen(false)
.SetContentIntent(pendingIntent);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
notificationBuilder.SetChannelId(AppConstants.NotificationChannelName);
}
var notificationManager = NotificationManager.FromContext(this);
notificationManager.Notify(0, notificationBuilder.Build());
}
}
google-services.json
I just downloaded this file from Firebase, added it to the Android project and set the Build Action to GoogleServicesJson.
Hopefully somebody can see what I'm missing as I've had this same tutorial working before.
Please uninstall the application in your android, then redeploy it.
onNewToken() will be called only once per installation.
If you need it to be called again, uninstall the app from your device and restart it.
You forgot [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
[Service()]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class FirebaseService : FirebaseMessagingService
{
After two days of troubleshooting, finally i found out that the latest versions of the below nuget packages (or may be one of them) is causing this. I downgraded to the below versions and it started working.
Xamarin.Azure.NotificationHubs.Android -> v0.6.0
Xamarin.Firebase.Messaging -> v71.1740.0
Xamarin.GooglePlayServices.Base -> v71.1610.0

Custom Localized Routes using IApplicationModelConvention are not respected in IUrlHelper.GetVirtualPathData

I'm currently porting an application from ASP.NET 4 to ASP.NET Core. I want to use attribute based routing while having the ability to localize the URLs.
The legacy application was using an approach using a custom IDirectRouteProvider. Since I didn't find the corresponding type in ASP.NET Core, I went with a solution inspired by https://www.strathweb.com/2015/11/localized-routes-with-asp-net-5-and-mvc-6/. Here's the implementation using an IApplicationModelConvention
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
o.Conventions.Insert(0, new LocalizedRouteConvention());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseRequestLocalization(new RequestLocalizationOptions { ... });
app.UseMvc();
}
}
public class LocalizedRouteConvention : IApplicationModelConvention
{
public void Apply(ApplicationModel application)
{
foreach (var controller in application.Controllers)
{
foreach (var action in controller.Actions)
{
var attributes = action.Attributes.OfType<RouteAttribute>().ToArray();
if (!attributes.Any()) return;
foreach (var attribute in attributes)
{
SelectorModel defaultSelector = action.Selectors.First();
foreach (var localizedVersion in GetLocalized(attribute.Template))
{
if (!action.Selectors.Any(s => s.AttributeRouteModel.Template == localizedVersion.Template))
{
action.Selectors.Insert(0, new SelectorModel(defaultSelector)
{
AttributeRouteModel = localizedVersion,
ActionConstraints =
{
new CultureActionConstraint { Culture = ((LocalizedRouteAttribute) localizedVersion.Attribute).Culture }
}
});
}
}
}
}
}
}
}
public class LocalizedRouteAttribute : RouteAttribute
{
public LocalizedRouteAttribute(string template) : base(template)
{
}
public string Culture { get; set; }
}
public class CultureActionConstraint : IActionConstraint
{
public string Culture { get; set; }
public int Order => 0;
public bool Accept(ActionConstraintContext context)
{
return CultureInfo.CurrentCulture.TwoLetterISOLanguageName == Culture;
}
}
Now, this approach works and the localized routes are only available when the correct request culture is set. However, when I use Html.ActionLink(...) or any other function that uses IUrlHelper.GetVirtualPathData(), the default route is returned instead of the localized one.
As far as I understand, the IUrlHelper will check the IRouteConstraints of a Route but it doesn't seem to respect the IActionConstraint. Unfortunately, I haven't found a way to set custom IRouteConstraints in my IApplicationModelConvention.

Authentication in Nancy MVC

I am using a third party authentication procedure to authorize my pages in Nancy. I have tried to do it in MVC and it is successfull but I cannot reproduce the same results in Nancy.
Here is what I am doing:
MVC-Startup:
using Microsoft.Owin;
using Owin;
using Passport.Auth;
[assembly: OwinStartup(typeof(TestAuthorization.Startup))]
namespace TestAuthorization
{
public partial class Startup:StartupBase
{
public void Configuration(IAppBuilder app)
{
base.Configuration(app);
}
public override string IdentityServerUri
{
get { return "https://test.ThirdParty.URL/identity"; }
}
public override string RedirectUri
{
get { return "https://localhost:4443"; }
}
public override string ApplicationClientId
{
get { return "local.fox.company"; }
}
}
}
Nancy-Startup:
using Microsoft.Owin;
using Owin;
using Passport.Auth;
using Nancy.Owin;
[assembly: OwinStartupAttribute(typeof(AgreementManagementTool.Startup))]
namespace AgreementManagementTool
{
public class Startup: StartupBase
{
public void Configuration(IAppBuilder app)
{
app.UseNancy();
base.Configuration(app);
}
public override string IdentityServerUri
{
get { return "https://test.ThirdParty.URL/identity"; }
}
public override string RedirectUri
{
get { return "https://localhost:4443"; }
}
public override string ApplicationClientId
{
get { return "local.fox.company"; }
}
}
}
Now here is my program.cs for Nancy only:
class Program
{
static void Main(string[] args)
{
var uri = "https://+:4443"; //"https://localhost:4443";
Console.WriteLine("Starting Nancy on " + uri);
using (WebApp.Start<Startup>(uri))
{
Console.WriteLine("\n\nServer listening at {0}. Press enter to stop", uri);
Console.ReadLine();
return;
}
}
}
Now all I have to do is write [Authorize] on top of my Nancy module and it should work just like MVC.
MVC-Controller:
using System.Web.Mvc;
namespace TestAuthorization.Controllers
{
[Authorize]
public class HomeController : Controller
{
public ActionResult Index()
{
//this.RequiresMSOwinAuthentication();
return View();
}
}
}
Nancy-Module:
using Nancy;
using AgreementManagementTool.Views.Home.Model;
using System.Web.Mvc;
namespace AgreementManagementTool.Modules
{
[Authorize]
public class HomeModule : NancyModule
{
public HomeModule()
: base("/home")
{
Get["/"] = parameters =>
{
//this.RequiresMSOwinAuthentication(); // Not working
//this.RequiresAuthentication(); // Not working
HomeModel result = new HomeModel();
result.ResultImport = "I am testing AUthentication";
return View["index", result];
};
}
}
}
When I browse to the page after running the MVC application run successfully and authorization work successfull, but nancy doesnot show anything.
I have tried to use this.RequiresAuthentication(); but it throws an exception:
Nancy-Exception
Just to mention that I have no idea how the third party authentication process works, I just have to use it.
In MVC I have recieved the sample and it is working fine, why is it not working the same in nancy.
Nancy does not use [Authorize] attribute.
Have a look sample using Identity server for Nancy specific implementation.
If you are just using Nancy, try this to replace both your startups (using Owin.Security.Cookies):
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
namespace TestOwin
{
public class Startup
{
// OWIN Configuration goes here
public static void Configuration(IAppBuilder app)
{
var cookieOptions = new CookieAuthenticationOptions() { AuthenticationMode = AuthenticationMode.Active };
var nancyCfg = new NancyOptions();
nancyCfg.Bootstrapper = new NBootstrapper();
app.UseStaticFiles();
app.UseCookieAuthentication(cookieOptions);
app.UseNancy(nancyCfg);
}
}
}
And in your NancyBootstrapper (Here is where you can redirect users to a login page):
public class NBootstrapper : DefaultNancyBootstrapper
{
//Nancy Bootstrapper overrides go here
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
// We don't call "base" here to prevent auto-discovery of
// types/dependencies
}
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
// Here we register our user mapper as a per-request singleton.
// As this is now per-request we could inject a request scoped
// database "context" or other request scoped services.
container.Register<IUserMapper, UserValidator>();
}
protected override void RequestStartup(TinyIoCContainer requestContainer, IPipelines pipelines, NancyContext context)
{
// At request startup we modify the request pipelines to
// include forms authentication - passing in our now request
// scoped user name mapper.
//
// The pipelines passed in here are specific to this request,
// so we can add/remove/update items in them as we please.
var formsAuthConfiguration =
new FormsAuthenticationConfiguration()
{
RedirectUrl = "~/login",
UserMapper = requestContainer.Resolve<IUserMapper>(),
};
FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
}
}
And then you can use:
this.RequiresMSOwinAuthentication();
on your modules.
Hope this helps! My problem is trying to also get the same authentication to work inside Nancy and outside too, with SignalR...

Categories