ZF2: Using JSON-RPC without MVC? - zend-framework2

It is stated in the ZF2 documentation, as well as by Matthew Weier O'Phinney's blog, that:
Many developers want to stick this in their MVC application directly,
in order to have pretty URLs. However, the framework team typically
recommends against this. When serving APIs, you want responses to
return as quickly as possible, and as the servers basically
encapsulate the Front Controller and MVC patterns in their design,
there's no good reason to duplicate processes and add processing
overhead.
It is recommended that you put the server endpoints in the public directory structure. For example, you might have /public/some-api.php that instantiates and runs the Zend RPC Server. But I have already created this dope module in which I have a bunch of classes and a config file that lays out the dependency injection, factories, etc for creating the classes.
Soo... how do I leverage that code in my RPC server, without putting it into a MVC controller?
Thanks!
Adam

Here is how I did it. I have this broken out into a few files, but you can put all this in your public root directory, something like rpc-service.php:
use Zend\ServiceManager\ServiceManager,
Zend\Mvc\Service\ServiceManagerConfig;
class Bootstrap {
/** #var ServiceManager */
private static $serviceManager;
private static function _go() {
chdir(dirname(__DIR__));
require __DIR__ . '/../init_autoloader.php';
$config = include __DIR__ . '/../config/application.config.php';
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
self::$serviceManager = $serviceManager;
}
/**
* #return ServiceManager
*/
public static function getServiceManager() {
if (!self::$serviceManager)
self:: _go();
return self::$serviceManager;
}
}
$sm = Bootstrap::getServiceManager();
use Zend\Json\Server\Server,
Zend\Json\Server\Smd,
$jsonRpc = new Server();
$jsonRpc->setClass($sm->get('Some\Class'));
$jsonRpc->getRequest()->setVersion(Server::VERSION_2);
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
echo $jsonRpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
}
else {
$jsonRpc->handle();
}
As you can see, I'm using the Service Manager! Yay. All is right in the world.

Related

Set up Dependency Injection on Service Fabric using default ASP.NET Core DI container

