mirror of https://github.com/vapor/docs.git
628 lines
28 KiB
HTML
628 lines
28 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="active">
|
|
<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="">
|
|
<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>
|
|
<h1 id="middleware">Middleware</h1>
|
|
<p>Middleware is an essential part of any modern web framework. It allows you to modify requests and responses as they pass between the client and your server.</p>
|
|
<p>You can imagine middleware as a chain of logic connecting your server to the client requesting your web app.</p>
|
|
<h2 id="basic">Basic</h2>
|
|
<p>As an example, let's create a middleware that will add the version of our API to each response. The middleware would look something like this:</p>
|
|
<pre><code class="language-swift">final class VersionMiddleware: Middleware {
|
|
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
|
|
let response = try next.respond(to: request)
|
|
|
|
response.headers["Version"] = "API v1.0"
|
|
|
|
return response
|
|
}
|
|
}</code></pre>
|
|
<p>We then supply this middleware to our <code>Droplet</code>.</p>
|
|
<pre><code class="language-swift">let drop = Droplet()
|
|
drop.middleware.append(VersionMiddleware())</code></pre>
|
|
<p>You can imagine our <code>VersionMiddleware</code> sitting in the middle of a chain that connects the client and our server. Every request and response that hits our server must go through this chain of middleware.</p>
|
|
<p><img src="https://cloud.githubusercontent.com/assets/1342803/17382676/0b51d6d6-59a0-11e6-9cbb-7585b9ab9803.png" alt="Middleware" /></p>
|
|
<h2 id="breakdown">Breakdown</h2>
|
|
<p>Let's break down the middleware line by line.</p>
|
|
<pre><code class="language-swift">let response = try next.respond(to: request)</code></pre>
|
|
<p>Since the <code>VersionMiddleware</code> in this example is not interested in modifying the request, we immediately ask the next middleware in the chain to respond to the request. This goes all the way down the chain to the <code>Droplet</code> and comes back with the response that should be sent to the client.</p>
|
|
<pre><code class="language-swift">response.headers["Version"] = "API v1.0"</code></pre>
|
|
<p>We then <em>modify</em> the response to contain a Version header.</p>
|
|
<pre><code class="language-swift">return response</code></pre>
|
|
<p>The response is returned and will chain back up any remaining middleware and back to the client.</p>
|
|
<h2 id="request">Request</h2>
|
|
<p>The middleware can also modify or interact with the request.</p>
|
|
<pre><code class="language-swift">func respond(to request: Request, chainingTo next: Responder) throws -> Response {
|
|
guard request.cookies["token"] == "secret" else {
|
|
throw Abort.badRequest
|
|
}
|
|
|
|
return try next.respond(to: request)
|
|
}</code></pre>
|
|
<p>This middleware will require that the request has a cookie named <code>token</code> that equals <code>secret</code> or else the request will be aborted.</p>
|
|
<h2 id="errors">Errors</h2>
|
|
<p>Middleware is the perfect place to catch errors thrown from anywhere in your application. When you let the middleware catch errors, you can remove a lot of duplicated logic from your route closures. Take a look at the following example:</p>
|
|
<pre><code class="language-swift">enum FooError: Error {
|
|
case fooServiceUnavailable
|
|
}</code></pre>
|
|
<p>Say there is a custom error that either you defined or one of the APIs you are using <code>throws</code>. This error must be caught when thrown, or else it will end up as a server error which may be unexpected to a user. The most obvious solution is to catch the error in the route closure.</p>
|
|
<pre><code class="language-swift">app.get("foo") { request in
|
|
let foo: Foo
|
|
do {
|
|
foo = try getFooFromService()
|
|
} catch {
|
|
throw Abort.badRequest
|
|
}
|
|
|
|
// continue with Foo object
|
|
}</code></pre>
|
|
<p>This solution works, but it would get repetitive if repeated throughout multiple routes. It can also easily lead to code duplication. Luckily, this error could be caught in a middleware instead.</p>
|
|
<pre><code class="language-swift">final class FooErrorMiddleware: Middleware {
|
|
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
|
|
do {
|
|
return try next.respond(to: request)
|
|
} catch FooError.fooServiceUnavailable {
|
|
throw Abort.custom(
|
|
status: .badRequest,
|
|
message: "Sorry, we were unable to query the Foo service."
|
|
)
|
|
}
|
|
}
|
|
}</code></pre>
|
|
<p>We just need to append this middleware to the <code>Droplet</code>.</p>
|
|
<pre><code class="language-swift">drop.middleware.append(FooErrorMiddleware())</code></pre>
|
|
<p>Now our route closures look a lot better and we don't have to worry about code duplication.</p>
|
|
<pre><code class="language-swift">app.get("foo") { request in
|
|
let foo = try getFooFromService()
|
|
|
|
// continue with Foo object
|
|
}</code></pre>
|
|
<p>Interestingly, this is how <code>Abort</code> itself is implemented in Vapor. <code>AbortMiddleware</code> catches any <code>Abort</code> errors and returns a JSON response. Should you want to customize how <code>Abort</code> errors appear, you can remove this middleware and add your own.</p>
|
|
<h2 id="configuration">Configuration</h2>
|
|
<p>Appending middleware to the <code>drop.middleware</code> array is the simplest way to add middleware--it will be used every time the application starts.</p>
|
|
<p>You can also use the <a href="config.html">configuration</a> files to enabled or disable middleware for more control. This is especially useful if you have middleware that should, for example, run only in production.</p>
|
|
<p>Appending configurable middleware looks like the following:</p>
|
|
<pre><code class="language-swift">let drop = Droplet()
|
|
drop.addConfigurable(middleware: myMiddleware, name: "my-middleware")</code></pre>
|
|
<p>Then, in the <code>Config/droplet.json</code> file, add <code>my-middleware</code> to the appropriate <code>middleware</code> array.</p>
|
|
<pre><code class="language-json">{
|
|
...
|
|
"middleware": {
|
|
"server": [
|
|
...
|
|
"my-middleware",
|
|
...
|
|
],
|
|
"client": [
|
|
...
|
|
]
|
|
},
|
|
...
|
|
}</code></pre>
|
|
<p>If the name of the added middleware appears in the <code>server</code> array for the loaded configuration, it will be added to the server's middleware when the application boots.</p>
|
|
<p>Likewise, if the middleware appears in the <code>client</code> array for the loaded configuration, it will be added to the client's middleware.</p>
|
|
<p>One middleware can be appended to both the Client and the Server, and can be added multiple times. The ordering of middleware is respected.</p>
|
|
<h2 id="extensions-advanced">Extensions (Advanced)</h2>
|
|
<p>Middleware pairs great with request/response extensions and storage.</p>
|
|
<pre><code class="language-swift">final class PokemonMiddleware: Middleware {
|
|
let drop: Droplet
|
|
init(drop: Droplet) {
|
|
self.drop = drop
|
|
}
|
|
|
|
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
|
|
let response = try next.respond(to: request)
|
|
|
|
if let pokemon = response.pokemon {
|
|
if request.accept.prefers("html") {
|
|
response.view = try drop.view("pokemon.mustache", context: pokemon)
|
|
} else {
|
|
response.json = try pokemon.makeJSON()
|
|
}
|
|
}
|
|
|
|
return response
|
|
}
|
|
}</code></pre>
|
|
<p>And the extension to <code>Response</code>.</p>
|
|
<pre><code class="language-swift">extension Response {
|
|
var pokemon: Pokemon? {
|
|
get { return storage["pokemon"] as? Pokemon }
|
|
set { storage["pokemon"] = newValue }
|
|
}
|
|
}</code></pre>
|
|
<p>In this example, we added a new property to response capable of holding a Pokémon object. If the middleware finds a response with one of these Pokémon objects, it will dynamically check whether the client prefers HTML. If the client is a browser like Safari and prefers HTML, it will return a Mustache view. If the client does not prefer HTML, it will return JSON.</p>
|
|
<p>Your closures can now look something like this:</p>
|
|
<pre><code class="language-swift">import HTTP
|
|
|
|
drop.get("pokemon", Pokemon.self) { request, pokemon in
|
|
let response = Response()
|
|
response.pokemon = pokemon
|
|
return response
|
|
}</code></pre>
|
|
<p>Or, if you want to go a step further, you can make <code>Pokemon</code> conform to <code>ResponseRepresentable</code>.</p>
|
|
<pre><code class="language-swift">import HTTP
|
|
|
|
extension Pokemon: ResponseRepresentable {
|
|
func makeResponse() throws -> Response {
|
|
let response = Response()
|
|
response.pokemon = self
|
|
return response
|
|
}
|
|
}</code></pre>
|
|
<p>Now your route closures are greatly simplified and you don't need to <code>import HTTP</code>.</p>
|
|
<pre><code class="language-swift">drop.get("pokemon", Pokemon.self) { request, pokemon in
|
|
return pokemon
|
|
}</code></pre>
|
|
<p>Middleware is incredibly powerful. Combined with extensions, it allows you to add functionality that feels native to the framework.</p>
|
|
<p>For those that are curious, this is how Vapor manages JSON internally. Whenever you return JSON in a closure, it sets the <code>json: JSON?</code> property on <code>Response</code>. The <code>JSONMiddleware</code> then detects this property and serializes the JSON into the body of the response.</p>
|
|
</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>
|