Node.Cs Mvc Controllers Usage

The MVC controllers in Node.Cs are exactly like the standard .Net Mvc contorllers.

They should respect the naming convention ControllerNameController and must implement the IController interface.

To ease the development a ControllerBase class exists, that contains several utilities for this purpose.

We assume here that the handler for Razor is loaded (Node.Cs.Razor).

TO UNDERSTAND HOW PARAMETERS ARE PASSED WITHIN ROUTING, PLEASE CHECK THE ROUTING SECTION!

Simple controller

The following is a controller that will respond to a GET request like http://server/Simple/Index and returns an Index.cshtml page as result passing the model SimpleModel to the view

public class SimpleController : ControllerBase
{
        public IEnumerable<IResponse> Index()
        {
            var model = new SimpleModel{Data = "data"};
            yield return View(model);
        }
}

This will call the view ~/Views/SimpleController/Index.cshtml, passing the model as parameter

UNLIKE THE STANDARD MVC Is not possible to call a view that use a model without passing AT LEAST an empty model

The corresponding cshtml could be like this:

<html>
    <body>
        The model data is @Model.Data
    </body>
</html>

Controller variables

Specifying verbs

Different verbs can be specified for the various method, being GET the default one. Note that only one method can exists with a given verb with the same name: two methods accessed through GET can't have the same name.

The available methods are the default ones:

public class SimpleController : ControllerBase
{
        [HttpGet]
        public IEnumerable<IResponse> Index()
        {
            ...
        }

        [HttpPost]
        public IEnumerable<IResponse> DoSomething()
        {
            ...
        }
}

Passing parameters with GET/DELETE

Passing parameters as query string will work as with MVC. Automatic conversion is made for the various types and default values are honoured

E.g. calling http://myhost/SimpleController/WithParams?par1=text&par2=100

Could invoke the following action.

public class SimpleController : ControllerBase
{
        [HttpGet]
        public IEnumerable<IResponse> WithParams(string par1,int par2)
        {
            ...

We could even call http://myhost/SimpleController/WithParams?par1=text&par2=100 without the par2 parameter It would be needed to mark it as nullable or with a default value on the action

public class SimpleController : ControllerBase
{
        [HttpGet]
        public IEnumerable<IResponse> WithParams(string par1,int? par2)
        {
            ...

Passing parameters with POST/PUT

The same will happen with the form parameters passed inside a PUT or a POST through a form with

The support to pass JSON or XML data will be added as soon as possible!

Uploading files

Posting a form with a file with a content type of multipart/form-data the file handling works slightly differently. Note that form data can be passed at the same time.

An example of controller handling the thing would be:

public class SimpleController : ControllerBase
{
    [HttpPost]
    public IEnumerable<IResponse> HandleFiles()
    {
        var requiredFile = Context.Request.Files["myFileId"];
        
        var fileModel = new MyFileModel();
        model.FileName = file.FileName;
        model.FileSize = file.ContentLength;
        ...

Session

Session variables can be set, removed and used.

The object stored in session are stored in memory. A recycle will delete all session data.

The object stored in session would have to be serializable (else it would not be possible to use other than the InMemorySessionStorage).

    Context.Session["testIndex"]=new MyHopefullySerializableObject();
    Context.Session.Remove("oldValue");
    var value = Context.Session["anotherValue"];
    Context.Session.Clear();

Cookies

Cookies variables can be set, removed and used. They will resist until they expire or the server recycle.

The are only string kye value pairs.

    Context.Response.AppendCookie(new HttpCookie("test","testValue")
    {
        Expires = DateTime.Now+TimeSpan.FromDays(1)
    });
    var myCookieValue = Context.Response.Cookies("test");

Returning Views

Exactly as with the standard .NET MVC it is possible to return views from the controllers

Returning Json, Xml, Texts or bytes

There are too several utility methods into the Controller base:

Redirect

It would be possible to call for redirects as in MVC. This will return an IResponse, they will conclude the execution of the controller

Calling functions asincronously

This means that the current controller will be put in a stopped state, not consuming processor power until the moment in which the called function will return. Note that the syntax is very similar to the one in my library of Concurrency Helpers, they are, in fact the base of the whole thing.

All these function must "yield return" directly out of the controller. Only when an explicit response is produced the Container terminates.

Calling default .NET async functions

The various functions supporting async and await returns Task-s these task are mostly calling function already controlled through the I/O completion ports, meaning that the simply wait for the data returned by the drivers, they mostly do nothing by themselves.

For example to write data on a stream, that essentially copies data on the network card buffer and wait for the response. In this situation the controller will be stopped until the end of the WriteAsync function.

    yield return InvokeTaskAndWait(tcpStream.WriteAsync(bytes, 0, bytes.Length 0));

The signature is

    IResponse InvokeTaskAndWait(Task task);

With return values

In the following code we are calling the ReadText function, its definition is the following. When it finish it will return a string with the content of the file. This function will call a ReadAsync on a file stream thus will return null, until the moment in which it would have read all the file content.

    IEnumerable<string> ReadText(string path);

The result, when the ReadText would be completed, will be stored inside the Container.RawData field. No wait will be made, only a poll to check if the asynchronous operation finished

    var result = new Container();
    yield return InvokeLocalAndWait(() => _globalPathProvider.ReadText(path), container);
    var data = container.RawData as string;

The signature is

    IResponse InvokeLocalAndWait<T>(Func<IEnumerable<T>> func, Container result = null)

As tasks

It is possible to call wetheaver Action as a task. For example when an operation would be very complex and need to be mantained all concentrated in a single "context" and would block the thread for too much time, we can invoke an action as a Task, and block the execution of the caller until the completion of the task.

    yield return InvokeAsTaskAndWait(() => context.Initialize())

The signature is

    IResponse InvokeAsTaskAndWait(Action action)

Working with files

An example working with file uploads. The following example some files are uploaded and the first of them is taken and its data shown on the model.

Note that the full content of the files is available.

[HttpPost]
public IEnumerable<IResponse> PostFiles()
{
    //Retrieve all the files ids
    var allFiles = HttpContext.Request.Files.AllKeys.ToArray();

    var model = new FileModel();
    //If some file exists
    if (allFiles.Length > 0)
    {
        //Take the first one
        var file = HttpContext.Request.Files[allFiles[0]];
        if (file != null)
        {
            //And load its data
            model.FileName = file.FileName;
            model.FileSize = file.ContentLength;
        }
    }
    //return the model
    yield return View(model);
}

Last modified on: February 28, 2014