Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

Invoke Command/Queries in the Browser, Execute Them in your App

4.55/5 (4 votes)
10 Mar 2015LGPL32 min read 8.7K  
I've created a small sample project which demonstrates how you can invoke a query using JavaScript (and execute them in your server).

The cool thing (IMHO) is that this technique obsoletes ASP.NET MVC/WebApi and similar. That is, if you combine command/queries over ajax/websockets, you only need static HTML pages and a client side library like angular. To gain performance, you could even move all static HTML to a CDN.

Since this architecture doesn’t require a server side view engine, view model binding, controllers or whatever, the logic in the server is pretty slim which also allows more optimizations and performance gains. The HTTP server is functioning more like a service broker which redirects incoming messages to different parts of the system. The CQS Server could for instance route messages to different micro services as I’ve shown in a previous post.

griffin_cqs

Let’s go through all small pieces in my proof of concept. Do note that the library is still in an very early stage. The sample code can be watched in the github repos (link at the top of this article).

Authentication/Authorization

To authenticate, I’m just using BASIC authentication in this sample. It works great when you have SSL activated (otherwise it’s not secure at all). The user is not requested to authenticate unless requested by your code.

Authentication is divided into two parts. The first part is to do the actual authentication by using a IAuthenticator, once completed a IPrincipalFactory is used to assign a IPrincipal to the current thread.

To activate authentication, you assign an authenticator to the listener:

C#
var listener = new CqsHttpListener(builder.BuildMessageProcessor());
listener.Authenticator = CreateAuthenticator();

... which in this case is built like this:

C#
private static IAuthenticator CreateAuthenticator()
{
    var accountService = new SimpleAccountService();
    accountService.Add("admin", "admin", new[] { "Admin", "User" });
    accountService.Add("user", "user", new []{"User"});
    var basicAuth = new BasicAuthentication(accountService, "localhost");
    return basicAuth;
}

I just got some fake users. The IAccountService interface (implemented by SimpleAccountService) is tiny and easy to implement (using a DB, flat files or whatever).

Finally, you need to be able to authorize users.

Start by telling the library to use role based authorization:

C#
GlobalConfiguration.AuthorizationFilter = new RoleAuthorizer();

Then you need to put the [Authorize] attribute either on your CQS class, or on the handler class:

C#
[Authorize("Admin")]
public class GetUsers : Query<GetUsersResult>
{
}

IoC Support

In this example, I’m using Autofac:

C#
private static AutofacAdapter BuildContainer()
{
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterServices(Assembly.GetExecutingAssembly());

    var container = containerBuilder.Build();
    var adapter = new AutofacAdapter(container);
    return adapter;
}

As you can see, I’m just using a single line to register all command/query handlers. It’s an extension method that is available in the Griffin.Framework.Autofac nuget library. It registers all classes that are decorated with the [ContainerService] attribute like this:

C#
[ContainerService]
class GetUsersHandler : IQueryHandler<GetUsers, GetUsersResult>
{
    public async Task<GetUsersResult> ExecuteAsync(GetUsers query)
    {
        return new GetUsersResult()
        {
            Items = new[]
            {
                new GetUsersResultItem
                {
                    Id = 1,
                    UserName = "arne"
                }
            }
        };
    }
}

Finally, I assign the ioc container to the CqsHttpListener.

C#
var builder = new IocBusBuilder(adapter);
var listener = new CqsHttpListener(builder.BuildMessageProcessor());

Client Side

HTML page which is getting the query result:

HTML
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Demo</title>
    <script type="text/javascript" 
    src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
</head>
<body>
<script type="text/javascript">
    $.ajax({
        type: "POST",
        url: 'http://localhost:8899/cqs',
        data: { /* no query data */ },
        headers: {
            'X-Cqs-Name': "GetUsers"
        },
        dataType: 'json'
    }).done(function (data) {

        // here is the result from the query
        console.log(data.Items);
    });
</script>
</body>
</html>

Result without the [Authorize] attribute:

griffin_cqs_0

... with the attribute...

griffin_cqs_1

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)