Self-host WebApi với Owin middleware

Trong bài viết trước tôi đã giới thiệu tới các bạn những khái niệm cơ bản về OWin (Open Web Interface for Net), các bạn có thể xem lại theo link sau:

Tổng quan về Owin - Open Web Server Interface for .NET

Để tiếp tục làm quen với Owin, trong bài viết này chúng ta sẽ xây dựng một Owin middleware đơn giản cho web api.

Trước khi đi vào triển khai ứng dụng, chúng ta cần biết về Katana. Vậy Katana là gì? Đây là một project open source giúp cho việc xây dựng và hosting OWIN-based web applications, nó được phát triển bởi Microsoft.

Bước tiếp theo chúng ta sẽ từng bước xây dựng middleware với Owin và Katana.

1. SimpleMiddleware class

Đây là tên class của middleware, đơn là chỉ là một tên mà tôi đặt, nó được kế thừa OwinMiddleware class (một built-in của Owin library):

public class SimpleMiddleware: OwinMiddleware
    {
        public SimpleMiddleware(OwinMiddleware next)
            : base(next)
        {
        }

        public async override Task Invoke(IOwinContext context)
        {
            context.Response.Headers["MachineName"] = Environment.MachineName;

            await Next.Invoke(context);
        }
    }

2. Startup class

Tại đây chúng ta thực hiện cấu hình việc sử dụng route cho Asp.net API, custom middleware, file option server,..

public class Startup
    {
        // This method is required by Katana:
        public void Configuration(IAppBuilder app)
        {
            // Adding to the pipeline with our own middleware
            app.Use(async (context, next) =>
            {
                // Add Header
                context.Response.Headers["Product"] = "Web Api and Owin Middleware";

                // Call next middleware
                await next.Invoke();
            });

            // Custom Middleare
            app.Use(typeof(SimpleMiddleware));

            // Configure Web API for self-host.
            var config = ConfigureWebApi();

            // Web Api
            app.UseWebApi(config);

            // File Server
            var options = new FileServerOptions
            {
                EnableDirectoryBrowsing = true,
                EnableDefaultFiles = true,
                DefaultFilesOptions = { DefaultFileNames = { "home.html" } },
                FileSystem = new PhysicalFileSystem("StaticFiles"),
                StaticFileOptions = { ContentTypeProvider = new FileExtensionContentTypeProvider() }
            };

            app.UseFileServer(options);
        }

        private HttpConfiguration ConfigureWebApi()
        {
            var config = new HttpConfiguration();
            config.Routes.MapHttpRoute(
                "DefaultApi",
                "api/{controller}/{id}",
                new { id = RouteParameter.Optional });
            return config;
        }
    }
}

Trong đó, phần cấu hình FileServerOptions giúp middleware có thể duyệt những static files, kết quả tôi sẽ được trình bày ở phần sau.

3. Program class

Tại hàm main của chương trình tôi dùng đối tượng HostFactory để thực hiện chạy server, đây là một class nằm trong thư viện Topshelf. Đây là một framework hỗ trợ việc hosting service được viết trong .Net. Các bạn có thể tìm hiểu sau hơn về nó tại đây. chúng ta tìm và add nó tới project từ Nuget.

 static void Main(string[] args)
        {
            Console.WriteLine("Starting web Server...");
            HostFactory.Run(x =>
            {
                x.Service<OwinServer>(s =>
                {
                    s.ConstructUsing(name => new OwinServer());
                    s.WhenStarted(tc => tc.Start());
                    s.WhenStopped(tc => tc.Stop());
                });
                x.RunAsLocalSystem();

                x.SetDescription("This is application of a Windows Service using Topshelf.");
                x.SetDisplayName("Self Host Web API and Owin Middleware");
                x.SetServiceName("AspNetSelfHostWithMiddleware");
            });

            Console.WriteLine("Server running at {0} - press Enter to quit. ", "http://localhost:8080");
            Console.ReadLine();
        }

Các bạn có thể thấy ở đây Topshelf giúp ứng dụng có thể chạy như một service, có thể cấu hình: description, displayName, serviceName. Lưu ý rằng, service được cấu hình để sử dụng OwinServer class

public class OwinServer
    {
        private IDisposable _webapp;

        public void Start()
        {
            _webapp = WebApp.Start<Startup>("http://localhost:8080");
        }

        public void Stop()
        {
            _webapp.Dispose();
        }
    }

Tại OwinServer class, chúng ta đơn giản thực hiện việc kích hoạt web server khởi chạy và lắng nghe tại cổng 8080 của localhost.

4. Xây dựng model và controller

Chúng ta sẽ xây dựng một api controller để thực hiện hiển thị dữ liệu trên web. Tôi sử dụng một đối tượng Product

public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

Controller:

 public class ProductsController : ApiController
    {
        private List<Product> _products = new List<Product>
        {
            new Product {Id = 1, Name = "Product 1"},
            new Product {Id = 2, Name = "Product 1"},
            new Product {Id = 3, Name = "Product 1"},
            new Product {Id = 4, Name = "Product 1"},
            new Product {Id = 5, Name = "Product 1"}
        };

        // api/products/
        public IEnumerable<Product> Get()
        {
            return _products;
        }

        // api/products/id
        public Product Get(int id)
        {
            var product = _products.FirstOrDefault(c => c.Id == id);
            if (product == null)
            {
                throw new HttpResponseException(
                    System.Net.HttpStatusCode.NotFound);
            }
            return product;
        }

        public async Task<IHttpActionResult> Post(Product product)
        {
            return Ok();
        }

        public async Task<IHttpActionResult> Put(Product product)
        {
            return Ok();
        }

        public async Task<IHttpActionResult> Delete(int id)
        {
            return Ok();
        }
    }

Để đơn giản hóa cho ứng dụng demo, tôi sử dụng một list product có sẵn như trên. Đồng thời, tôi chỉ triển khai hai api: getAll, getOne.

5. Kết quả

Nhấn F5 chạy chương trình, chúng ta có thông tin server như bên dưới:

middleware.jpg

Tiếp theo, chúng ta gõ trên trình duyệt, thông tin được hiển thị như bên dưới:

home_page.jpg

Thông tin static file được hiển thị bởi vì chúng ta đã cấu hình file option server tại startup class, nó giúp cho middleware có thể duyệt những file tĩnh trong thư mục. Sau khi click vào link testFile.txt thì nội dung file sẽ được hiển thị:

staticFile.jpg

Để biết các api có làm việc hay không, chúng ta gõ trên trình duyệt các api.

/api/products/

all_products.jpg

/api/products/id/

one_product.jpg

Thêm nữa, chúng ta cũng có thể check thêm một số thông tin cấu hình của middleware thông qua header của request, như các bạn thấy bên dưới

server_response.jpg

Ta có thể hiểu thông tin đó lấy từ đâu, nhìn lại những dòng code dưới đây:

context.Response.Headers["MachineName"] = Environment.MachineName;
context.Response.Headers["Product"] = "Web Api and Owin Middleware";

7. Thảo luận

Với một ứng dựng middleware đơn giản ở trên, hy vọng các bạn có thể hình dung Owin, Katana dùng để làm gì và ứng dụng của nó ở đâu. Chúc các bạn thành công.

Tài liệu tham khảo:

http://www.asp.net/aspnet/overview/owin-and-katana

http://www.codeproject.com/Articles/864725/ASP-NET-Understanding-OWIN-Katana-and-the-Middlewa

Link source code:

https://drive.google.com/file/d/0B0YcdbjNGk5NRlh0N2x0eTdrWFU/view?usp=sharing


All Rights Reserved