unosquare / embedio

A tiny, cross-platform, module based web server for .NET
http://unosquare.github.io/embedio
Other
1.46k stars 176 forks source link

2.x to 3.x migration #366

Closed charlyR closed 5 years ago

charlyR commented 5 years ago

Could you please add more examples for 2.x to 3.x migration?

How can i do this in the new 3.x version?

/*LOCAL WEBSERVER API */
private EmbedIO.WebServer _server;
private CancellationTokenSource _cts;
private Task _task;

private void ServiceStart() {
//To start webapi server
 _server.RegisterModule(new WebApiModule());
 _server.Module<WebApiModule>().RegisterController<HKWebServicesController>((ctx) => new HKWebServicesController(ctx, _hkControl));
_server.Module<WebApiModule>().RegisterController<AppWebServicesController>((ctx) => new AppWebServicesController(ctx, _appControl));

_cts = new CancellationTokenSource();
_task = _server.RunAsync(_cts.Token);
}
//=====================================
// To stop webapi server
private void ServiceStop() {
 _cts.Cancel();
_task.Wait();
_server.Dispose();
}

Thanks in advance

NvVijayakumara commented 5 years ago

Hi @charlyR , I'm using this library with version 2.2.9. I tried your code, it seems WebServer got closed.. After that I checked in Windows Task Manager > CPU usage, here I found that resources are not released.. Any help..

charlyR commented 5 years ago

Add a line, where the server object is created, before module registration. _server = new WebServer(url, RoutingStrategy.Regex);

Then in the controller you can use the object passed as parameter. It´s just a 2.x sample _server.Module().RegisterController((ctx) => new HKWebServicesController(ctx, _hkControl));

