65

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

  • Upload
    -

  • View
    903

  • Download
    0

Embed Size (px)

Citation preview

仕事

個人活動

http://tanaka733.net

http://www.buildinsider.net/small/csharplang/06002

チーム:銀の光

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: 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

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-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

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/51136

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: 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

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 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 で多言語化

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 の定義からタグを自動生成? orWinJSなら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