Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
192 lines (168 sloc) 7.85 KB
---
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
title: "Developing Web Service Applications"
---
<p>
This document explains how to develop (REST) web service type applications on the container -
design options, accessing the request path, returning a status code etc.
There are two types of web service APIs:
<ul>
<li>Fine-grained APIs with closed semantics – for example <em>return the
number of stars of an article</em></li>
<li>Coarse-grained APIs with open semantics – for example <em>return a page
containing the most relevant mixture of stuff for this user and action</em></li>
</ul>
With fine-grained APIs, the container can help map the various requests to
method calls through exposing an implementation of <em>JAX-RS(Jersey)</em>.
With coarse-grained APIs, it can help dealing with the complexity typically
involved in the implementation of such APIs
by providing a way to compose and federate components contributing to processing the request
and provide and modify the returned data,
and a way to allow such requests to start returning
before they are finished to reduce latency with large responses.
This is the <a href="jdisc/processing.html">processing</a> framework
(or, in the case of search-like application,
the <a href="searcher-development.html">searcher</a> specialization).
</p><p>
In addition, the <a href="jdisc/how-does-jdisc-work.html">container</a>
features a generic mechanism which allows a
<a href="jdisc/how-does-jdisc-work.html#requesthandler">Handler</a> component
to be <a href="jdisc/how-does-jdisc-work.html#binding">bound</a> to a URL
and invoked to handle all requests matching that URL.
This is useful where there is neither a large fine-grained API
nor any need to handle complexity and/or federation of various kinds of data in the response.
Both the approaches above are implemented as provided Handlers.
</p><p>
A custom Handler may be written to parse the url path/method
and dispatch to an appropriate chain of processing components.
A "main" processing chain may be written
to do the same by dispatching to other chains.
</p><p>
The simplest way to invoke a specific chain of processors
is to forward a query to the <code>ProcessingHandler</code>
with the request property <code>chain</code> set to the name of the chain to invoke:
<pre>
import com.google.inject.Inject;
public class DemoHandler extends com.yahoo.container.jdisc.ThreadedHttpRequestHandler {
...
@Inject
public DemoHandler(Executor executor, ProcessingHandler processingHandler) {
super(executor);
this.processingHandler = processingHandler;
}
...
@Override
public HttpResponse handle(HttpRequest request) {
HttpRequest processingRequest = new HttpRequest.Builder(request)
.put(com.yahoo.processing.Request.CHAIN, "theProcessingChainIWant")
.createDirectRequest();
HttpResponse r = processingHandler.handle(processingRequest);
return r;
}
...
}
</pre>
</p>
<h2 id="accessing-the-http-request">Accessing the HTTP request</h2>
<p>
In a general JDisc <a href="jdisc/developing-request-handlers.html">request handler</a>
the <code>com.yahoo.jdisc.Request</code> can be cast to an instance
of <code>com.yahoo.jdisc.http.HttpRequest</code> when the request is over HTTP.
Web service handlers should usually extend
<code>com.yahoo.container.jdisc.ThreadedHttpRequestHandler</code>
(or the <code>com.yahoo.container.jdisc.LoggingRequestHandler</code> subclass
which also does access logging),
which provides to subclasses a <code>com.yahoo.container.jdisc.HttpRequest</code>
which contains both the core HttpRequest and the associated properties and request data.
</p><p>
In <a href="jdisc/processing.html">Processing</a>,
one is given a <code>com.yahoo.processing.Request</code>
which contains the HTTP URL parameters
as well as the entire container request as a member variables.
So to access the HttpRequest data in Processing, use:
<pre>
// url parameters are added to properties
String urlParameter = request.properties().get("urlParameterName");
// jdisc request context is added with prefix context
Object contextValue = request.properties().get("context.contextKey");
// or, to access path, HTTP method or data, get the whole HTTP request:
com.yahoo.container.jdisc.HttpRequest httpRequest =
(com.yahoo.container.jdisc.HttpRequest) request.properties().get("jdisc.request");
// Then POST data inputstream would be:
InputStream in = httpRequest.getData();
// And the HTTP method:
Method method = httpRequest.getMethod();
</pre>
</p>
<h3 id="setting-the-HTTP-status-and-http-headers">Setting the HTTP status and HTTP headers</h3>
<p>
In Processing, the return status can be set by adding a special Data item to the Response:
<pre>
response.data().add(new com.yahoo.processing.handler.ResponseStatus(404, request));
</pre>
If no such data element is present, the status will be determined by the container.
If it contains renderable data it will be 200,
otherwise it will be determined by any ErrorMessage present in the response.
</p>
<h3 id="setting-response-headers-from-processors">Setting response headers from Processors</h3>
<p>
Response headers may be added to any Response by adding instances of
<code>com.yahoo.processing.handler.ResponseHeaders</code> to the Response
(ResponseHeaders is a kind of response Data).
Multiple instances of this may be added to the Response,
and the complete set of headers returned is the superset of all such objects.
Example Processor:
<pre>
processingResponse.data().add(new com.yahoo.processing.handler.ResponseHeaders(myHeaders, request));
</pre>
Request handlers may in general set their return status,
and manipulate headers directly on the HttpRequest.
</p>
<h2 id="search-queries">Search queries</h2>
<p>
Sometimes all that is needed is letting the standard search framework
reply for more paths than standard.
This is possible by adding extra <a href="reference/services-search.html#binding">binding</a>s
inside the <code>&lt;search&gt;</code> element in <code>services.xml</code>.
Writing a custom <a href="jdisc/developing-request-handlers.html">request handler</a>
is recommended if the application is a standalone HTTP API,
and especially if there are properties used with the same name as those in the
<a href="reference/search-api-reference.html">search API</a>.
A request handler may query the search components running in the same container
with any appreciable overhead:</p>
<h3 id="invoking-search-queries-from-a-jdisc-component">Invoking search queries from a JDisc component</h3>
<p>
It is not necessary to dispatch a request through JDisc,
with the overhead associated with that procedure,
to perform a search query from e.g. a request handler
running in the JDisc Container if the same container also functions as a search container.
If the application is a search application,
simply add the class <em>com.yahoo.search.handler.SearchHandler</em>
to the list of constructor arguments,
and invoke its <em>handle(HttpRequest)</em> method directly. Example:
</p><p>
Construction:
<pre>
private final SearchHandler searchHandler;
public HelloWorld(Executor executor, AccessLog accessLog,
SearchHandler searchHandler, ResponseConfig config) {
super(executor, accessLog);
super(executor, accessLog,null,true);
this.response = config.response();
this.searchHandler = searchHandler;
}
</pre>
Request handling:
<pre>
@Override
public HttpResponse handle(HttpRequest request) {
HttpRequest searchRequest = new HttpRequest.Builder(request)
.put(Model.QUERY_STRING, "something to look for")
.createDirectRequest();
HttpResponse response = searchHandler.handle(searchRequest);
return r;
}
</pre>
The search will then run directly in the thread owned by the <em>HelloWorld</em> request handler,
and no extra resolving of URI pattern bindings is necessary.
</p>