blob: f3c22d3004abfe56cc3e60b94940e0cd3b452390 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Xtext - HTTP Routing Language</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description"
content="The website of Eclipse Xtext, an open-source framework for development of programming languages and domain-specific languages">
<meta name="author" content="Sven Efftinge">
<meta name="author" content="Miro Spoenemann">
<!-- styles -->
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Le fav and touch icons -->
<link rel="shortcut icon" href="/Xtext/images/favicon.png">
<link href="/Xtext/css/bootstrap.css" rel="stylesheet" type='text/css'>
<link href="/Xtext/css/bootstrap-responsive.css" rel="stylesheet" type='text/css'>
<link href="/Xtext/css/shield-responsive.css" rel="stylesheet" type='text/css'>
<link href='/Xtext/css/fonts.css' rel='stylesheet' type='text/css'>
<link href="/Xtext/css/prettyPhoto.css" rel="stylesheet" media="screen" type='text/css'>
<link href="/Xtext/css/prettify.css" type="text/css" rel="stylesheet"/>
<link href="/Xtext/css/style.css" rel="stylesheet" type='text/css'>
<!-- cover flow -->
<link href="/Xtext/css/coverflow.css" rel="stylesheet" type='text/css'>
<!--[if lt IE 9]>
<link href="/css/iebugs.css" rel="stylesheet" type='text/css'>
<![endif]-->
</head>
<body>
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-TGDS5S"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-TGDS5S');</script>
<!-- End Google Tag Manager -->
<header class="site-header">
<!-- Navbar -->
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse"
data-target=".nav-collapse"> <span class="icon-bar"></span> <span
class="icon-bar"></span> <span class="icon-bar"></span>
</a> <a class="brand" href="/Xtext/index.html"></a>
<div class="nav-collapse collapse" style="height: 0px;">
<ul class="nav">
<!--li ><a href="/Xtext/news.html">News</a></li-->
<li ><a href="/Xtext/download.html">Download</a></li>
<li ><a href="/Xtext/documentation/index.html">Documentation</a></li>
<li ><a href="/Xtext/community.html">Community</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Support &amp; Trainings<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="https://www.itemis.com/en/xtext/support-and-team/" target="_blank">itemis</a></li>
<li><a href="https://typefox.io/trainings-2" target="_blank">TypeFox</a></li>
</ul>
</li>
<li ><a href="http://xtend-lang.org">Xtend</a></li>
</ul>
<!--div class="nav pull-right">
<li ><a><iframe src="https://ghbtns.com/github-btn.html?user=eclipse&repo=xtext&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe></a></li>
</div-->
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<!-- Navbar End -->
</header>
<div class="page-content">
<script>
function startSearch(event) {
if (event.keyCode == 13) {
var q = 'site:eclipse.org/Xtext/documentation+' + event.target.value;
window.open('https://www.google.com/search?q=' + q, "_self");
}
}
</script>
<div class="wrapper">
<div id="page">
<div class="inner">
<div id="maincontainer" class="container">
<span class="edit-on-github pull-right">
<a href="https://github.com/eclipse/xtext/edit/website-published/xtext-website/documentation/206_httprouting.md">Edit on Github</a>
</span>
<div class="span3" style="margin-left: 0px;">
<div class="search-bar">
<img src="/Xtext/images/search-gray.png"/>
<input type="search" id="google-search" onkeyup="startSearch(event);"/>
</div>
<ul id="nav-outline" style="margin-left: 0px;">
<li class="nav-part">Getting Started</li>
<li><a href="102_domainmodelwalkthrough.html">15 Minutes Tutorial</a></li>
<li><a href="103_domainmodelnextsteps.html">15 Minutes Tutorial - Extended</a></li>
<li><a href="104_jvmdomainmodel.html">Five simple steps to your JVM language</a></li>
<!--li class="nav-part">Seven JVM Languages Built With Xbase</li>
<li><a href="201_sevenlang_introduction.html">Introduction</a></li>
<li><a href="202_scripting.html">Scripting Language</a></li>
<li><a href="203_builddsl.html">Build Language</a></li>
<li><a href="204_mongodb.html">DSL for MongoDB</a></li>
<li><a href="205_guice.html">DSL for Guice</a></li>
<li><a href="206_httprouting.html">Http Routing Language</a></li>
<li><a href="207_template.html">Template Language</a></li>
<li><a href="208_tortoise.html">Little Tortoise</a></li-->
<li class="nav-part">Reference Documentation</li>
<li><a href="301_grammarlanguage.html">The Grammar Language</a></li>
<li><a href="302_configuration.html">Configuration</a></li>
<li><a href="303_runtime_concepts.html">Language Implementation</a></li>
<li><a href="305_xbase.html">Integration with Java</a></li>
<!--li><a href="306_mwe2.html">MWE2</a></li-->
<!--li><a href="307_special_languages.html">Typical Language Configurations</a></li-->
<li><a href="308_emf_integration.html">Integration with EMF</a></li>
<li><a href="310_eclipse_support.html">Eclipse Support</a></li>
<!--li><a href="320_idea_support.html">IntelliJ IDEA Support</a></li-->
<li><a href="330_web_support.html">Web Editor Support</a></li>
<li><a href="350_continuous_integration.html">Continuous Integration</a></li>
<!--li class="nav-part">Appendix</li>
<li><a href="401_migrating_from_1_0_x.html">Migrating from Xtext 1.0.x to 2.0</a></li>
<li><a href="402_migrating_from_0_7.html">Migrating from Xtext 0.7.x to 1.0</a></li-->
</ul>
</div>
<div class="span8 doc-contents">
<h1 id="http-routing">HTTP Routing Language</h1>
<p>This is a little language that lets you dispatch incoming HTTP requests.</p>
<p><img src="images/httprouting_screenshot.png" alt="" /></p>
<h2 id="routing-solution">Overview</h2>
<p>For server-side web development one needs to match any incoming HTTP requests to some handling code. A request is defined by a HTTP method (i.e. GET, POST, etc.), a URL and maybe some other data (headers, parameters, etc.). In the Java world there are many different solutions to this problem. Java Servlets come with an external configuration (web.xml), where you configure what servlet should be called for which URLs. JAX-RS (JSR-311) uses annotations to match incoming URLs and other frameworks have their own external DSLs for that.</p>
<p>The HTTP routing language explained in this section uses an approach very similar to the one from the <a href="http://playframework.org">Play framework</a>. You basically declare a list of URL patterns and explain what to do for each case. In contrast to Play! which heavily relies on static methods, you can also declare so called dependencies which are translated to fields annotated with <code>@Inject</code>. So this DSL plays nicely with dependency injection and especially with the <a href="205_guice.html">Guice modules DSL</a>.</p>
<pre><code class="language-routing">inject GuessTheNumber controller
GET /guess/:theGuess
do controller.handleGuess(theGuess)
</code></pre>
<p>As you can see, you can have named variable placeholders in the URL and use them in the <code>do</code>-part. There also is a <code>when</code>-part which allows to specify an additional condition using the request object as well as any parameters:</p>
<pre><code class="language-routing">inject GuessTheNumber controller
GET /guess/:theGuess
when !controller.isValidGuess(theGuess)
do controller.handleWrongRange(theGuess)
GET /guess/:theGuess
do controller.handleGuess(theGuess)
</code></pre>
<h2 id="routing-running">Running the Example</h2>
<p>The example project’s name is <em>org.xtext.httprouting.examples</em> and includes a simple but runnable number guessing game. Just start the server (an embedded Jetty) by running <code>framework.StartServer</code> as a Java Application. Then point your browser to the URL <a href="http://localhost:8080/guess">http://localhost:8080/guess</a>.</p>
<h2 id="routing-grammar">Grammar</h2>
<p>The Routing DSL extends <em>org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations</em> to make use of expressions predefined by <a href="305_xbase.html#xbase-expressions">Xbase</a> plus support for Annotations.</p>
<pre><code class="language-xtext">grammar org.xtext.httprouting.Route
with org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations
generate route "http://www.xtext.org/httprouting/Route"
import "http://www.eclipse.org/xtext/common/JavaVMTypes" as types
import "http://www.eclipse.org/xtext/xbase/Xbase" as xbase
Model :
importSection=XImportSection?
declarations+=AbstractDeclaration*;
AbstractDeclaration :
Dependency | Route;
Dependency :
'inject' annotations+=XAnnotation? type=JvmTypeReference name=ID;
Route :
requestType=RequestType url=URL
('when' condition=XExpression)?
'do' call=XExpression;
enum RequestType :
GET | POST | PUT | DELETE | HEAD;
/**
* matches URLs like
* '/foo/bar.html' or
* '/customer/:customerID/save'
*/
URL :
{URL}
( '/' | ('/' (QualifiedName | variables+=Variable))*
('/' variables+=Variable wildcard?='*')?);
Variable :
':' name=ID;
</code></pre>
<p>There should not be any surprises if you know the grammar language: A <em>Model</em> consists of an <em>XImportSection</em> followed by any number of <em>Dependencies</em> and <em>Routes</em>. A <em>Route</em> starts with a <em>RequestType</em>, i.e. HTTP method, followed by a <em>URL</em> pattern. Then an optional when-clause can be specified followed by a mandatory do-clause.</p>
<h2 id="routing-inferrer">Translation to Java</h2>
<p>In <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.httprouting/src/org/xtext/httprouting/jvmmodel/RouteJvmModelInferrer.xtend">RouteJvmModelInferrer</a> you can see that a Java class extending the class <a href="https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html">HttpServlet</a> is derived. First the <em>Dependencies</em> are translated to Java fields. This is almost a one-to-one mapping.</p>
<pre><code class="language-xtend">// translate the dependencies to fields annotated with @Inject
for (field : model.declarations.filter(Dependency)) {
members += field.toField(field.name, field.type) [
annotations += annotationRef(Inject)
addAnnotations(field.annotations)
]
}
</code></pre>
<p>Next up a field for the <em>URL</em> patterns is generated and a method for the used expressions, such giving them a proper scope and context.</p>
<pre><code class="language-xtend">// declare fields for the URL regexp, a method for each when-part
// and of course the call part of a route
for (route : model.routes.filter[ url != null ]) {
members += route.toRoutePatternField
if (route.condition != null)
members += route.toRouteConditionMethod
members += route.toRouteCallMethod
}
</code></pre>
<p>Note that the code in a model inferrer has to be very defensive, because it is called for any kind of broken models. You just cannot assume that the URL is set although it is mandatory in the grammar, because the user might have written syntactically incorrect code.</p>
<p>Next up the handler methods from <a href="https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServlet.html">HttpServlet</a> are implemented such that they dispatch according the URL patterns and when-clauses. Here is a translated example:</p>
<ul>
<li>
<p>DSL:</p>
<pre><code class="language-routing">import com.acme.GuessTheNumber
inject GuessTheNumber controller
GET /guess/:theGuess
do controller.handleGuess(theGuess)
</code></pre>
</li>
<li>
<p>Java:</p>
<pre><code class="language-java">@SuppressWarnings("serial")
public class NumberGuessing extends HttpServlet {
@Inject
private GuessTheNumber controller;
private static Pattern _pattern2 = Pattern.compile("/guess/(\\w+)");
public void _doGet2(final HttpServletRequest request,
final HttpServletResponse response,
final String theGuess) {
this.controller.handleGuess(theGuess);
}
@Override
public void doGet(final HttpServletRequest request,
final HttpServletResponse response) {
String url = request.getRequestURL().toString();
{
Matcher _matcher = _pattern2.matcher(url);
if (_matcher.find()) {
String theGuess = _matcher.group(1);
_doGet2(request, response, theGuess);
}
}
}
}
</code></pre>
</li>
</ul>
<p>As you can see the expression <code>controller.handleGuess(theGuess)</code> is put into a method with three parameters. This is done in the following method from <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.httprouting/src/org/xtext/httprouting/jvmmodel/RouteJvmModelInferrer.xtend">RouteJvmModelInferrer</a>:</p>
<pre><code class="language-xtend"> /**
* Creates a method for the route's target call.
* Gives scope and live to the expression.
*/
def protected toRouteCallMethod(Route route) {
route.toMethod(route.nameOfRouteMethod, typeRef(Void.TYPE)) [
parameters += route.toParameter("request", typeRef(HTTP_REQUEST))
parameters += route.toParameter("response", typeRef(HTTP_RESPONSE))
for (variable : route.url.variables) {
parameters += variable.toParameter(variable.name, typeRef(String))
}
body = route.call
]
}
</code></pre>
<p>Just because of that code you can now refer to the local variables <code>request</code>, <code>response</code>, and <code>theGuess</code>. Also it defines that the expected type is <code>void</code> so you are not allowed to write things like <code>return 42</code>.</p>
<hr />
<p><strong><a href="207_template.html">Next Chapter: Template Language</a></strong></p>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="site-footer">
<div id="extra">
<div class="inner">
<div class="container">
<div class="row">
<div class="span6">
<h3 class="footer-links-header">Quick Links</h3>
<ul class="footer-links clearfix">
<li><a href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a></li>
<li><a href="http://www.eclipse.org/legal/termsofuse.php">Terms of Use</a></li>
<li><a href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a></li>
<li><a href="http://www.eclipse.org/legal/">Legal</a></li>
</ul>
<ul class="footer-links clearfix">
<li><a href="http://www.eclipse.org">Eclipse Home</a></li>
<li><a href="http://marketplace.eclipse.org/">Market Place</a></li>
<li><a href="http://live.eclipse.org/">Eclipse Live</a></li>
<li><a href="http://www.planeteclipse.org/">Eclipse Planet</a></li>
</ul>
</div>
<div class="span6">
<h3 class="footer-links-header"><a href="https://twitter.com/xtext" style="color: white;">@Xtext</a> on Twitter</h3>
<a class="twitter-timeline" href="https://twitter.com/xtext" data-widget-id="346625441290928128"
data-chrome="noheader nofooter transparent"
data-theme="dark">Tweets by @xtext</a>
<script>
!function(d,s,id) {
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
if(!d.getElementById(id)) {
js=d.createElement(s);
js.id=id;
js.src=p+"://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js,fjs);
}
}(document,"script","twitter-wjs");
</script>
</div>
</div>
</div>
</div>
</div>
<a href="#" class="scrollup fadeOutRight animated" style="display: none;">ScrollUp</a>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="/Xtext/js/jquery-1.11.3.min.js"></script>
<script src="/Xtext/js/bootstrap.min.js"></script>
<script src="/Xtext/js/jquery.easing.1.3.js" type="text/javascript"></script>
<script src="/Xtext/js/jquery.prettyPhoto.js" type="text/javascript"></script>
<script src="/Xtext/js/twitter.js" type="text/javascript"></script>
<script src="/Xtext/js/prettify.js" type="text/javascript"></script>
<script src="/Xtext/js/lang-xtend.js" type="text/javascript"></script>
<script src="/Xtext/js/lang-common.js" type="text/javascript"></script>
<script src="/Xtext/js/custom.js" type="text/javascript"></script>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<!--script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push([ '_setAccount', 'UA-2429174-3' ]);
_gaq.push([ '_trackPageview' ]);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl'
: 'http://www')
+ '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script-->
<script src="/Xtext/js/coverflow.min.js" type="text/javascript"></script>
<script>
$(function() {
$('#coverflow').coverflow({
active : 1,
visibleAside: 2,
overlap : 0.5,
scale : 0.9,
angle : 20,
trigger : {
"itemfocus" : true,
"swipe" : true,
"mousewheel" : false
}
});
$('#coverflow :hidden').toggle();
$(window).resize(function() {
$('#coverflow').coverflow();
});
});
</script>
</footer>
</body>
</html>