As mentioned, I've followed Scott Allen's pluralsight.com MVC5 trainings to build a .NET Framework 4.5.1 OWIN-flavored console application like so:
using System;
using Microsoft.Owin.Hosting;
namespace MyKatana
{
class Program
{
static void Main(string[] args)
{
string uri = "http://localhost:8080";
using (WebApp.Start<Startup>(uri))
{
Console.WriteLine("Started!");
Console.ReadKey();
Console.WriteLine("Stopping!");
}
}
}
}
Also, as mentioned here, there is a Startup class in the mix. Recently, I altered it (from what is at the link I provide) as seen here:
using MyKatana.Middleware;
using Owin;
namespace MyKatana
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<HelloWorldComponent>();
}
}
}
I did this to bring in a component, a piece of middleware that is! In Mr. Allen's video he first builds a piece of middleware like so to show off a common shape:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
public class HelloWorldComponent
{
private Func<IDictionary<string, object>, Task> _next;
public HelloWorldComponent(Func<IDictionary<string, object>, Task> next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
await _next(environment);
}
}
}
A Func<IDictionary<string, object>, Task> must be taken at the constructor of a componet for it to behave well (or at all really) in the OWIN context, and thus there is a need to write a custom constructor with the signature seen here for each component. We also need an Invoke method. It is important to note that using the await keyword in lieu of a return requires one to also use the async keyword. Mr. Allen suggested that the await/async shape to the Invoke method is the norm. It is not however mandatory, as he went on to refactor what is above to what is below.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
public class HelloWorldComponent
{
private Func<IDictionary<string, object>, Task> _next;
public HelloWorldComponent(Func<IDictionary<string, object>, Task> next)
{
_next = next;
}
public Task Invoke(IDictionary<string, object> environment)
{
Stream response = environment["owin.ResponseBody"] as Stream;
using (StreamWriter writer = new StreamWriter(response))
{
return writer.WriteAsync("Hello World!");
}
}
}
}
We take off async in taking off await! Alright, clearly this piece is intended to the very last in a sequence of steps while the prior shape (which did nothing) was more friendly to being somewhere upstream of a last step. How can we use both shapes and still write a line of copy to the browser?
using MyKatana.Middleware;
using Owin;
namespace MyKatana
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<HelloComponent>().Use<WorldComponent>();
}
}
}
We will run the HelloComponent before the WorldComponent and it looks like so:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
public class HelloComponent
{
private Func<IDictionary<string, object>, Task> _next;
public HelloComponent(Func<IDictionary<string, object>, Task> next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
Stream response = environment["owin.ResponseBody"] as Stream;
using (StreamWriter writer = new StreamWriter(response))
{
writer.Write("Hello: ");
}
await _next(environment);
}
}
}
environment["owin.ResponseBody"] = response; ...was just before the last line of code above is my first pass at this. It turns out I didn't need it after after given that I am altering a reference type however. WorldComponent is just a mild revamp of HelloWorldComponent. It looks like this:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
public class WorldComponent
{
private Func<IDictionary<string, object>, Task> _next;
public WorldComponent(Func<IDictionary<string, object>, Task> next)
{
_next = next;
}
public Task Invoke(IDictionary<string, object> environment)
{
Stream response = environment["owin.ResponseBody"] as Stream;
using (StreamWriter writer = new StreamWriter(response))
{
return writer.WriteAsync("World!!!");
}
}
}
}
After I did this, I turned around and mused that HelloComponent didn't really need to interface with owin.ResponseBody the way the last step in the chain (WorldComponent) must. I ended up refactoring it like so:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
public class HelloComponent
{
private Func<IDictionary<string, object>, Task> _next;
public HelloComponent(Func<IDictionary<string, object>, Task> next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
environment["htmlspool"] = "Hello: ";
await _next(environment);
}
}
}
This makes WorldComponent change up to accomodate.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
public class WorldComponent
{
private Func<IDictionary<string, object>, Task> _next;
public WorldComponent(Func<IDictionary<string, object>, Task> next)
{
_next = next;
}
public Task Invoke(IDictionary<string, object> environment)
{
string htmlspool = environment["htmlspool"] as String;
Stream response = environment["owin.ResponseBody"] as Stream;
using (StreamWriter writer = new StreamWriter(response))
{
return writer.WriteAsync(htmlspool + "World!!!");
}
}
}
}
Another part of Mr. Allen's training involves making an AppFunc using statement not unlike a using directive. Here is what one of those looks like:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
using AppFunc = Func<IDictionary<string, object>, Task>;
public class WorldComponent
{
AppFunc _next;
public WorldComponent(AppFunc next)
{
_next = next;
}
public Task Invoke(IDictionary<string, object> environment)
{
string htmlspool = environment["htmlspool"] as String;
Stream response = environment["owin.ResponseBody"] as Stream;
using (StreamWriter writer = new StreamWriter(response))
{
return writer.WriteAsync(htmlspool + "World!!!");
}
}
}
}
At the very end I decided to fall back to using a .Run instead of a .Use to implement the last step in my process. I did so like this:
using MyKatana.Middleware;
using Owin;
namespace MyKatana
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<HelloComponent>().Run(environment =>
{
return environment.Response.WriteAsync("World!!!");
});
}
}
}
That meant that it now made sense to revert the HelloComponent class to its initial shape:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
namespace MyKatana.Middleware
{
public class HelloComponent
{
private Func<IDictionary<string, object>, Task> _next;
public HelloComponent(Func<IDictionary<string, object>, Task> next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
Stream response = environment["owin.ResponseBody"] as Stream;
using (StreamWriter writer = new StreamWriter(response))
{
writer.Write("Hello: ");
}
await _next(environment);
}
}
}
using System.Web.Http;
using MyKatana.Middleware;
using Owin;
namespace MyKatana
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
"MyRoutingRule",
"api/{controller}/{id}",
new {id = RouteParameter.Optional});
app.UseWebApi(config).Use<HelloComponent>().Run(environment =>
{
return environment.Response.WriteAsync("World!!!");
});
}
}
}
Now the app will continue to just serve up "Hello: World!!!" unless a legitimate ApiController route is hit at which point behavior differs! http://localhost:8080/api/whatever/ is such a route thanks to this controller:
using System;
using System.Web.Http;
using MyKatana.Objects;
namespace MyKatana.Controllers
{
public class WhateverController : ApiController
{
public Whatever Get()
{
return new Whatever()
{
MeaningOfEverything = 42
};
}
}
}
It will cough up an instance of this object:
namespace MyKatana.Objects
{
public class Whatever
{
public int MeaningOfEverything { get; set; }
}
}
No comments:
Post a Comment