A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>What is a Web Framework? (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://www.jeffknupp.com/blog/2014/03/03/what-is-a-web-framework/">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. What is a Web Framework? (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://www.jeffknupp.com/blog/2014/03/03/what-is-a-web-framework/">Source originale du contenu</a></h3>
  445. <p>Web application frameworks, or simply "web frameworks", are the de facto way to
  446. build web-enabled applications. From simple blogs to complex AJAX-rich applications, every
  447. page on the web was created by writing code. I've recently found that
  448. many developers interested in learning a web framework like Flask or Django
  449. don't really understand what a web framework <em>is</em>, what their purpose is, or how they
  450. work. In this article, I'll explore the oft-overlooked topic of web framework
  451. fundamentals. By the end of the article, you should have a solid understanding
  452. of what a web framework is and why they exist in the first place.
  453. This will make it <em>far</em> easier to learn a new web framework and make an informed
  454. decision regarding which framework to use.
  455. </p>
  456. <h2>How The Web Works</h2>
  457. <p>Before we talk about frameworks, we need to understand how the web "works". To
  458. do so, we'll delve into what happens when you type a URL into your browser and
  459. hit <code>Enter</code>. Open a new tab in your browser and navigate to
  460. <a href="http://www.jeffknupp.com">http://www.jeffknupp.com</a>. Let's talk about
  461. the steps your browser took in order to display the page (minus DNS lookups).</p>
  462. <h3>Web Servers and ... web ... servers...</h3>
  463. <p>Every web page is transmitted to your browser as <code>HTML</code>, a language used by
  464. browsers to describe the content and structure of a web page. The application
  465. responsible for sending <code>HTML</code> to browsers is called a <em>web server</em>.
  466. Confusingly, the machine this application resides on is also usually called a
  467. web server. </p>
  468. <p>The important thing to realize, however, is that at the end of the
  469. day, all a web application really does is send <code>HTML</code> to browsers. No matter how
  470. complicated the logic of the application, the final result is always <code>HTML</code>
  471. being sent to a browser (I'm purposely glossing over the ability for
  472. applications to respond with different types of data, like <code>JSON</code> or CSS files,
  473. as the concept is the same).</p>
  474. <p>How does the web application know <em>what</em> to send to the browser? <strong>It sends
  475. whatever the browser requests</strong>.</p>
  476. <h3>HTTP</h3>
  477. <p>Browsers download websites from <em>web servers</em> (or "application servers") using
  478. the <code>HTTP</code> <em>protocol</em> (a <em>protocol</em>, in the realm of programming, is a
  479. universally known data format and sequence of steps enabling communication
  480. between two parties). The <code>HTTP</code> protocol is based on a <code>request-response</code> model.
  481. The client (your browser) <em>requests</em> data from a web application that resides
  482. on a physical machine. The web application in turn <em>responds</em> to the request with
  483. the data your browser requested.</p>
  484. <p>An important point to remember is that communication is always initiated by the
  485. <em>client</em> (your browser). The <em>server</em> (web server, that is) has no way of
  486. initiating a connection to you and sending your browser unsolicited data. If you
  487. receive data from a web server, it is because your browser explicitly asked for
  488. it.</p>
  489. <h4>HTTP Methods</h4>
  490. <p>Every message in the <code>HTTP</code> protocol has an associated <em>method</em> (or <em>verb</em>). The various <code>HTTP</code> methods
  491. correspond to logically different types of requests the client can send, which in turn
  492. represent different intentions on the client side. Requesting the HTML
  493. of a web page, for example, is logically different than submitting a form, so the
  494. two actions require the use of different methods.</p>
  495. <h5>HTTP GET</h5>
  496. <p>The <code>GET</code> method does exactly what it sounds like: gets (requests) data from the
  497. web server. <code>GET</code> requests are the by far the most common <code>HTTP</code> request. During
  498. a <code>GET</code> request the web application shouldn't need to do anything more than
  499. respond with the requested page's HTML. Specifically, the web application should not
  500. alter the state of the application as a result of a <code>GET</code> request (for example,
  501. it should not create a new user account based on a <code>GET</code> request). For
  502. this reason, <code>GET</code> requests are usually considered "safe" since they don't
  503. result in changes to the application powering the website.</p>
  504. <h5>HTTP POST</h5>
  505. <p>Clearly, there is more to interacting with web sites than simply looking at
  506. pages. We are also able to <em>send</em> data to the application, e.g. via a form.
  507. To do so, a different type of request is required: <code>POST</code>. <code>POST</code> requests
  508. usually carry data entered by the user and result in some action being taken
  509. within the web application. Signing up for a web site by entering your
  510. information on a form is done by <code>POST</code>ing the data contained in the form to the
  511. web application.</p>
  512. <p>Unlike a <code>GET</code> request, <code>POST</code> requests usually result in the state of the
  513. application changing. In our example, a new user account is created when the
  514. form is <code>POST</code>ed. Unlike <code>GET</code> requests, <code>POST</code> requests do not always result in
  515. a new HTML page being sent to the client. Instead, the client uses the response's
  516. <em>response code</em> do determine if the operation on the application was successful.</p>
  517. <h4>HTTP Response Codes</h4>
  518. <p>In the normal case, a web server returns a <em>response code</em> of 200, meaning, "I did
  519. what you asked me to and everything went fine". <em>Response codes</em> are always a
  520. three digit numerical code. The web applications must send one with each
  521. response to indicate what happened as a result of a given request. The response code <code>200</code>
  522. literally means "OK" and is the code most often used when responding to a <code>GET</code>
  523. request. A <code>POST</code> request, however, may result in code <code>204</code> ("No Content")
  524. being sent back, meaning "Everything went OK but I don't really have anything to
  525. show you."</p>
  526. <p>It's important to realize that <code>POST</code> requests are still sent
  527. to a specific URL, which may be different from the page the data was submitted
  528. from. Continuing our sign up example, the form may reside at
  529. <code>www.foo.com/signup</code>. Hitting <code>submit</code>, however, may result in a <code>POST</code> request
  530. with the form data being sent to <code>www.foo.com/process_signup</code>. The location a
  531. <code>POST</code> request should be sent to is specified in the form's <code>HTML</code>.</p>
  532. <h2>Web Applications</h2>
  533. <p>You can get quite far using only <code>HTTP</code> <code>GET</code> and <code>POST</code>, as they're the two most
  534. common <code>HTTP</code> methods by a wide margin. A web application, then, is responsible
  535. for receiving an <code>HTTP</code> request and replying with an <code>HTTP</code> response, usually
  536. containing HTML that represents the page requested. <code>POST</code> requests cause the
  537. web application to take some action, perhaps adding a new record in the
  538. database. There are a number of other <code>HTTP</code> methods, but we'll focus on <code>GET</code> and
  539. <code>POST</code> for now.</p>
  540. <p>What would the simplest web application look like? We could write an application
  541. that listened for connections on port <code>80</code> (the well-known <code>HTTP</code> port that
  542. almost all <code>HTTP</code> traffic is sent to). Once it received a connection it would
  543. wait for the client to send a request, then it might reply with some very simple
  544. HTML.</p>
  545. <p>Here's what that would look like:</p>
  546. <div class="codehilite"><pre><span class="kn">import</span> <span class="nn">socket</span>
  547. <span class="n">HOST</span> <span class="o">=</span> <span class="s">''</span>
  548. <span class="n">PORT</span> <span class="o">=</span> <span class="mi">80</span>
  549. <span class="n">listen_socket</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
  550. <span class="n">listen_socket</span><span class="o">.</span><span class="n">bind</span><span class="p">((</span><span class="n">HOST</span><span class="p">,</span> <span class="n">PORT</span><span class="p">))</span>
  551. <span class="n">listen_socket</span><span class="o">.</span><span class="n">listen</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
  552. <span class="n">connection</span><span class="p">,</span> <span class="n">address</span> <span class="o">=</span> <span class="n">listen_socket</span><span class="o">.</span><span class="n">accept</span><span class="p">()</span>
  553. <span class="n">request</span> <span class="o">=</span> <span class="n">connection</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
  554. <span class="n">connection</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="s">"""HTTP/1.1 200 OK</span>
  555. <span class="s">Content-type: text/html</span>
  556. <span class="s">&lt;html&gt;</span>
  557. <span class="s"> &lt;body&gt;</span>
  558. <span class="s"> &lt;h1&gt;Hello, World!&lt;/h1&gt;</span>
  559. <span class="s"> &lt;/body&gt;</span>
  560. <span class="s">&lt;/html&gt;"""</span><span class="p">)</span>
  561. <span class="n">connection</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
  562. </pre></div>
  563. <p>(If the above doesn't work, try changing the <code>PORT</code> to something like <code>8080</code>)</p>
  564. <p>This code accepts a single connection and a single request. Regardless of what
  565. URL was requested, it responds with an <code>HTTP 200</code> response (so it's not <em>really</em> a
  566. web server). The <code>Content-type: text/html</code> line represents a <em>header</em> field.
  567. <em>Headers</em> are used to supply meta-information about the request or response.
  568. In this case, we're telling the client that the data that follows
  569. is HTML (rather than, say, JSON).</p>
  570. <h3>Anatomy of a Request</h3>
  571. <p>If I look at the <code>HTTP</code> request I sent to test the program above, I find it looks
  572. quite similar to the response. The first line is <code>&lt;HTTP Method&gt; &lt;URL&gt; &lt;HTTP version&gt;</code>
  573. or, in this case, <code>GET / HTTP/1.1</code>. After the first line come a few headers like <code>Accept: */*</code>
  574. (meaning we will accept any type of content in a response). That's basically it.</p>
  575. <p>The reply we send has a similar first request line, in the format <code>&lt;HTTP version&gt; &lt;HTTP
  576. Status-Code&gt; &lt;Status-Code Reason-Phrase&gt;</code> or <code>HTTP/1.1 200 OK</code> in our case. Next
  577. come headers, in the same format as the request headers. Lastly, the actual
  578. content of the response is included. Note that this can be encoded as a string
  579. or binary object (in the case of files). The <code>Content-type</code> header lets the
  580. client know how to interpret the response.</p>
  581. <h3>Web Server Fatigue</h3>
  582. <p>If we were going to continue building on the example above as the basis for a
  583. web application, there are a number of problems we'd need to solve:</p>
  584. <ol>
  585. <li>How do we inspect the requested URL and return the appropriate page?</li>
  586. <li>How do we deal with <code>POST</code> requests in addition to simple <code>GET</code> requests</li>
  587. <li>How do we handle more advanced concepts like sessions and cookies?</li>
  588. <li>How do we scale the application to handle thousands of concurrent connections?</li>
  589. </ol>
  590. <p>As you can imagine, no one wants to solve these problems each time they build a
  591. web application. For that reason, packages exist that handle the nitty-gritty
  592. details of the <code>HTTP</code> protocol and have sensible solutions to problems
  593. the problems above. Keep in mind, however, at their core they function in much
  594. the same way as our example: listening for requests and sending <code>HTTP</code> responses with
  595. some HTML back.</p>
  596. <p><em>Note that <strong>client-side</strong> web frameworks are a much different beast and deviate significantly from the above description.</em></p>
  597. <h2>Solving The Big Two: Routing and Templates</h2>
  598. <p>Of all the issues surrounding building a web application, two stand out.</p>
  599. <ol>
  600. <li>How do we map a requested URL to the code that is meant to handle it?</li>
  601. <li>How do we create the requested HTML dynamically, injecting calculated values
  602. or information retrieved from a database?</li>
  603. </ol>
  604. <p>Every web framework solves these issues in some way, and there are many
  605. different approaches. Examples will be helpful, so I'll discuss Django
  606. and Flask's solutions to both of these problems. First, though, we need
  607. to briefly discuss the <em>MVC</em> pattern.</p>
  608. <h3>MVC in Django</h3>
  609. <p>Django makes use of the <em>MVC</em> pattern and requires code using the framework
  610. to do the same. <em>MVC</em>, or "Model-View-Controller" is simply a way of logically
  611. separating the different responsibilities of the application. Resources like
  612. database tables are represented by <em>models</em> (in much the same way a <code>class</code> in
  613. Python often models some real-world object). <em>controllers</em> contain the business
  614. logic of the application and operate on models. <em>Views</em> are given all of
  615. the information they needs to dynamically generate the HTML representation of the page.</p>
  616. <p>Somewhat confusingly, in Django, <em>controllers</em> are called <em>views</em> and <em>views</em>
  617. are called <em>templates</em>. Other than naming weirdness, Django is a pretty
  618. straightforward implementation of an <em>MVC</em> architecture.</p>
  619. <h3>Routing in Django</h3>
  620. <p><em>Routing</em> is the process of mapping a requested URL to the code responsible for
  621. generating the associated HTML. In the simplest case, <em>all</em> requests are handled
  622. by the same code (as was the case in our earlier example). Getting a little more
  623. complex, every URL could map 1:1 to a <code>view function</code>. For example, we could
  624. record somewhere that if the URL <code>www.foo.com/bar</code> is requested, the function
  625. <code>handle_bar()</code> is responsible for generating the response. We could build up
  626. this mapping table until all of the URLs our application supports are enumerated
  627. with their associated functions.</p>
  628. <p>However, this approach falls flat when the URLs contain useful data, such as the
  629. ID of a resource (as is the case in <code>www.foo.com/users/3/</code>). How do we map that
  630. URL to a view function, and at the same time make use of the fact that we want
  631. to display the user with ID <code>3</code>? </p>
  632. <p>Django's answer is to map URL <em>regular expressions</em> to view functions that can
  633. take parameters. So, for example, I may say that URLs that match
  634. <code>^/users/(?P&lt;id&gt;\d+)/$</code> calls the <code>display_user(id)</code> function where the <code>id</code>
  635. argument is the captured group <code>id</code> in the regular expression. In that way, any
  636. <code>/users/&lt;some number&gt;/</code> URL will map to the <code>display_user</code> function. These
  637. regular expressions can be arbitrarily complex and include both keyword and
  638. positional parameters.</p>
  639. <h3>Routing in Flask</h3>
  640. <p>Flask takes a somewhat different approach. The canonical method for hooking up
  641. a function to a requested URL is through the use of the <code>route()</code> decorator. The
  642. following Flask code will function identically to the regex and function listed
  643. above:</p>
  644. <div class="codehilite"><pre><span class="nd">@app.route</span><span class="p">(</span><span class="s">'/users/&lt;id:int&gt;/'</span><span class="p">)</span>
  645. <span class="k">def</span> <span class="nf">display_user</span><span class="p">(</span><span class="nb">id</span><span class="p">):</span>
  646. <span class="c"># ...</span>
  647. </pre></div>
  648. <p>As you can see, the decorator uses an almost simplified form of regular expression
  649. to map URLs to arguments (one that implicitly uses <code>/</code> as separators). Arguments are
  650. captured by including a <code>&lt;name:type&gt;</code> directive in the URL passed to <code>route()</code>.
  651. Routing to static URLs like <code>/info/about_us.html</code> is handled as you would
  652. expect: <code>@app.route('/info/about_us.html')</code></p>
  653. <h3>HTML Generation Through Templates</h3>
  654. <p>Continuing the example above, once we have the appropriate piece of code mapped
  655. to the correct URL, how do we dynamically generate HTML in a way that still
  656. allows web designers to hand-craft it? For both Django and Flask,
  657. the answer is through <em>HTML templating</em>.</p>
  658. <p><em>HTML Templating</em> is similar to using <code>str.format()</code>: the desired output is written
  659. with placeholders for dynamic values. These are later replaced by arguments to
  660. the <code>str.format()</code> function. Imagine writing an entire web page as a single string,
  661. marking dynamic data with braces, and calling <code>str.format()</code> at the end.
  662. Both <em>Django templates</em> and <a href="http://jinja.pocoo.org">jinja2</a>, the template engine
  663. Flask uses, are designed to be used in this way.</p>
  664. <p>However, not all templating engines are created equal. While Django has
  665. rudimentary support for programming in templates, Jinja2 basically lets you execute
  666. arbitrary code (it doesn't <em>really</em>, but close enough). Jinja2 also aggressively <em>caches</em>
  667. the result of rendering templates, so that subsequent requests with the exact
  668. same arguments are returned from the cache instead of expensively being
  669. re-rendered.</p>
  670. <h3>Database Interaction</h3>
  671. <p>Django, with its "batteries included" philosophy, includes an <code>ORM</code>
  672. ("Object Relational Mapper"). The purpose of an <code>ORM</code> is two-fold: it maps Python
  673. classes to database tables and abstracts away the differences between various
  674. database engines (though the former is its primary role). No one loves <code>ORM</code>s
  675. (because the mapping between domains is never perfect), rather, they are
  676. tolerated. Django's is reasonably full-featured. Flask, being a
  677. "micro-framework", does not include one (though it is quite compatible with
  678. SQLAlchemy, the Django <code>ORM</code>'s biggest/only competitor).</p>
  679. <p>The inclusion of an <code>ORM</code> gives Django the ability to create a full-featured
  680. <code>CRUD</code> application. <code>CRUD</code> (<strong>C</strong>reate <strong>R</strong>ead <strong>U</strong>pdate <strong>D</strong>elete)
  681. applications seem to be the sweet spot for web frameworks (on the server side).
  682. Django (and Flask-SQLAlchemy) make the various <code>CRUD</code> operations for each model
  683. straightforward.</p>
  684. <h2>Web Framework Round-Up</h2>
  685. <p>By now, the purpose of web frameworks should be clear: to hide the boilerplate
  686. and infrastructural code related to handling <code>HTTP</code> requests and responses. Just
  687. <em>how much</em> is hidden depends on the framework. Django and Flask represent two
  688. extremes. Django includes something for every situation, almost to its
  689. detriment. Flask bills itself as a "micro-framework" and handles the bare
  690. minimum of web application functionality, relying on third-party packages to do
  691. some of the less common web framework tasks.</p>
  692. <p>Remember, though, that at the end of the day, Python web frameworks all work the
  693. same way: they receive <code>HTTP</code> requests, dispatch code that generates HTML, and
  694. creates an <code>HTTP</code> response with that content. In fact, <em>all</em> major server-side
  695. frameworks work in this way (excluding JavaScript frameworks). Hopefully, you're
  696. now equipped to choose between frameworks as you understand their purpose.</p>
  697. </article>
  698. </section>
  699. <nav id="jumpto">
  700. <p>
  701. <a href="/david/blog/">Accueil du blog</a> |
  702. <a href="http://www.jeffknupp.com/blog/2014/03/03/what-is-a-web-framework/">Source originale</a> |
  703. <a href="/david/stream/2019/">Accueil du flux</a>
  704. </p>
  705. </nav>
  706. <footer>
  707. <div>
  708. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  709. <p>
  710. Bonjour/Hi!
  711. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  712. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  713. </p>
  714. <p>
  715. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  716. </p>
  717. <p>
  718. Voici quelques articles choisis :
  719. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  720. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  721. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  722. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  723. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  724. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  725. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  726. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  727. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  728. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  729. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  730. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  731. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  732. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  733. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  734. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  735. </p>
  736. <p>
  737. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  738. </p>
  739. <p>
  740. Je ne traque pas ta navigation mais mon
  741. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  742. conserve des logs d’accès.
  743. </p>
  744. </div>
  745. </footer>
  746. <script type="text/javascript">
  747. ;(_ => {
  748. const jumper = document.getElementById('jumper')
  749. jumper.addEventListener('click', e => {
  750. e.preventDefault()
  751. const anchor = e.target.getAttribute('href')
  752. const targetEl = document.getElementById(anchor.substring(1))
  753. targetEl.scrollIntoView({behavior: 'smooth'})
  754. })
  755. })()
  756. </script>