I would like to use ASP.NET Core's default DI container to setup DI for my Service Fabric project.
//This is what I've got so far, and it works great
ServiceRuntime.RegisterServiceAsync(
"MyServiceType",
context => new MyService(context, new MyMonitor()
).GetAwaiter().GetResult();
//This is how I use it
public MyService(StatefulServiceContext context, IMonitor myMonitor)
: base(context)
{
this._myMonitor = myMonitor;
}
How would I set up DI, if MyMonitor class has a dependency on a ConfigProvider class, like this:
public MyMonitor(IConfigProvider configProvider)
{
this._configProvider = configProvider;
}
I think this question will give you some light: Why does ServiceRuntime.RegisterServiceAsync return before the serviceFactory func completes?
Technically, the ServiceRuntime.RegisterServiceAsync() is a dependency registration, it requires you to pass the serviceTypeName and the factory method responsible for creating the services Func<StatelessServiceContext, StatelessService> serviceFactory
The factory method receives the context and returns a service (Stateful or stateless).
For DI, you should register all dependencies in advance and call resolve services to create the constructor, something like:
var provider = new ServiceCollection()
.AddLogging()
.AddSingleton<IFooService, FooService>()
.AddSingleton<IMonitor, MyMonitor>()
.BuildServiceProvider();
ServiceRuntime.RegisterServiceAsync("MyServiceType",
context => new MyService(context, provider.GetService<IMonitor>());
}).GetAwaiter().GetResult();
PS:
Never Register the context (StatelessServiceContext\StatefulServiceContext) in the DI, in a shared process approach, multiple partitions might be hosted on same process and will have multiple contexts.
This code snippet is not tested, I've used in the past, don't have access to validate if matches the same code, but is very close to the approach used, might need some tweaks.
Hi #OscarCabreraRodríguez
I am working on the project that simplifies development of Service Fabric Reliable Services and it has great built-in support for dependency injection scenarios.
You can find general information project page, wiki and specific information about dependency injection here.
The idea is that project abstracts you from working with Service instance directly instead providing you with a set of more concrete objects.
Here is a simple example for ASP.NET Core application:
public static void Main(string[] args)
{
new HostBuilder()
.DefineStatefulService(
serviceBuilder =>
{
serviceBuilder
.UseServiceType("ServiceType")
.DefineAspNetCoreListener(
listenerBuilder =>
{
listenerBuilder
.UseEndpoint("ServiceEndpoint")
.UseUniqueServiceUrlIntegration()
.ConfigureWebHost(
webHostBuilder =>
{
webHostBuilder
.ConfigureServices(
services =>
{
// You can configure as usual.
services.AddTransient<IMyService, MyService>();
})
.UseStartup<Startup>();
});
});
})
.Build()
.Run();
[Route("api")]
public class ApiController : Controller
{
public ApiController(IMyService service) { }
[HttpGet]
[Route("value")]
public string GetValue()
{
return $"Value from {nameof(ApiController)}";
}
}
Hope I understand your use case correctly and this information is relevant.

No default Instance is registered and cannot be automatically determined for type

The definition of my interface is as follows:
public interface IApplicationSettings
{
string LoggerName { get; }
string NumberOfResultsPerPage { get; }
string EmailAddress { get; }
string Credential { get; }
}
The implementation of this interface is given below:
public class WebConfigApplicationSettings : IApplicationSettings
{
public string LoggerName
{
get { return ConfigurationManager.AppSettings["LoggerName"]; }
}
public string NumberOfResultsPerPage
{
get { return ConfigurationManager.AppSettings["NumberOfResultsPerPage"]; }
}
public string EmailAddress
{
get { return ConfigurationManager.AppSettings["EmailAddress"]; }
}
public string Credential
{
get { return ConfigurationManager.AppSettings["Credential"]; }
}
}
I also created a factory class to obtain the instance of the concrete implementation of WebConfigSettings as follows:
public class ApplicationSettingsFactory
{
private static IApplicationSettings _applicationSettings;
public static void InitializeApplicationSettingsFactory(
IApplicationSettings applicationSettings)
{
_applicationSettings = applicationSettings;
}
public static IApplicationSettings GetApplicationSettings()
{
return _applicationSettings;
}
}
Then I resolved dependency as follows:
public class DefaultRegistry : Registry {
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<IApplicationSettings>().Use<WebConfigApplicationSettings>();
ApplicationSettingsFactory.InitializeApplicationSettingsFactory
(ObjectFactory.GetInstance<IApplicationSettings>());
}
}
Now when i running my application it throw me following exception:
Exception has been thrown by the target of an invocation.
and the Inner Exception is
No default Instance is registered and cannot be automatically determined for type 'Shoppingcart.Infrastructure.Configuration.IApplicationSettings'\r\n\r\nThere is no configuration specified for Shoppingcart.Infrastructure.Configuration.IApplicationSettings\r\n\r\n1.) Container.GetInstance(Shoppingcart.Infrastructure.Configuration.IApplicationSettings)\r\n
I am using StructureMap for MVC5
The reason your code isn't working is because when you call ObjectFactory.GetInstance<IApplicationSettings>(), your registry hasn't been registered and thus, StructureMap's configuration is incomplete.
I believe what you're trying to do is the following (tested and works):
public class ApplicationSettingsFactory
{
public ApplicationSettingsFactory(WebConfigApplicationSettings applicationSettings)
{
_applicationSettings = applicationSettings;
}
private static IApplicationSettings _applicationSettings;
public IApplicationSettings GetApplicationSettings()
{
return _applicationSettings;
}
}
With your registry configured like this:
public DefaultRegistry() {
Scan(scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
this.For<IApplicationSettings>().Use(ctx => ctx.GetInstance<ApplicationSettingsFactory>().GetApplicationSettings());
}
I can't really tell you why your registration fails in StructureMap, but if you allow me, I would like to feedback on your design.
Your design and code violates a few basic principles:
You are violating the Interface Segregation Princple (ISP).
The ISP describes that interfaces should be narrow (role interfaces) and should not contain more members than a consumer uses. You however defined an application wide IApplicationSettings interface and your intention is to inject into any consumer that needs some configuration settings. Changes are really slim however that there is a consumer that actually needs all settings. This forces the consumer to depend on all members, it makes the API more complex, while it just needs one.
You are violating the Open/Closed Principle (OCP).
The OCP describes that it should be possible to add new features without making changes to existing classes in the code base. You will however find yourself updating the IApplicationSettings interface and its implementations (you will probably have a fake/mock implementation as well) every time a new setting is added.
Configuration values aren't read at startup, which makes it harder to verify the application's configuration.
When a consumer makes a call to a property of your IApplicationSettings abstraction, you are forwarding the call to the ConfigurationManager.AppSettings. This means that if the value isn't available or incorrectly formatted, the application will fail at runtime. Since some of your configuration values will only be used in certain cases, this forces you to test every such case after you deployed the application to find out whether the system is configured correctly.
Solution
The solution to these problems is actually quite simple:
Load configuration values at start-up.
Inject configuration values directly into a component that needs that exact value.
Loading the configuration values directly at start-up, allows the application to fail fast in case of a configuration error, and prevents the configuration from being read over and over again needlessly.
Injecting configuration values directly into a component, prevents that component from having to depend on an ever-changing interface. It makes it really clear what a component is depending upon, and bakes this information in during application start-up.
This doesn't mean though that you can't use some sort of ApplicationSettings DTO. Such DTO is exactly what I use in my applications. This basically looks as follows:
public static Container Bootstrap() {
return Bootstrap(new ApplicationSettings
{
LoggerName = ConfigurationManager.AppSettings["LoggerName"],
NumberOfResultsPerPage = int.Parse(
ConfigurationManager.AppSettings["NumberOfResultsPerPage"]),
EmailAddress = new MailAddres(
ConfigurationManager.AppSettings["EmailAddress"]),
Credential = ConfigurationManager.AppSettings["Credential"],
});
}
public static Container Bootstrap(ApplicationSettings settings) {
var container = new Container();
container.RegisterSingle<ILogger>(
new SmtpLogger(settings.LoggerName, settings.EmailAddress));
container.RegisterSingle<IPagingProvider>(
new PagingProvider(settings.NumberOfResultsPerPage));
// Etc
return container;
}
In the code above you'll see that the creation of the ApplicationSettings DTO is split from the configuration of the container. This way I can test my DI configuration inside an integration test, where the start-up projects configuration file is not available.
Also note that I supply the configuration values directly to the constructors of components that require it.
You might be skeptic, because it might seem to pollute your DI configuration, because you have dozens of objects that require to be set with the same configuration value. For instance, your application might have dozens of repositories and each repository needs a connection string.
But my experience is that is you have many components that need the same configuration value; you are missing an abstraction. But don't create an IConnectionStringSettings class, because that would recreate the same problem again and in this case you aren't really making an abstraction. Instead, abstract the behavior that uses this configuration value! In the case of the connection string, create an IConnectionFactory or IDbContextFactory abstraction that allows creation of SqlConnection's or DbContext classes. This completely hides the fact that there is a connection string from any consumer, and allows them to call connectionFactory.CreateConnection() instead of having to fiddle around with the connection and the connection string.
My experience is that makes the application code much cleaner, and improves the verifiability of the application.
Thanks every one for responses. I found my solution. The solution is instead of using Default Registry I created another class for resolve the dependencies. Inside the class I used
ObjectFactory.Initialize(x =>
{
x.AddRegistry<ControllerRegistry>();
});
instead of
IContainer Initialize() {
return new Container(c => c.AddRegistry<ControllerRegistry>());
}
Then inside ControllerRegistry I resolved dependencies as follows:
// Application Settings
For<IApplicationSettings>().Use<WebConfigApplicationSettings>();
Then I called that class inside Global.asax as follows:
Bootstrap.ConfigureDependencies();
Finally inside Global.asax I resolved dependency for Factory class as follows:
ApplicationSettingsFactory.InitializeApplicationSettingsFactory
(ObjectFactory.GetInstance<IApplicationSettings>());
My entire code is given below:
Bootstrap class (newly created)
public class Bootstrap
{
public static void ConfigureDependencies()
{
ObjectFactory.Initialize(x =>
{
x.AddRegistry<ControllerRegistry>();
});
}
public class ControllerRegistry : Registry
{
public ControllerRegistry()
{
// Application Settings
For<IApplicationSettings>().Use<WebConfigApplicationSettings>();
}
}
}
Global.asax
Bootstrap.ConfigureDependencies();
ApplicationSettingsFactory.InitializeApplicationSettingsFactory
(ObjectFactory.GetInstance<IApplicationSettings>());

ZF2 - Injecting pages to navigation before controller is called

I'm creating a dynamic Application in which the content is added through a CMS. Inside the CMS, I'm setting a db entry which states what module to use for each content page.
NodeId,
ParentNodeId,
Name_de,
Name_en,
ModuleName,
foreignkey_ContentLinks,
in this table entries look as follows:
6,
1,
Veranstaltung-21-02-2013,
Event-21-02-2013,
Events,
682
The entire tree should end up in my navigation (and perfectly also in my routing). I do not want to add it in some controller, because my Application consists of a whole bunch of Modules and I want to access that Info across all my Modules.
I already tried injecting it in the global.php, but to no avail because I can't my db adapter or any other important classes at that stage.
Any ideas or links to best practices?
The navigation containers are composed by factory classes. The easiest approach is to write your own factory and have the getPages() method fetch pages from a database instead of from config. If you extend from the AbstractNavigationFactory you only need to write a couple of methods.
<?php
namespace Application\Navigation\Service;
use Zend\Navigation\Service\AbstractNavigationFactory;
use Zend\ServiceManager\ServiceLocatorInterface;
class CmsNavigationFactory extends AbstractNavigationFactory
{
/**
* #param ServiceLocatorInterface $serviceLocator
* #return array
* #throws \Zend\Navigation\Exception\InvalidArgumentException
*/
protected function getPages(ServiceLocatorInterface $serviceLocator)
{
if (null === $this->pages) {
$application = $serviceLocator->get('Application');
$routeMatch = $application->getMvcEvent()->getRouteMatch();
$router = $application->getMvcEvent()->getRouter();
// get your pages from wherever...
$pages = $this->getPagesFromDB();
$this->pages = $this->injectComponents($pages, $routeMatch, $router);
}
return $this->pages;
}
public function getName()
{
// this isn't used if fetching from db, it's just here to keep the abstract factory happy
return 'cms';
}
}
Add the factory to the service manager, just like you would for other containers
'service_manager' => array(
'factories' => array(
'CmsNavigation' => 'Application\Navigation\Service\CmsNavigationFactory',
),
),
And use it with the navigation view helpers in the same way
<?php echo $this->navigation()->menu('CmsNavigation'); ?>
Responding to your comment on #Crisp's answer, and for future googlers, I'll explain how to do something similar for routing.
Typically you would want to create a custom router that can match URLs to the pages in your database, similarly to the standard Segment router. To do this, you will have to implement the Zend\Mvc\Router\RouteInterface interface. For example:
namespace Application\Router;
use Zend\Mvc\Router\RouteInterface;
use Application\Model\CMSTable;
class CmsRoute implements RouteInterface, ServiceLocatorAwareInterface
{
protected $table;
// Service locator injection code
public function getCmsTable()
{
// Retrieve the table from the service manager
}
public function match(Request $request)
{
// Match the request on some route field, etc.
}
public function assemble(array $params = array(), array $options = array())
{
// Assemble a URL based on the given parameters (e.g. page ID).
}
public static function factory($options = array())
{
// Construct a new route, based on the options.
}
}
You could then register this route as an invokable for the RoutePluginManager in your module configuration:
'route_manager' => array(
'invokables' => array(
'Cms' => 'Application\Router\CmsRoute'
),
),
Then, you can create a new route (just as you would for any other route) with type Cms. The route plugin manager will create your route instance, and since CmsRoute implements ServiceLocatorAwareInterface, the plugin manager will inject itself in the route. In turn, the plugin manager has the main service manager set, so that you can get the database table from there!
Of course you can match on page ID, but if you have a hierarchical structure, it's nicer to reflect that in your URLs. I would therefore recommend adding a route field to the database schema and match on that, beginning with the tree root and working down.

Autofac Dependencies Per Area

I'm creating a new MVC4 site using Autoface that has a public consumer site as well as an admin area for managing the consumer facing site. The admin site will be located in a different area be using the same services as the consumer facing site, but will not having some of the custom branding features.
I've followed the advice given elsewhere of having a ViewDataFactory which provides a set of shared data for the view to use. My goal is to provide a different ViewDataFactory depending on what Area you are in.
So for example, here is the Service that implements IViewDataFactory
builder.RegisterType<SelfServiceViewDataFactory>().As<IViewDataFactory>();
This gives me one ViewFactory which is injected into all my controllers. However what I'm trying to acheive is something like this (not functional code):
builder.RegisterType<ViewDataFactory>().As<IViewDataFactory>().ForType(ControllerBase1);
builder.RegisterType<DifferentViewDataFactory>().As<IViewDataFactory>().ForType(ControllerBase2);
Where the controller type or the MVC area would determine which service is resolved.
EDIT
To clarify my post has two questions:
Is there a way in Autofac to say "only for classes of type X, a service of type Y will be provided by instance Z" ?
Is there a way to change the Autofac behavior based on the Area the component is being used in?
From everything I've been reading the answer to #1 seems to be "no" unless you have a parameter to use to check which component to supply. I know Ninject can supply a dependency based on namespace so other frameworks seems to handle this case. Seems the solution is to either supply a parameter or have two different services defined.
I haven't really seen much discussion of Autofac and MVC areas so I'm guessing #2 is also not possible without a custom solution. Thanks!
Using named services is probably your best option. So you'd do something like:
builder
.RegisterType<ViewDataFactory>()
.Named<IViewDataFactory>("Area1");
builder
.RegisterType<DifferentViewDataFactory>()
.As<IViewDataFactory>("Area2");
And then if you want to avoid having to then manually register your controllers. You could use this code that I just cobbled together and haven't tested:
Put this attribute somewhere globally accessible:
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class ServiceNamedAttribute : Attribute
{
private readonly string _key;
public ServiceNamedAttribute(string key)
{
_key = key;
}
public string Key { get { return _key; } }
}
Add this module to your Autofac config:
public class ServiceNamedModule : Module
{
protected override void AttachToComponentRegistration(
IComponentRegistry registry, IComponentRegistration registration)
{
registration.Preparing +=
(sender, args) =>
{
if (!(args.Component.Activator is ReflectionActivator))
return;
var namedParameter = new ResolvedParameter(
(p, c) => GetCustomAttribute<ServiceNamedAttribute>(p) != null,
(p, c) => c.ResolveNamed(GetCustomAttribute<ServiceNamedAttribute>(p).Name, p.ParameterType));
args.Parameters = args.Parameters.Union(new[] { namedParameter });
};
}
private static T GetCustomAttribute<T>(ParameterInfo parameter) where T : Attribute
{
return parameter.GetCustomAttributes(typeof(T), false).Cast<T>().SingleOrDefault();
}
}
And then you can still auto-register your controllers by decorating the constructor like so:
public class Controller1
{
public Controller1(ServiceNamed["Area1"] IViewDataFactory factory)
{ ... }
}

Using Ninject.MockingKernel with Asp.Net Web API

I've set up a Web API project using Ninject, and I've used the fix detailed here for getting it to work with the latest version of the Web API. Everything is working fine, but I'm now trying to write some tests.
I'm using in-memory hosting to run the project for the tests, as detailed here, as I have a DelegatingHandler that performs authentication and then sets a property on the request message that is used by all the Api Controllers.
So, I've got a base class for my tests, and have a SetUp method where I set up the HttpServer and configuration, which I've pretty much taken from my working Ninject code:
[SetUp]
public void Setup()
{
bootstrapper = new Bootstrapper();
DynamicModuleUtility.RegisterModule(
typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(
typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("Login",
"api/auth/token",
new { controller = "Users", action = "Login" });
config.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
config.DependencyResolver =
new NinjectResolver(CreateKernel());
config.MessageHandlers.Add(
new AuthenticationHandler(CreateUserManager()));
Server = new HttpServer(config);
}
This is how I create the MoqMockingKernel:
private static IKernel CreateKernel()
{
var kernel = new MoqMockingKernel();
kernel.Bind<Func<IKernel>>()
.ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>()
.To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver =
new NinjectResolver(kernel);
return kernel;
}
And this is how I register the objects to use:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserManager>().ToMock();
kernel.Bind<UsersController>().ToSelf();
}
While I'm not testing the Controller per se, I do want a proper instance of it to be called, which is why I'm binding it ToSelf. I must admit that I am assuming that this is correct. This is an example of a test:
public void UserCannotLogin()
{
System.Net.Http.HttpClient client =
new System.Net.Http.HttpClient(Server);
string json = string.Format(
"{{ \"Username\": \"{0}\", \"Password\": \"{1}\" }}",
"wrong", "wrong");
HttpRequestMessage request =
CreateRequest(#"api/auth/token", json, HttpMethod.Get);
Action action = () => client.SendAsync(request);
using (var response = client.SendAsync(request).Result)
{
response.StatusCode.Should()
.Be(HttpStatusCode.Unauthorized);
}
}
I'm basically getting a 404 error. When I debug it, it does go to my DelegatingHandler, but it doesn't go to my controller.
I get the feeling that I'm fundamentally missing a point here, and it may not even be possible to do what I'm trying to do, but if anyone has any suggestions for either how to do this, or a different way to achieve the same thing, I'm all ears.
Update I think that it's because the default behaviour of the MockingKernel is to provide a Mock unless told otherwise, so it is returning a Mock of IHttpControllerSelector. I've set up a couple of default ones now:
kernel.Bind<IHttpControllerSelector>()
.To<DefaultHttpControllerSelector>();
kernel.Bind<IContentNegotiator>()
.To<DefaultContentNegotiator>();
It's still not working, I think because there are no formatters specified. I'll try that tomorrow and see if that gets me there.
Ok, I think that I was correct when I said that I was fundamentally missing a point here, but I'll answer this in case it helps someone else avoid the same mistake!
The Ninject MockingKernel is, I think, primarily about auto-mocking, so where you have a lot of interfaces you don't care about how they are set up in your test, you can ignore them in your tests and they will be automatically created for you.
In the case of the Web API, this is most definitely not the case, as you don't want the controller selector class to be auto mocked, otherwise you won't end up calling your controllers.
So, the solution I've come up with is to stick with using a standard Ninject Kernel, and then bind your interface to a constant Mock object:
kernel.Bind<IUserManager>().ToConstant(CreateUserManager());
private IUserManager CreateUserManager()
{
Mock<IUserManager> userManager = new Mock<IUserManager>();
// Set up the methods you want mocked
return userManager.Object;
}
Doing this, I've been able to successfully write tests that use an HttpClient to call an in-memory HttpServer that successfully call my DelegatingHandler and then end up at my controllers.

Resources