Role-based navigation display in MVC4 Bootstrap Sample - asp.net-mvc

How are you supposed to conditionally display menu items based on roles in the Bootstrap Sample project? I was thinking of doing the following
Implement INavigatonRouteFilter - really just implementing the shouldRemove(Route navigationRoutes) method - by getting the default controller/action for the route and seeing if the user is authorized
Call NavigationRoutes.Filters.Add(myAuthorizationFilter) after configuring the NavigationRoutes in App_Start
There are two problems I see with this approach:
I don't actually know how to do the first step unless I add in a bunch of conditional statements to check for Controller's name explicitly
This seems like it could make NavigationRoutes.Filters very hard to deal with once there are a lot of filters or a desire for more modularity later on
I don't know that I've explained the problem clearly enough, but basically I want to use what is provided in the Bootstrap sample to implement authorization-based navigation menu display if at all possible. Using INavigationRouteFilter just seemed like the most natural way to do so.

For those looking for an answer or at least a quick fix.
Here's what I've come up with after 5 minutes and I most certainly haven't though about any side effects this may have.
routes.MapNavigationRoute<HomeController>("Index", c => c.Index())
.FilterRoute(() => !WebSecurity.IsAuthenticated);
You can either do all your filtering in your call to FilterRoute() or you can add more extension methods to save you some characters.
I'm thinking of .RequireRole("Adiministrators"); that calls WebSecurity.RequireRoles() in turn (or HttpContext.Current.User.IsInRole()) etc.
public static NavigationRouteBuilder FilterRoute(this NavigationRouteBuilder builder, Func<bool> func)
{
var currentRoute = builder._parent;
NavigationRoutes.Filters.Add(new BootstrapAuthorizationFilter(builder, x =>
{
if (x == currentRoute)
return func();
else
return false;
}));
return builder;
}
and BootstrapAuthorizationFilter is just a class implementing INavigationRouteFilter that calls func() in its ShouldRemove() method
public class BootstrapAuthorizationFilter : INavigationRouteFilter
{
private NavigationRouteBuilder builder;
private Func<NamedRoute, bool> func;
public BootstrapAuthorizationFilter(NavigationRouteBuilder builder, Func<NamedRoute, bool> func)
{
this.builder = builder;
this.func = func;
}
public bool ShouldRemove(Route navigationRoutes)
{
if (navigationRoutes is NamedRoute)
return func(navigationRoutes as NamedRoute);
return false;
}
}
Clearly nothing fancy and I'm not sure if I'd use it in production.
But I think is simple enough and works (for the cases I tested).
Having said that, I hope the new routing functionality is going to be released soon :)

Related

ZF2, dependencies which I don't know at start

