WebフレームワークにはNancyを使いました。
※SignalRが含まれていますがまだ手を付けていません。
プロジェクト構成
FormとWebページが混在しています。分けたいです。
package.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="jQuery" version="1.6.4" targetFramework="net451" />
<package id="jquery.TypeScript.DefinitelyTyped" version="1.4.0" targetFramework="net451" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net451" />
<package id="Microsoft.AspNet.SignalR" version="2.1.2" targetFramework="net451" />
<package id="Microsoft.AspNet.SignalR.Core" version="2.1.2" targetFramework="net451" />
<package id="Microsoft.AspNet.SignalR.JS" version="2.1.2" targetFramework="net451" />
<package id="Microsoft.AspNet.SignalR.SystemWeb" version="2.1.2" targetFramework="net451" />
<package id="Microsoft.Owin" version="3.0.0" targetFramework="net451" />
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.0" targetFramework="net451" />
<package id="Microsoft.Owin.Host.SystemWeb" version="2.0.1" targetFramework="net451" />
<package id="Microsoft.Owin.Hosting" version="3.0.0" targetFramework="net451" />
<package id="Microsoft.Owin.Security" version="3.0.0" targetFramework="net451" />
<package id="Nancy" version="0.23.2" targetFramework="net451" />
<package id="Nancy.Authentication.Forms" version="0.23.2" targetFramework="net451" />
<package id="Nancy.Owin" version="0.23.2" targetFramework="net451" />
<package id="Nancy.Viewengines.Razor" version="0.23.2" targetFramework="net451" />
<package id="Newtonsoft.Json" version="6.0.4" targetFramework="net451" />
<package id="Owin" version="1.0" targetFramework="net451" />
</packages>
Bootstrapperクラス
Windowsアプリでホストする都合上、RootPathProviderを独自に用意する必要があります。
今回はデバッグビルドの出力先が bin\Debug に生成されていることを前提とした、SelfHostRootProviderクラスを実装しました。 (リリースビルドのときはexeとリソースフォルダが同じ場所に配置されると思うので、気にしていません。)
あとは、静的リソース場所の設定とフォーム認証とセッションの有効化を行っています。
namespace SelfHostApp2
{
public class MyBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(Nancy.TinyIoc.TinyIoCContainer container, Nancy.Bootstrapper.IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
#if DEBUG
container.Register<IRootPathProvider>(new SelfHostRootProvider());
#endif
CookieBasedSessions.Enable(pipelines);
}
protected override void ConfigureConventions(NancyConventions nancyConventions)
{
base.ConfigureConventions(nancyConventions);
this.Conventions.StaticContentsConventions.Clear();
this.Conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("/Content"));
this.Conventions.StaticContentsConventions.Add(StaticContentConventionBuilder.AddDirectory("/Scripts"));
}
protected override void ConfigureRequestContainer(Nancy.TinyIoc.TinyIoCContainer container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
container.Register<IUserMapper, UserDatabase>(); //今回は掲載していないが、認証用のUserDatabaseクラスが別途存在する。
}
protected override void RequestStartup(Nancy.TinyIoc.TinyIoCContainer container, Nancy.Bootstrapper.IPipelines pipelines, NancyContext context)
{
base.RequestStartup(container, pipelines, context);
var formsAuthConfiguration = new FormsAuthenticationConfiguration()
{
RedirectUrl = "/Auth", //認証失敗時のリダイレクト先
UserMapper = container.Resolve<IUserMapper>()
};
FormsAuthentication.Enable(pipelines, formsAuthConfiguration); //フォーム認証の有効化
}
}
#if DEBUG
public class SelfHostRootProvider : IRootPathProvider
{
public string GetRootPath()
{
var assembly = Assembly.GetEntryAssembly();
return assembly != null ?
GetProjectDirectory(assembly.Location) :
GetProjectDirectory(Assembly.GetExecutingAssembly().Location);
}
private string GetProjectDirectory(string asmPath)
{
var di = new DirectoryInfo(Path.GetDirectoryName(asmPath));
return di.Parent.Parent.FullName;
}
}
#endif
}
Razorテンプレート
Razorテンプレートからhtmlが生成され、静的リソースの参照が可能ことを確認しました。
ここに載せてはいませんが、フォーム認証機能とセッション機能も有効に動きました。
@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<dynamic>
<!DOCTYPE html>
<html lang="jp" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>@Model.Detail</title>
<link rel="stylesheet" href="/Content/style/index.css" />
</head>
<body>
<div class="container">
<p>24時間まえの時間は @DateTime.Now.AddHours(-24.0d) です。</p>
<p>現在の時間は @DateTime.Now です。</p>
<p>24時間後の時間は @DateTime.Now.AddHours(24.0d) です。</p>
<img class="fax-image" src="/Content/img/faxfax.jpg" />
</div>
<script src="/Scripts/jquery-1.6.4.min.js"></script>
<script src="/Scripts/Hoge.js"></script>
</body>
</html>
TypeScript
プロジェクトがWindowsアプリケーションのため、TypeScriptファイルを編集保存してもjavascriptへの自動変換は行われません。
Webアプリケーションのプロジェクトファイルと比較して差異を確認したところ、下記2箇所を追加することで自動変換されるようになりました。
1. /Project 要素
以下のImport要素を追加。
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets')" />
2. /Project/PropertyGroup 要素
先頭のPropertyGroup要素に以下を追加。
<TypeScriptToolsVersion>1.0</TypeScriptToolsVersion>
フォーム
using Microsoft.Owin.Hosting;
namespace SelfHostApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
btnStart.Enabled = true;
btnStop.Enabled = false;
}
private IDisposable webapp = null;
private void button1_Click(object sender, EventArgs e)
{
var url = "http://+:" + txtPort.Text;
this.webapp = WebApp.Start(url);
btnStart.Enabled = false;
btnStop.Enabled = true;
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
StopWebApp();
}
private void btnStop_Click(object sender, EventArgs e)
{
StopWebApp();
btnStart.Enabled = true;
btnStop.Enabled = false;
}
private void StopWebApp()
{
if (webapp != null)
webapp.Dispose();
}
}
}