public class HKWebServicesController : WebApiController
    {
        private HK _handKey;
        private IHttpContext _context;

        public HKWebServicesController(IHttpContext context, HK hK) : base(context)
        {
             _context = context;
            _handKey = hK;
        }

        [WebApiHandler(HttpVerbs.Get, "/api/biometrics/healthcheck")]
        public async Task<bool> GetHKStatus()
        {
            try
            {
                HKStatusResponse status = new HKStatusResponse();
                bool stat = _handKey.GetStatus();
                status.IsConnected = _handKey.IsConnected();
                status.DeviceId = _handKey.DeviceId;

                if (stat)
                {
                    status.Status = "HK.READY";
                }

                if (_handKey.HkInfo.Status == 1)
                {
                    status.Status = "HK.BUSY";
                }

                if (_handKey.HkInfo.Status == -1)
                {
                    status.Status = "HK.COMMAND_FAILED";
                }

                return await _context.JsonResponseAsync(status);
            }
            catch (Exception ex)
            {
                return await _context.JsonResponseAsync(ex);
            }
        }
rdeago commented 5 years ago

Hello @charlyR, thanks for using EmbedIO!

You're right, documentation is currently incomplete, although this Wiki page is at least a starting point for migrating a project from EmbedIO v2.x to v3.0.

As I believe in teaching how to fish instead of just handing out fish, let me introduce you to a couple key concepts about EmbedIO v3.0. Then we will go through your code and see how it can be ported to EmbedIO v3.0 with as few changes as possible.

(EDIT: the above-mentioned Wiki page has just been updated with the information below. Go read it, then skip down to the horizontal separator. 😉)

Immutable configuration

Previous versions of EmbedIO allowed you to register and unregister modules, web API controllers, and virtual paths for static files while a web server was running. This had a direct impact on both code complexity and run-time performance.

Version 3 has immutable configuration: once a web server is started, its configuration (the set of properties that define the web server's behavior, as well as the list of registered modules) becomes read-only, and so does the configuration of all registered modules, lists of web API controllers, etc.

Another consequence of immutable configuration is that it is no longer possible to unregister modules, controllers, and virtual paths: the Unregister**** methods have simply been removed.

Immutable configuration has allowed us to simplify code in several places, removing locking mechanisms that had a direct impact on run-time performance.

New routing rules

In previous versions of EmbedIO, every single path or route (web API method routes, virtual paths for static files) was absolute. If you wanted a web API controller method to handle /api/customer/{id}, that's what you specified. If all your API method paths started with /api/, you had to specify it every time. For every request, the web server had to call into each module until it found one who could handle the requested path.

EmbedIO v3.0 introduces the concept of base route. Every module has its own base route, which is tested against the beginning of the requested path; the IHttpContext.RequestedPath property that handlers see is relative to the module's base route. For example, if a WebApiModule has a base route of /api/ (base routes always end with a slash) then a WebApiController method with a [Route("/customer/{id}")] attribute will handle a request for /api/customer/12345.

Base routes enable a web server to more quickly find out which module is "responsible" for a given path. If a base path has parameters, you can find their values in the IHttpContext.Route property, which implements IReadOnlyDictionary<string,string>.

Changes to Web API

Web API is probably the area that has undergone the most changes in EmbedIO v3.0. Writing web API controllers is now easier, there is less boilerplate code to write (if any), but porting controllers from previous versions requires some work. Let's see what has changed.

Relative routes

First of all, due to base routes, web API controller method routes (which are now specified with a RouteAttribute instead of WebApiHandlerAttribute) are now relative to the module's base route, as we saw a couple paragraphs above.

No construction parameters for controllers

WebApiController's constructor is now parameterless, so there is no need for derived classes to take constructor parameters just to pass them to base(). Your controller constructors will have just the parameters you need (a reference to a database, for example) instead of being compelled to always take an IHttpContext.

Of course the HTTP context is still available to controller methods as the HttpContext property, which now gets automatically injected post-construction.

No need to return bool

Another fundamental change is that web API controller methods do NOT need a bool or Task<bool> return type.

When a void controller method returns (or the Task returned by a controller method completes) a default 200 OK response is sent to the client - unless, of course, the method has crafted its own response.

When a controller method generates a result, either by returning a value or when a returned Task<> completes, the result gets serialized and sent to the client. You don't need to generate your JSON any longer, just return the result and WebApiModule will take care of serialization. The default serialization callback used by WebApiModule leverages Swan.Json, but it's easy to write a serialization callback: it's just a method that takes an IHttpContext and an object and returns a Task. You can find the method signature defined as ResponseSerializationCallback.

For an example of how a web API controller changes when ported from EmbedIO v2.x to v3.0, you can check the PeopleController class from the sample project: here is the 2.x version and here is the 3.0 version.

Fluent initialization

A lot of work has been devoted in the development of EmbedIO v3.0 to ensure that a web server can be initialized entirely using "fluent" extension methods. This futher reduces the boilerplate code in applications, leaving you with a bare minimum that usually fits in a screenful and lets you see the structure and options of your web server at a glance.

I usually write a private CreateWebServer method to initialize my web servers, so I can use an expression body and lambdas to write more concise code, like this:

private WebServer CreateWebServer(int port, Database database)
    => new WebServer(port)
        .WithCors()
        .WithStaticFolder("/", "C:\\www", true)
        .WithWebApi("/api/", m => m
            .WithController(() => new CustomerController(database))
            .WithController(() => new InvoiceController(database))));

Although, as you rightly pointed out, documentation is still lacking, fluent extension methods always start with With (or Handle if their purpose is to set a callback method) so Intellisense is your friend. You may also take a look at EmbedIO's source code: all extension methods are in classes whose name ends in Extensions, so they are rather easy to find.


Now let's take a look at your code.

Your ServiceStart method seems to act on an existing instance of WebServer. While this is perfectly acceptable, my advice is to keep all initialization code together, including module initialization. Starting a WebServer should be no more than a call to RunAsync (plus, in your case, creating a cancellation token and storing the returned Task for later waiting).

using System.Threading;
using System.Threading.Tasks;
using EmbedIO;
using EmbedIO.WebApi;

namespace Example
{
    public class WebServerExample
    {
        private WebServer _server;
        private CancellationTokenSource _cts;
        private Task _task;

        // "object" because I don't know what type they are :)
        private void ServiceStart(int port, object hkControl, object appControl)
        {
            _server = new WebServer(port)
                .WithModule(new WebApiModule("/api")
                    .WithController(() => new HKWebServicesController(hkControl))
                    .WithController(() => new AppWebServicesController(appControl)));

            _cts = new CancellationTokenSource();
            _task = _server.RunAsync(_cts.Token);
        }

        private void ServiceStop()
        {
            _cts.Cancel();
            _task.Wait();
            _server.Dispose();
        }
    }
}

I hope I've been of help in making your application work with EmbedIO v3.0, and above all in making you more productive using it. Feel free to follow up here, or to join our Slack workspace if you need more advice.

charlyR commented 5 years ago

@rdeago Thank you very much, for your quick response. I will try the migration with your tips. It clarifies a lot, about the new version. I am trying to use EmbedIO to provide a webapi middleware to some serial and TCP socket devices to be accesible from a web client.