In my controller, via service, I get from DB a list of the names of widgets (eg. chart, calendar, etc). Every widget implements WidgetInterface and may need other services as its own dependencies. The list of widgets can be different for each user, so I don't know which widgets / dependencies I will need in my controller. Generally, I put dependencies via DI, using factories, but in this case I don't know dependencies at the time of controller initialization.
I want to avoid using service locator directly in controller. How can I manage that issue? Should I get a list of the names of widgets in controller factory? And depending on widgets list get all dependencies and put them to controller?
Thanks, Tom
Solution
I solved my issue in a way that suggested Kwido and Sven Buis, it means, I built my own Plugin Manager.
Advantages: I do not need use service locator directly in controller and I have clear and extensible way to get different kinds of widgets.
Thank you.
Create your own Manager, like some sort of ServiceManager, for your widgets.
class WidgetManager extends AbstractPluginManager
Take a look at: Samsonik tutorial - pluginManager. So this way you can inject the WidgetManager and only retrieve the widgets from this manager as your function: validatePlugin, checks whether or not the fetched instance is using the WidgetInterface. Keep in mind that you can still call the parent ServiceManager.
Or keep it simple and build a plugin for your controller that maps your widget names to the service. This plugin can then use the serviceLocator/Manager to retrieve your widget(s), whether they're created by factories or invokableFactories. So you dont inject all the widget directly but only fetch them when they're requested. Something realy simplistic:
protected $map = [
// Widget name within the plugin => Name or class to call from the serviceManager
'Charts' => Widget\Charts::class,
];
public function load($name)
{
if (array_key_exists($name, $this->map)) {
return $this->getServiceManager()->get($this->map[$name]);
}
return null;
}
Injecting all the Widgets might be bad for your performance so you might consider something else, as when the list of your widgets grow so will the time to handle your request.
Hope this helped you and pushed you in some direction.
This indeed is a interesting question. You could consider using Plugins for the widgets, which can be loaded on the fly.
Depency injection is a good practise, but sometimes, with dynamic content, impossible to implement.
Another way to do this, is to make your own widget-manager. This manager then can load the specific widgets you need. The widget-manager can be injected into the controller.
Edit:
As you can see above, same idea from #kwido.
I would use a separate service and inject that into the controller.
interface UserWidgetServiceInterface
{
public function __construct(array $widgets);
public function getWidget($name);
}
The controller factory
class MyControllerFactory
{
public function __invoke(ControllerManager $controllerManager, $name, $requestedName)
{
$serviceLocator = $controllerManager->getServiceLocator();
$userWidgetService = $serviceLocator->get('UserWidgetService');
return new MyController($userWidgetService);
}
}
Then the logic to load the widgets would be moved to the UserWidgetServiceFactory.
public function UserWidgetServiceFactory
{
public function __invoke(ServiceManager $serviceLocator, $name, $requestedName)
{
$userId = 123; // Load from somewhere e.g session, auth service.
$widgetNames = $this->getWidgetNames($serviceLocator, $userId);
$widgets = $this->loadWidgets($serviceManager, $widgetNames);
return new UserWidgetService($widgets);
}
public function getWidgetNames(ServiceManager $sm, $userId)
{
return ['foo','bar'];
}
public function loadWidgets(serviceManager $sm, array $widgets)
{
$w = [];
foreach($widgets as $widgetName) {
$w[$widgetName] = $sm->get($widgetName);
}
return $w;
}
}
The call to loadWidgets() would eager load all the widgets; should you wish to optimise this you could register your widgets as LazyServices

HTML Extensions for wrapping other extensions

