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!
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>
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 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) { ...
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!
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 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 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");
Exactly as with the standard .NET MVC it is possible to return views from the controllers
There are too several utility methods into the Controller base:
It would be possible to call for redirects as in MVC. This will return an IResponse, they will conclude the execution of the controller
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.
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);
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)
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)
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); }