仕事
個人活動
http://tanaka733.net
http://www.buildinsider.net/small/csharplang/0600
2
チーム:銀の光
https://mycode.azurewebsites.net/
4
プライベートなコード共有
コード片をとりあえずWeb上に保存したい
人には見られたくない。
URLが知られてもアクセスできないようにしたい。
許可した人は、アクセスできてもいいよ。
5
ふだん、XAML などでアプリ書いている人へ
ASP.NET MVC を知ってもらおう
ASP.NET MVC でこういう機能を実装するなら、
こうしてみた、という実例を紹介しよう
(会社のアプリはASP.NET MVC だけど全体像を触ってないので、
一度自分でWebアプリを作ってみましたというお話です)
6
Azure WebSites でさくっと環境構築
ASP.NET MVC でWeb開発
highlight.js でコードハイライト
ASP.NET Identity で認証・認可
Dapper で軽量DBアクセス
ASP.NET MVC で多言語化
7
9
10
11
12
そのほかできること
WebSitesまわり
SSL、(自動)スケール、カスタムドメイン、バックアップ
WebJobsによるスケジュールタスクの実行
Azure Storage
運用環境でのエラーログの出力先に
Azure Redis Cache
マネージドなRedis
13
ASP.NET MVC でWeb開発
ASP.NET MVC とは
ASP.NET 上で動くWebアプリケーションFW
ASP.NET はIISで動かすのがほとんど
WebFormsと使って比較的モダンな開発スタイル
「設定より規約」(Ruby on Rails like)
フルスタック「ではない」
オープンソース
.NET Core よりずっと前から
15
這い寄る ASP.NET MVC
神獄のヴァルハラゲート
モンスターハンターロアオブカード
弊社ゲームですね
SanSan
法人向けサービスのWeb側(求人情報より)
ConoHa (VPSサービス)のコントロールパネル
VB.NET らしい(求人情報より)
DELLのDriver Downloadサイト
http://www.dell.com/support/home/jp/ja/jpbsd1/Products/?app=drivers
16
17
認証・認可などを
フィルターとして
追加可能
VとC以外がModel。
ASP.NET MVC の
フレームワークに
依存しない処理
18
[Authorize]
public class CodesController : Controller
{
// GET: Codes
public ActionResult Index()
{
var model = new CodeModel();
var authorId = User.Identity.GetUserId();
var codes = model.GetRecentCode(authorId, 5).Select(c => new CodeViewModel(c)).ToArray();
return View(codes);
}
[MyCodeAuthorize]
// GET: Codes/Detail/5
public ActionResult Detail(int id)
{}
// GET: Codes/Create
public ActionResult Create()
{
return View(new CodeCreateViewModel());
}
// POST: Codes/Create
[HttpPost, ValidateInput(false)]
public ActionResult Create([Bind(Include = "Title,LanguageId,RawCode,AllowUsers,IsPublic")]CodeCreateViewModel vm)
{}
}
19
@model GistService.ViewModels.Codes.CodeViewModel[]
<h2>@Html.Resource("Resources, YourCode")</h2>
@Html.ActionLink(Html.Resource("Resources, CreateNew"), "Create", null, new { @class = "btn bt
@foreach (var code in @Model)
{
<h2>@code.Title</h2>
<pre><code class="@code.Language.Brush">@code.RawCode</code></pre><br/>
if (@code.AuthorId == @User.Identity.GetUserId())
{
@Html.ActionLink(Html.Resource("Resources, Detail"), "Detail", new {code.Id}, new {@cl
@Html.ActionLink(Html.Resource("Resources, Edit"), "Edit", new {code.Id}, new {@class
@Html.ActionLink(Html.Resource("Resources, Delete"), "Delete", new {code.Id}, new {@cl
}
<br/><br/>
} 20
@model GistService.ViewModels.Codes.CodeViewModel[]
<h2>@Html.Resource("Resources, YourCode")</h2>
@Html.ActionLink(Html.Resource("Resources, CreateNew"), "Create", null, new { @class = "btn bt
@foreach (var code in @Model)
{
<h2>@code.Title</h2>
<pre><code class="@code.Language.Brush">@code.RawCode</code></pre><br/>
if (@code.AuthorId == @User.Identity.GetUserId())
{
@Html.ActionLink(Html.Resource("Resources, Detail"), "Detail", new {code.Id}, new {@cl
@Html.ActionLink(Html.Resource("Resources, Edit"), "Edit", new {code.Id}, new {@class
@Html.ActionLink(Html.Resource("Resources, Delete"), "Delete", new {code.Id}, new {@cl
}
<br/><br/>
} 21
highlight.js でコードハイライト
23
24
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MyCode</title>
@Styles.Render("~/Content/css")
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/vs.min.css">
</head>
<body>
<pre><code class="@Model.Language.Brush">@Model.RawCode</code></pre>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>
</html>
25
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MyCode</title>
@Styles.Render("~/Content/css")
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/styles/vs.min.css">
</head>
<body>
<pre><code class="@Model.Language.Brush">@Model.RawCode</code></pre>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.3/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>
</html>
26
実際にはDBのテーブルに格納して、
アプリ起動時にメモリに読み込み。
選択肢多いので、
自動補完形式にしたいけど…
27
http://highlightjs.readthedocs.org/en/latest/c
ss-classes-reference.html
28
favicon の準備
http://itexp.hateblo.jp/entry/website-needs-21-favicons
30
http://realfavicongenerator.net/ 31
<head>
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-ico
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-ico
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-ico
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-ico
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-ico
<link rel="icon" type="image/png" href="/favicon-192x192.png" sizes
<link rel="icon" type="image/png" href="/favicon-160x160.png" sizes
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="
<meta name="msapplication-TileColor" content="#2b5797">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">
</head>
32
ASP.NET Identity で認証・認可
認証 (Authentication)
操作しているユーザーの正当性を確認する
@tanaka_733 であることを確認する
認可 (Authorization)
リソースへのアクセス権を確認する
@tanaka_733 が管理者であると確認する
34
独自アカウント登録はしたくない
ユーザーも面倒 (わざわざパスワード覚えたくないetc)
開発者も (パスワードのハッシュ化とか再発行とか…)
3rd Party の認証を使おう
今回はMicrosoftアカウントのみを利用
(本当はTwitter, Googleなど選択できればよかったけど、
実装工数との兼ね合いにより断念)
35
ASP.NET Identity
それまでの ASP.NET メンバーシップよりも柔軟
Nugetから利用可能
3rd partyログインなどカスタマイズ容易
Visual Studioのプロジェクト作成時に組み込める
http://codezine.jp/article/corner/511
36
Microsoft アカウント デベロッパーセンターでアプリ登録
https://account.live.com/developers/applications/index
37
38
39
プロジェクト作成時にオプションつける
40
// 次の行のコメントを解除して、
// サード パーティのログイン プロバイダーを使用したログインを有効にします
app.UseMicrosoftAccountAuthentication(
new MicrosoftAccountAuthenticationOptions
{
ClientId = "0000000000000000",
ClientSecret = “AAAAAAAAAAAAAAAAAAA-bbbbbb"
});
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
41
適切なアクセス制御(差別化要素ですし)
自分のコードは表示・編集・削除できる
権限が与えられているコードは表示できる(編集不可)
ログインページやヘルプは認証なしで表示できる
42
Roleを使った認可が標準機能
UserとRoleをDBで管理
アクセス権限が比較的静的に決まるタイプ
(管理者用のページ、一般ユーザー向けのページ、など)
カスタマイズしよう
AuthorizationFilter の実装
43
Authentication Filters in ASP.NET Web API 2
http://www.asp.net/web-api/overview/security/authentication-filters 44
public class MyCodeAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var idStr = httpContext.Request.RequestContext.RouteData.Values["id"] as string;
int id;
if (!int.TryParse(idStr, out id))
{
return false;
}
var user = httpContext.User;
return user.CanAccessCode(id);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden,
"コードが存在しないか、見る権限がありません");
}
}
45
public class MyCodeAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var idStr = httpContext.Request.RequestContext.RouteData.Values["id"] as string;
int id;
if (!int.TryParse(idStr, out id))
{
return false;
}
var user = httpContext.User;
return user.CanAccessCode(id);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Forbidden,
"コードが存在しないか、見る権限がありません");
}
}
46
[Authorize]
public class CodesController : Controller
{
// GET: Codes
public ActionResult Index()
{}
[MyCodeAuthorize]
// GET: Codes/Detail/5
public ActionResult Detail(int id)
{}
// GET: Codes/Create
public ActionResult Create()
{}
}
47
<system.webServer>
<rewrite>
<rules>
<rule name="Force HTTPS" enabled="true">
<match url="(.*)" ignoreCase="false" />
<conditions>
<add input="{HTTPS}" pattern="off" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}"
appendQueryString="true" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
48
Dapper で軽量DBアクセス
Entity Framework
重厚長大なDBアクセスFW #個人的感想
Recommendedなので、資料や書籍での紹介も多い
Code Firstという機能でコードからDBを管理可能
RoR的なMigration機能も持っている
50
SQL書かせろ!
SQLを抽象化したクラス書くよりSQLの方が楽
実行結果のマッピングはほしい
ResultSet.getInt した結果を代入する、
という処理くらいは自動でやってほしい
DB管理とアプリのデプロイは独立させたい
アプリのデプロイでDB定義更新するのは好きでない
51
public string[] GetAllowedUserIds(int codeId)
{
using (var conn = GetConnection())
{
return conn.Query<string>(@"
SELECT UserId
FROM AllowedUsers
WHERE CodeId = @codeId", new { codeId }).ToArray();
}
}
52
public Code GetById(int id)
{
using (var conn = GetConnection())
{
return conn.Query<Code, AspNetUsers, Code>(@"
SELECT c.*, u.*
FROM Code c
INNER JOIN [AspNetUsers] u ON c.UserId = u.Id
WHERE c.Id = @id", (c, u) =>
{
c.User = u;
return c;
}, new { id }).First();
}
}
53
public int Create(string authorId, string title,
int langId, string rawCode, bool isPublic)
{
using (var conn = GetConnection())
{
return conn.Query<int>(@"
INSERT INTO [Code] (UserId, Title, LangId, RawCode, IsPublic)
OUTPUT INSERTED.Id
VALUES(@authorId, @title, @langId, @rawCode, @isPublic)",
new { authorId, title, langId, rawCode, isPublic }).Single();
}
}
54
ASP.NET MVC で多言語化
ASP.NET MVC 3 の Razor でも多言語対応を試してみる
- しばやん雑記
http://blog.shibayan.jp/entry/20110121/1295543963
ASP.NET MVC 4でViewModelのDisplayName(ラベ
ル)を多言語化する - 虎塚
http://d.hatena.ne.jp/torazuka/20131206/displayname
56
何も選択しない: ブラウザのロケールで選択
<system.web>
<globalization culture="auto"
uiCulture="auto" enableClientBasedCulture="true" />
</system.web>
57
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
<li><a href="@(Request.Url.GetLeftPart(UriPartial.Path)+"?lang=en")">English</a></li>
<li><a href="@(Request.Url.GetLeftPart(UriPartial.Path)+"?lang=ja")">日本語</a></li>
</ul>
58
public class UILanguageFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var lang = filterContext.RequestContext.HttpContext.Request.QueryString.GetValues("lang");
if (lang != null)
{
var l = lang.FirstOrDefault();
if (l != null)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(l);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(l);
var cookie = new HttpCookie("favoritelang", l) { Expires = DateTime.MaxValue };
filterContext.HttpContext.Response.Cookies.Add(cookie);
return;
}
}
}
//続く
59
//続き
var setLang = filterContext.HttpContext.Request.Cookies.Get("favoritelang");
if (setLang != null)
{
var l = setLang.Value;
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(l);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(l);
}
}
60
そして、Universal App?
めとべやなので…
認証をUniversalAppの仕組みに載せたい
コード編集機能強化したい
コードハイライトは当然
 当然欲しいけど、実は最大のネック…
highlight.js の定義からタグを自動生成? or
WinJSならhighlight.js使える?
コード共有されたらPush通知とか
62
認証方式を増やしたい
GoogleとかTwitterとか
その場合のクロスアカウントでの権限指定をどうするか
コード編集機能強化したい
権限与えるところ、うまく補完したい
アカウントの存在確認に使えるので、バランスが難しい
63
まとめ
Azure WebSitesですぐに開発・デプロイ開始
ASP.NET MVC は割と柔軟なフレームワークです
Filter差しこんで認可したり、Cultureいじったり
ASP.NET Identity で認証カスタマイズしたり
Razor は C#er なら使いやすいんじゃないかな!?
EF が Recommendedではあるけど、
SQL書きたい人向けにはDapperも使えますよ
65

20150221 めとべや東京-プライベートコード共有サービス