Most times when using DevEx MVC extensions, I find myself having to use repetitive code to generate the controls/layouts that I use on a regular basis. For example, I tend to prefer left-aligned captions to controls. Ideally, I'd be able to do something like this and have it "just work":
#Html.DevExpress().TextBoxFor(m => m.Notes).GetHtml()
However, in order to place the caption on the left, I need to also pass in a settings object for it, or dome something much more verbose such as:
#Html.DevExpress().TextBox(
s => {
// ...
s.Properties.CaptionCellStyle.Width = 100;
// ...
}
).Bind(Model.Notes).GetHtml()
What I thought to do was create my own set of extensions that would wrap the DevEx extensions, giving me some sort of common/core customization layer, so I could do something like this:
#Html.MyComponents().TextBoxFor(m => m.Notes)
This call would in turn call the DevExpress TextBoxExtension with a common set of settings and output the DevEx textbox as desired.
I've worked this up via custom html extensions (code below), but have two issues with this basic implementation:
The control renders at the very top of the Body element, not at the position in which it's placed in the view, and
There's a JavaScript error "ASPxClientTextBox" is not defined (this is part of the client-side scripting that DevEx uses with their controls).
I was hoping this would be an easy "wrap it and go" type of scenario. Is there some basic concept of these custom HTML extensions that I'm missing? Or does anyone know if this is a general limitation in trying to wrap another company's pre-existing extensions?
Here's the code I have implemented (which is not yet fully fleshed out, just trying for proof of concept here):
public static class HtmlHelpers
{
public static MyComponentsHtmlHelpers<TModel> MyComponents<TModel>(this HtmlHelper<TModel> html) {
return new MyComponentsHtmlHelpers<TModel>(html);
}
}
public class MyComponentsHtmlHelpers<TModel>
{
private HtmlHelper<TModel> html;
public MyComponentsHtmlHelpers(HtmlHelper<TModel> html) {
this.html = html;
}
public MvcHtmlString TextBoxFor<TValue>(Expression<Func<TModel, TValue>> expression) {
var data = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string propertyName = data.PropertyName;
System.Diagnostics.Debug.WriteLine("PROPERTY NAME: " + propertyName);
TextBoxSettings s = new TextBoxSettings();
s.Name = "textbox_" + propertyName;
s.Properties.Caption = propertyName;
s.Properties.CaptionCellStyle.Width = 100;
TextBoxExtension tb = new TextBoxExtension(s);
return tb.GetHtml();
}
}
Nevermind - cleaning the solution and rebuilding as well as force refreshing the test browser a few times and ensure there's no cache and... now everything works as intended.
(not sure if I ought to delete this question or leave it around in case someone wants to attempt the same... I'll leave it for now, feel free to remove)

May a scenario not have a When in BDD?

I'm currently learning/testing BDD using SpecFlow, and it works great!
Before I choose to ask my question, I have read this one, and I felt like I had to ask my question despite the fact that the same problem is addressed, because of the Exception scenario which is not mentioned.
I'm actually testing this scenario:
Scenario: I should get an error whenever I try to remove an item from an empty stack
Given I have an empty stack
When I pop from it
Then I should get an error
public class StackBehaviour {
public void GivenIHaveAnEmptyStack() { stack = new CustomStack<string>(); }
// This will throw whenever called!
// So the Then method will never be run!
// I feel like I should just put a comment which says why it's empty,
// allowing a fellow programmer to understand the exact intention.
public void WhenIPopFromIt() { stack.Pop(); }
// It is here that it verifies whether the CustomStack meets the expected behaviour.
public void ThenIShouldGetAnError() {
Assert.Throws<IndexOutOfRangeException>(delegate {
stack.Pop();
});
}
private CustomStack<string> stack;
}
public class CustomStack<T> {
public T Pop() {
if (stack.Count == 0)
throw new IndexOutOfRangeException("Cannot pop from an empty stack!");
T item = stack[stack.Count-1];
stack.RemoveAt(stack.Count-1);
return item;
}
private ArrayList stack = new ArrayList();
}
I think that leaving a comment in the When method is correct, so that the business requirement doesn't lack any information, and on the code behind, I put it clear what my intention is exactly by commenting.
What do you think? Any other ideas why I shouldn't make it?
There is another trick that you can use where the bindings help make the feature's meaning a little clearer. In this case, let's start at the end.
Then I should get an error
Your problem is a basically here, you know want an error, but you don't know how to get it. In fact you've already missed it, the error has already occurred in the When step, and in your code example, you moved the action into the then step just so you could get the error.
But what if keep the when performing the action amd re-express our then to reflect what really happens
Then I should have had an error
Seems a trivial change but now our feature reflects that the error should have been associated with the When step and we are simply evaluating it later, and that is something we can code. We just need something to remember the error in the when and deliver it to the then.
private Exception errorFromWhen = null;
public void WhenIPopFromIt()
{
try
{
stack.Pop();
}
catch(Exception ex)
{
errorFromWhen = ex;
}
}
public void ThenIShouldGetAnError()
{
errorFromWhen.ShouldNotBeNull();
errorFromWhen.ShouldBe<IndexOutOfRangeException>();
}
SpecFlow has absolutely no problems with this, in fact due to its mini Dependency injection system, you can even pass this state between binding classes if necessary.
May a scenario not have a When in BDD?
In Specflow, neither given, when or then are mandatory.
However in your example, I don't believe this is a good use of Specflow and BDD. In this answer here Marcus states:
"BDD is about ensuring that we're building the right thing, TDD is about ensuring that we're building it right."
In your example the scope of what is being tested i.e. the CustomStack, should be tested via TDD. It is the end solution that makes use of the CustomStack should be tested via BDD (and hence SpecFlow) e.g. if this CustomStack was being exercised via a certain action on a website.
This answer is also pertinent to your question.

Streaming text output for long-running action?

I have a few utility actions that return text output via return Content("my text","text/plain").
Sometimes these methods take a few minutes to run (i.e. log parsing, database maintenance).
I would like to modify my action method so that instead of returning all of the output at once, the text is instead streamed to the client when it is ready.
Here's a contrived example:
public ActionResult SlowText()
{
var sb = new System.Text.StringBuilder();
sb.AppendLine("This happens quickly...");
sb.AppendLine("Starting a slow 10 second process...");
System.Threading.Thread.Sleep(10000);
sb.AppendLine("All done with 10 second process!");
return Content(sb.ToString(), "text/plain");
}
As written, this action will return three lines of text after 10 seconds. What I want is a way to keep the response stream open, and return the first two lines immediately, and then the third line after 10 seconds.
I remember doing this 10+ years ago in Classic ASP 3.0 using the Response object. Is there an official, MVC-friendly way to accomplish this?
--
Update: using Razor .cshtml in the app; but not using any views (just ContentResult) for these actions.
Writing directly to the Response object should work, but only in some simple cases. Many MVC features depend on output writer substitution (e.g. partial views, Razor view engine, and others) and if you write directly to the Response your result will be out of order.
However, if you don't use a view and instead write straight in the controller then you should be fine (assuming your action is not being called as a child action).
I would skip the MVC controller entirely since you are going to break encapsulation anyway. In it's place I'd use a barenaked IHttpHandler implementation, streaming directly to the aforementioned output stream.
You are exposing yourself to a browser timeout if the process takes longer than originally intended. Then you don't have a way to recover what happened / unless you implement a separate method that gives the information on the long running process.
Given that you want the other method anyway, you can start a long running process and return immediately. Have the browser check the other method that gives the latest information on the long running process. On the last time I had to do this, I kept it simple and just set the refresh header from the controller before returning the view.
As for starting a long running process, you can do something like this:
// in the controller class
delegate void MyLongProcess();
//...
// in the method that starts the action
MyLongProcess processTask = new MyLongProcess(_someInstance.TheLongRunningImplementation);
processTask.BeginInvoke(new AsyncCallback(EndMyLongProcess), processTask);
//...
public void EndMyLongProcess(IAsyncResult result)
{
try{
MyLongProcess processTask = (MyLongProcess)result.AsyncState;
processTask.EndInvoke(result);
// anything you needed at the end of the process
} catch(Exception ex) {
// an error happened, make sure to log this
// as it won't hit the global.asax error handler
}
}
As for where do you put the log of the actions that happened, it's up to you to how long lived you want it to be. It can be as simple as a static field/class where you add the info of the ongoing process, or instead saving it to a data store where it can survive an application recycle.
The above assume this is all about a long running process that goes on reporting the actions that has been done. Streaming is a different subject, but the above might still play a role in keeping the operations in your controller & only the piece responsible of streaming what becomes available to the client in the action result.
You can implement your custom ActionResult like ContentStreamingResult and use HttpContext, HttpRequest and HttpResponse in the ExecuteResult method.
public class ContentStreamingResult : ActionResult
{
private readonly TextReader _reader;
public ContentStreamingResult(TextReader reader)
{
_reader = reader;
}
public override void ExecuteResult(ControllerContext context)
{
var httpContext = context.HttpContext;
//Read text from the reader and write to the response
}
}
public class YourController : Controller
{
public ContentStreamingResult DownloadText()
{
string text = "text text text";
return new ContentStreamingResult(new System.IO.StringReader(text));
}
}
Try Response.Flush and BufferOutput to false. Note it would work with the different action results, you have to directly write into the response object. Probably you can use it with conjunction with AsyncController.

ASP.NET MVC tests with MSpec. Is this correct or needs refactoring?

I'm new to MSpec and would like to know if the way I wrote my test for ASP.NET MVC is correct. The test passes but I don't really like the way it's written and it seems awkward. I'm certainly missing something.
public class AccountControllerTests3
{
protected static AccountController controller;
static IFormsAuthenticationService formsService;
static IMembershipService membershipService;
protected static ActionResult result;
protected static LogOnModel model;
Establish context = () =>
{
var controllerBuilder = new TestControllerBuilder();
formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
membershipService = MockRepository.GenerateStub<IMembershipService>();
model = MockRepository.GenerateStub<LogOnModel>();
controller =
controllerBuilder.CreateController<AccountController>(new object[]
{
formsService,
membershipService
});
};
Because user_logs = () =>
{
bool rememberMe = false;
membershipService.Stub(
x => x.ValidateUser("bdd", "mspec")).Return(true);
formsService.Stub(x => x.SignIn("bdd", rememberMe));
controller.ModelState.IsValid.ShouldBeTrue();
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_good_login_and_password : AccountControllerTests3
{
private It user_should_be_redirected_to_the_home_page = () =>
{
model.UserName = "bdd";
model.Password = "mspec";
result = controller.LogOn(model, string.Empty);
result.AssertActionRedirect().ToAction<HomeController>(
x => x.Index());
};
}
[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_bad_login_and_password : AccountControllerTests3
{
It The_error_message_should_be_shown = () =>
{
model.UserName = "BAD";
model.Password = "BAD";
result = controller.LogOn(model, string.Empty);
controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual(
"The user name or password provided is incorrect.");
};
}
Thanks in advance,
Thomas
One of my goals when I write tests with MSpec is to get the "Assert" or the "It" down to one line. MSpec is not like NUnit in that it only executes the Context (made up of the Establish clauses from the current class and all base classes and the Because clause) once followed by all of the Specifications (It clauses).
This is designed explicitly to force you to not perform any behavior in the It clauses, but rather observe it.
What you're actually doing here is using MSpec like NUnit. Try and rewrite the tests in a single class (using no inheritance). Work backwards... in the It, place a single line that asserts what you want to assert. Perhaps the AssertRedirect. In the Because, try and put a single line that causes the observations to be observable. This would probably be your call to the controller's logon method. Finally, in the "Establish context" you'd want to put everything else.
After a while, you may want to pull some of the things in the Establish context only into a base class, but in doing so, be sure that your entire subclass stands alone in terms of understanding. A reader shouldn't need to read the base class in order to understand what the actual spec is doing. It's ok to hide ceremonial implementation details, but be sure to hide them behind descriptive method names.
There's another line I'm not sure about:
controller.ModelState.IsValid.ShouldBeTrue();
If this is a test, it should probably be in its own It clause. Though really, do you want to test this? What are you testing here? Shouldn't your controller take an action based on whether or not the model is valid? Shouldn't the result of that action be observable (validation error instead of login error). I just wonder if you really need to test this.
A few other things to check out, first for styling with R#, it seems your tests are falling victim to R#'s defaults. I posted about how to fight this here:
http://codebetter.com/blogs/aaron.jensen/archive/2008/10/19/getting-resharper-and-vs-to-play-nice-with-mspec.aspx
Also, James Broome has some nice MVC MSpec extensions that are worth checking out:
http://jamesbroo.me/introducing-machinespecificationsmvc/
Good luck and Enjoy! Feel free to ping me on twitter if you have any other unrelated questions.
Here's a remark: instead of using CreateController method use InitializeController, because it is compile-time safer and refactor friendlier.
Instead of:
controller = controllerBuilder.CreateController<AccountController>(
new object[] { formsService, membershipService });
Do:
controller = new AccountController(formsService, membershipService);
controllerBuilder.InitializeController(controller);
The first will still compile if you change the controller constructor arguments and it will blow at runtime, while the second will generate a compile-time error.

Resources