mirror of https://github.com/vapor/docs.git
560 lines
24 KiB
HTML
560 lines
24 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
<title>Vapor Documentation</title>
|
|
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro">
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Quicksand:400,700,300">
|
|
<link rel="stylesheet" href="/1.5/styles/vapor-code.css">
|
|
<link rel="stylesheet" href="/1.5/styles/main.css">
|
|
</head>
|
|
<body>
|
|
|
|
<header>
|
|
<a class="logo" href="/1.5/">
|
|
<img src="/1.5/images/droplet.svg" alt="Vapor">
|
|
<h1>Vapor <em>Docs</em></h1>
|
|
</a>
|
|
<ul>
|
|
<li>
|
|
<a href="http://vapor.codes">Home</a>
|
|
</li>
|
|
<li>
|
|
<a href="http://example.vapor.codes">Example</a>
|
|
</li>
|
|
<li>
|
|
<a href="https://github.com/vapor/vapor">GitHub</a>
|
|
</li>
|
|
<li>
|
|
<a href="https://twitter.com/@codevapor">Twitter</a>
|
|
</li>
|
|
<li>
|
|
<a href="http://vapor.team">Slack</a>
|
|
</li>
|
|
</ul>
|
|
</header>
|
|
|
|
|
|
|
|
<nav>
|
|
<a href="#" class="toggle show">☰</a>
|
|
<a href="#" class="toggle close">×</a>
|
|
|
|
<div class="scroll">
|
|
<section>
|
|
<h3>Getting Started</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/getting-started/install-swift-3-macos.html">
|
|
Install Swift 3: macOS
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/getting-started/install-swift-3-ubuntu.html">
|
|
Install Swift 3: Ubuntu
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/getting-started/install-toolbox.html">
|
|
Install Toolbox
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/getting-started/hello-world.html">
|
|
Hello, World
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/getting-started/manual.html">
|
|
Manual
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/getting-started/xcode.html">
|
|
Xcode
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>Guide</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/guide/droplet.html">
|
|
Droplet
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/folder-structure.html">
|
|
Folder Structure
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/json.html">
|
|
JSON
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/config.html">
|
|
Config
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/views.html">
|
|
Views
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/leaf.html">
|
|
Leaf
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/controllers.html">
|
|
Controllers
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/middleware.html">
|
|
Middleware
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/validation.html">
|
|
Validation
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/provider.html">
|
|
Provider
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/sessions.html">
|
|
Sessions
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/hash.html">
|
|
Hash
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/guide/commands.html">
|
|
Commands
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>Routing</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/routing/basic.html">
|
|
Basic
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/routing/parameters.html">
|
|
Route Parameters
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/routing/query-parameters.html">
|
|
Query Parameters
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/routing/group.html">
|
|
Group
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/routing/collection.html">
|
|
Collection
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>Fluent</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/fluent/driver.html">
|
|
Driver
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/fluent/model.html">
|
|
Model
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/fluent/query.html">
|
|
Query
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/fluent/relation.html">
|
|
Relation
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>Auth</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/auth/user.html">
|
|
User
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/auth/middleware.html">
|
|
Middleware
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/auth/request.html">
|
|
Request
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/auth/protect.html">
|
|
Protect
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>HTTP</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/http/request.html">
|
|
Request
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/http/response.html">
|
|
Response
|
|
</a>
|
|
</li>
|
|
<li class="active">
|
|
<a href="/1.5/http/body.html">
|
|
Body
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/http/response-representable.html">
|
|
ResponseRepresentable
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/http/responder.html">
|
|
Responder
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/http/client.html">
|
|
Client
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/http/server.html">
|
|
Server
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/http/cors.html">
|
|
CORS
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>WebSockets</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/websockets/droplet.html">
|
|
Droplet
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/websockets/custom.html">
|
|
Custom
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>Testing</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/testing/modules.html">
|
|
Modules
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/testing/basic.html">
|
|
Basic
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>Deploy</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/deploy/nginx.html">
|
|
Nginx
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/deploy/supervisor.html">
|
|
Supervisor
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<section>
|
|
<h3>Version (1.5)</h3>
|
|
<ul>
|
|
<li class="">
|
|
<a href="/1.5/switch/1_5.html">
|
|
1.5
|
|
</a>
|
|
</li>
|
|
<li class="">
|
|
<a href="/1.5/switch/2_0.html">
|
|
2.0
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
</div>
|
|
</nav>
|
|
|
|
<main>
|
|
<a href="https://github.com/vapor/documentation/blob/master/CONTRIBUTING.md" class="edit">✎ Edit on GitHub</a>
|
|
<blockquote>
|
|
<p>Module: <code>import HTTP</code></p>
|
|
</blockquote>
|
|
<h1 id="body">Body</h1>
|
|
<p>The <code>HTTP.Body</code> represents the payload of an <code>HTTP.Message</code>, and is used to pass the underlying data. Some examples of this in practice would be <code>JSON</code>, <code>HTML</code> text, or the bytes of an image. Let's look at the implementation:</p>
|
|
<pre><code class="language-swift">public enum Body {
|
|
case data(Bytes)
|
|
case chunked((ChunkStream) throws -> Void)
|
|
}</code></pre>
|
|
<h2 id="data-case">Data Case</h2>
|
|
<p>The <code>data</code> case is by far the most common use for a <code>Body</code> in an <code>HTTP.Message</code>. It is simply an array of bytes. The serialization protocol or type associated with these bytes is usually defined by the <code>Content-Type</code> header. Let's look at some examples.</p>
|
|
<h3 id="applicationjson">Application/JSON</h3>
|
|
<p>If our <code>Content-Type</code> header contains <code>application/json</code>, then the underlying bytes represent serialized JSON.</p>
|
|
<pre><code class="language-swift">if let contentType = req.headers["Content-Type"], contentType.contains("application/json"), let bytes = req.body.bytes {
|
|
let json = try JSON(bytes: bytes)
|
|
print("Got JSON: \(json)")
|
|
}</code></pre>
|
|
<h3 id="imagepng">Image/PNG</h3>
|
|
<p>If our <code>Content-Type</code> contains <code>image/png</code>, then the underlying bytes represent an encoded png.</p>
|
|
<pre><code class="language-swift">if let contentType = req.headers["Content-Type"], contentType.contains("image/png"), let bytes = req.body.bytes {
|
|
try database.save(image: bytes)
|
|
}</code></pre>
|
|
<h2 id="chunked-case">Chunked Case</h2>
|
|
<p>The <code>chunked</code> case only applies to outgoing <code>HTTP.Message</code>s in Vapor. It is traditionally a responder's role to collect an entire chunked encoding before passing it on. We can use this to send a body asynchronously.</p>
|
|
<pre><code class="language-swift">let body: Body = Body.chunked(sender)
|
|
return Response(status: .ok, body: body)</code></pre>
|
|
<p>We can implement this manually, or use Vapor's built in convenience initializer for chunked bodies:</p>
|
|
<pre><code class="language-swift">return Response(status: .ok) { chunker in
|
|
for name in ["joe", "pam", "cheryl"] {
|
|
sleep(1)
|
|
try chunker.send(name)
|
|
}
|
|
|
|
try chunker.close()
|
|
}</code></pre>
|
|
<blockquote>
|
|
<p>Make sure to call <code>close()</code> before the chunker leaves scope.</p>
|
|
</blockquote>
|
|
<h2 id="bodyrepresentable">BodyRepresentable</h2>
|
|
<p>In addition to the concrete <code>Body</code> type, as is common in Vapor, we also have wide support for <code>BodyRepresentable</code>. This means objects that we're commonly converting to <code>Body</code> type can be used interchangeably. For example:</p>
|
|
<pre><code class="language-swift">return Response(body: "Hello, World!")</code></pre>
|
|
<p>In the above example, string is converted to bytes and added to the body.</p>
|
|
<blockquote>
|
|
<p>In practice, it is better to use <code>return "Hello, World!"</code>. Vapor will automatically be able to set the <code>Content-Type</code> to appropriate values.</p>
|
|
</blockquote>
|
|
<p>Let's look at how it's implemented:</p>
|
|
<pre><code class="language-swift">public protocol BodyRepresentable {
|
|
func makeBody() -> Body
|
|
}</code></pre>
|
|
<h3 id="custom">Custom</h3>
|
|
<p>We can conform our own types to this as well where applicable. Let's pretend we have a custom data type, <code>.vpr</code>. Let's conform our <code>VPR</code> file type model:</p>
|
|
<pre><code class="language-swift">extension VPRFile: HTTP.BodyRepresentable {
|
|
func makeBody() -> Body {
|
|
// collect bytes
|
|
return .data(bytes)
|
|
}
|
|
}</code></pre>
|
|
<blockquote>
|
|
<p>You may have noticed above, that the protocol throws, but our implementation does not. This is completely valid in Swift and will allow you to not throw if you're ever calling the function manually.</p>
|
|
</blockquote>
|
|
<p>Now we're able to include our <code>VPR</code> file directly in our <code>Responses</code>.</p>
|
|
<pre><code class="language-swift">drop.get("files", ":file-name") { request in
|
|
let filename = try request.parameters.extract("file-name") as String
|
|
let file = VPRFileManager.fetch(filename)
|
|
return Response(status: .ok, headers: ["Content-Type": "file/vpr"], body: file)
|
|
}</code></pre>
|
|
<p>In practice, if we're repeating this often, we'll probably conform <code>VPRFile</code> directly to <code>ResponseRepresentable</code></p>
|
|
<pre><code class="language-swift">extension VPRFile: HTTP.ResponseRepresentable {
|
|
func makeResponse() -> Response {
|
|
return Response(
|
|
status: .ok,
|
|
headers: ["Content-Type": "file/vpr"],
|
|
body: file
|
|
)
|
|
}
|
|
}</code></pre>
|
|
<p>Here's our above example now:</p>
|
|
<pre><code class="language-swift">drop.get("files", ":file-name") { request in
|
|
let filename = try request.parameters.extract("file-name") as String
|
|
return VPRFileManager.fetch(filename)
|
|
}</code></pre>
|
|
<p>We could also use type-safe routing to make this even more concise:</p>
|
|
<pre><code class="language-swift">drop.get("files", String.self) { request, filename in
|
|
return VPRFileManager.fetch(filename)
|
|
}</code></pre>
|
|
</main>
|
|
|
|
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
|
|
<script src="/1.5/scripts/highlight.pack.js"></script>
|
|
|
|
<script>
|
|
hljs.registerLanguage("leaf", function (hljs) {
|
|
/* Author: Hale Chan <halechan@qq.com> */
|
|
return {
|
|
c: [
|
|
{
|
|
cN: 'function',
|
|
b: '#+',
|
|
e: '[)] ',
|
|
rB: true,
|
|
eE: false,
|
|
c : [
|
|
{
|
|
b: '[(]',
|
|
e: '[)]',
|
|
eE: true,
|
|
c: [
|
|
{
|
|
cN: 'string',
|
|
b: '"',
|
|
e: '"'
|
|
},
|
|
{
|
|
cN: 'title',
|
|
b: '[A-Za-z_][A-Za-z_0-9]*'
|
|
}
|
|
]
|
|
},
|
|
{
|
|
cN: 'keyword',
|
|
b: '#+([A-Za-z$_][0-9A-Za-z$_]*)?',
|
|
eP: true
|
|
},
|
|
]
|
|
}
|
|
]
|
|
};
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
$(function() {
|
|
// Syntax highlighting
|
|
hljs.initHighlightingOnLoad();
|
|
|
|
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
|
|
$('body').addClass('safari');
|
|
}
|
|
|
|
$('h1, h2, h3, h4, h5, h6').each(function(key, item) {
|
|
var $item = $(item);
|
|
var id = $item.attr('id');
|
|
if (id) {
|
|
var link = $('<a>');
|
|
link.attr('href', '#' + id);
|
|
$item.wrap(link);
|
|
}
|
|
})
|
|
|
|
$('a.toggle').on('click', function(e){
|
|
e.preventDefault();
|
|
console.log('hi');
|
|
var body = $('body');
|
|
if (body.hasClass('toggled')) {
|
|
body.removeClass('toggled');
|
|
} else {
|
|
body.addClass('toggled');
|
|
}
|
|
})
|
|
|
|
var lastScroll = 0;
|
|
var isUp = false;
|
|
var win = $(window);
|
|
|
|
win.on('scroll', function(){
|
|
var scrollTop = win.scrollTop();
|
|
var scrollBottom = win.scrollTop() + win.height();
|
|
var docHeight = $(document).height();
|
|
|
|
if ( scrollTop < 0 || scrollBottom >= docHeight ){
|
|
return;
|
|
}
|
|
|
|
if (scrollTop > (lastScroll + 0) && scrollTop >= 0) {
|
|
if (!isUp) {
|
|
$('a.toggle.show').addClass('hide')
|
|
}
|
|
isUp = true;
|
|
lastScroll = scrollTop;
|
|
} else if (scrollTop < (lastScroll - 0)) {
|
|
if (isUp) {
|
|
$('a.toggle.show').removeClass('hide')
|
|
}
|
|
isUp = false;
|
|
lastScroll = scrollTop;
|
|
}
|
|
});
|
|
|
|
// scroll to the active nav item
|
|
$("nav .scroll").animate({
|
|
scrollTop: $("nav .active").offset().top - 157
|
|
}, 0);
|
|
|
|
// var lastScrollTop = 0;
|
|
// $(window).scroll(function(event){
|
|
// var st = $(this).scrollTop();
|
|
// if (st > lastScrollTop){
|
|
// $('a.toggle.show').addClass('hide')
|
|
// } else {
|
|
// $('a.toggle.show').removeClass('hide')
|
|
// }
|
|
// lastScrollTop = st;
|
|
// });
|
|
});
|
|
</script>
|
|
|
|
<script>
|
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
|
|
|
ga('create', 'UA-76177358-4', 'auto');
|
|
ga('send', 'pageview');
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|