20150221 めとべや東京-プライベートコード共有サービス
TRANSCRIPT
仕事
個人活動
http://tanaka733.net
http://www.buildinsider.net/small/csharplang/06002
ふだん、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
そのほかできること
WebSitesまわりSSL、(自動)スケール、カスタムドメイン、バックアップ
WebJobsによるスケジュールタスクの実行
Azure Storage運用環境でのエラーログの出力先に
Azure Redis CacheマネージドなRedis
13
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
[Authorize]public class CodesController : Controller{
// GET: Codespublic 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/5public ActionResult Detail(int id){}
// GET: Codes/Createpublic 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 btn
@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 {@class = @Html.ActionLink(Html.Resource("Resources, Edit"), "Edit", new {code.Id}, new {@class = @Html.ActionLink(Html.Resource("Resources, Delete"), "Delete", new {code.Id}, new {@class =
}<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 btn
@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 {@class = @Html.ActionLink(Html.Resource("Resources, Edit"), "Edit", new {code.Id}, new {@class = @Html.ActionLink(Html.Resource("Resources, Delete"), "Delete", new {code.Id}, new {@class =
}<br/><br/>
} 21
<!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
http://highlightjs.readthedocs.org/en/latest/c
ss-classes-reference.html
28
http://itexp.hateblo.jp/entry/website-needs-21-favicons
30
<head><link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png"><link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png"><link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png"><link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png"><link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon<link rel="icon" type="image/png" href="/favicon-192x192.png" sizes="192x192"><link rel="icon" type="image/png" href="/favicon-160x160.png" sizes="160x160"><link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16"><link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32"><meta name="msapplication-TileColor" content="#2b5797"><meta name="msapplication-TileImage" content="/mstile-144x144.png">
</head>
32
認証 (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/51136
Microsoft アカウントデベロッパーセンターでアプリ登録
https://account.live.com/developers/applications/index
37
// 次の行のコメントを解除して、// サード パーティのログイン プロバイダーを使用したログインを有効にします
app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions{
ClientId = "0000000000000000",ClientSecret = “AAAAAAAAAAAAAAAAAAA-bbbbbb"
});
//app.UseTwitterAuthentication(// consumerKey: "",// consumerSecret: "");
41
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: Codespublic ActionResult Index(){}
[MyCodeAuthorize]// GET: Codes/Detail/5public ActionResult Detail(int id){}
// GET: Codes/Createpublic 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
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 UserIdFROM AllowedUsersWHERE 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.IdWHERE 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.IdVALUES(@authorId, @title, @langId, @rawCode, @isPublic)",
new { authorId, title, langId, rawCode, isPublic }).Single();}
}
54
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
認証をUniversalAppの仕組みに載せたい
コード編集機能強化したい
コードハイライトは当然
当然欲しいけど、実は最大のネック…
highlight.js の定義からタグを自動生成? orWinJSならhighlight.js使える?
コード共有されたらPush通知とか
62
認証方式を増やしたいGoogleとかTwitterとか
その場合のクロスアカウントでの権限指定をどうするか
コード編集機能強化したい
権限与えるところ、うまく補完したいアカウントの存在確認に使えるので、バランスが難しい
63