Synopse Open Source - Tag - DataSnapmORMot MVC / SOA / ORM and friends2024-02-02T17:08:25+00:00urn:md5:cc547126eb580a9adbec2349d7c65274DotclearmORMots know how to swim like fishesurn:md5:83e960a365673a3b1512d92bb7c6b2dc2013-04-24T15:53:00+02:002013-04-26T13:32:32+02:00AB4327-GANDImORMot FrameworkAJAXauthenticationblogDatabaseDataSnapDelphiHTTPhttp.sysJSONmORMotORMRestSQLSQLite3 <p><img src="http://www.alansmind.com/marmot.jpg" alt="" /></p>
<p>Another great video by <a href="http://synopse.info/forum/profile.php?id=835">warleyalex</a>.</p>
<p>This time, a full FishFacts demo in AJAX, using mORMot and its
<em>SQLite3</em> ORM as server.</p>
<p><a href="http://youtu.be/eqzJQftv89s">See it on YouTube</a>!</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1236">welcome on our forum</a>.</p>
<p><strong>Update:</strong></p>
<p>I've just uploaded the corresponding <em>source code</em> to our
repository.<br />
See sample <a href="http://synopse.info/fossil/dir?name=SQLite3/Samples/19+-+AJAX+ExtJS+FishFacts">
19 - AJAX ExtJS FishFacts</a>.<br />
You need to <a href="https://blog.synopse.info?post/2013/04/24/You%20need%20to%20download%20the%20DB%20from%20http://synopse.info/files/samples/Project19Server.zip">
download the corresponding DB file</a> to run the sample.<br />
Enjoy!</p>Two videos about EXTjs client of mORMot serverurn:md5:41fa721e8f4df06b6f1624a125bc1ca42013-04-02T20:35:00+02:002013-04-02T19:43:55+02:00AB4327-GANDImORMot FrameworkAJAXauthenticationblogDatabaseDataSnapDelphiHTTPhttp.sysJSONmORMotORMRestSourceSQLSQLite3 <p>Two nice videos, posted by a <a href="http://synopse.info/forum/profile.php?id=835">framework user</a>.</p>
<p>The first one presents a <a href="http://www.youtube.com/watch?v=e4vcglINrko">remote RESTful access of a
<em>SQLite3</em> database</a>, hosted by a <em>mORMot</em> server:</p>
<p><iframe width="420" height="315" src="http://www.youtube.com/embed/e4vcglINrko" frameborder="0"></iframe></p>
<p>After <a href="http://synopse.info/forum/viewtopic.php?id=1182">one post in
the forum</a>, <em>warleyalex</em> was able to easily <a href="http://www.youtube.com/watch?v=fh1Iqt6-_uk">add remote filtering of the
request</a>:</p>
<p><iframe width="420" height="315" src="http://www.youtube.com/embed/fh1Iqt6-_uk" frameborder="0"></iframe></p>
<p>In addition to the <a href="http://www.youtube.com/watch?v=LIl1HbjxnIA">previous video</a> about security
(by which the <em>mORMot</em> authentication model seems much more secure than
<em>DataSnap</em>'s), this is a very nice demo!<br />
Thanks a lot, <em>warleyalex</em> for the feedback and information!</p>Video about mORMot authenticationurn:md5:5db5a7efc2a33e5ca9e28d1c052bda152013-01-27T10:13:00+01:002013-01-27T10:18:55+01:00AB4327-GANDImORMot FrameworkAJAXauthenticationblogDataSnapDelphiDocumentationGoodPracticeORMsecuritysession <p>A new <a href="http://synopse.info/forum/viewtopic.php?id=1060"><em>mORMot</em> user notified
on our forum</a> that he just made a short video, about authentication and
security with our framework, from the perspective of an AJAX Client.<br />
Many thanks for sharing your experiences!</p>
<p>This video illustrate <a href="https://blog.synopse.info?post/post/2011/05/24/How-to-implement-RESTful-authentication">how RESTful
authentication is implemented by mORMot</a>.<br />
It compares also with the unsecured scheme used with
<em>DataSnap</em> - pretty informative.</p>
<p><a href="http://youtu.be/LIl1HbjxnIA">Click here to watch the 5 minutes
video.</a></p>
<p>Each time you are logged, a light session is created on the server, and is
the root of all <em>mORMot</em> advanced <a href="https://blog.synopse.info?post/post/2012/03/07/Interface-based-services-implementation-details">security
attributes</a>.<br />
Also a big difference with the heavy implementation of <em>DataSnap</em>
session handling.</p>
<p>Note that the 1.18 upcoming revision feature Windows Authentication, i.e.
<a href="https://blog.synopse.info?post/post/2012/11/20/Authentication-using-Windows-credentials">automatic
log with your Windows credentials.</a><br />
With it, it is not even necessary to enter/remember/manage your login/password
pair: <em>mORMot</em> is able to use your Windows domain security to let you
connected.</p>
<p>Feedback <a href="http://synopse.info/forum/viewtopic.php?id=1060">is
welcome on our forum</a>.</p>Speed comparison between WCF, Java, DataSnap and mORMoturn:md5:fa3f3183eb1cc53d8303d711920a8db02012-11-23T16:03:00+01:002013-01-03T09:49:17+01:00AB4327-GANDImORMot FrameworkblogDataSnapDelphiHTTPhttp.sysJSONmORMotperformanceWCF<p>Roberto Scheinders wrote a <a href="http://robertocschneiders.wordpress.com/2012/11/22/datasnap-analysis-based-on-speed-stability-tests/">
nice blog post</a> about performance and stability of <em>DataSnap</em> XE3,
compared with <em>mORMot</em> and some other available frameworks.</p>
<p><img src="http://fc04.deviantart.net/fs70/f/2012/206/0/7/marmot_can_run_by_jaffa_tamarin-d58lk2i.jpg" alt="" width="450" height="251" /></p>
<p>Compared frameworks were:</p>
<ul>
<li><em>DataSnap</em> (Delphi)</li>
<li><em>mORMot</em> (Delphi)</li>
<li>ASP.NET WCF</li>
<li><em>Jersey/Grizzly</em> (Java)</li>
<li>Node.JS (JavaScript)</li>
</ul>
<p>In short, <em>DataSnap</em> was slow and not stable (concurrent test was
crashing the application), whereas <em>mORMot</em> was very stable, very fast
(faster than any other in concurrent mode), and used much less memory.</p> <p>When compared with WCF, we can be proud that our little <em>mORMot</em> has
a very good responsiveness, in regard to all the resources put by Microsoft for
optimizing their platform.<br />
"Native" compilation like Delphi can do wonders, when used properly, as we try
to do with our framework.</p>
<p>In fact, WCF and <em>mORMot</em> uses the same kernel-mode server of
Windows, i.e. http.sys.<br />
See <a href="http://blog.synopse.info/post/2011/03/11/HTTP-server-using-fast-http.sys-kernel-mode-server">
this blog article.</a></p>
<p>So you can still have "managed" applications (like Java or .Net) be more
responsive than "native" applications, when performance was not part of the
roadmap, just like with <em>DataSnap</em>.</p>
<p>Some benchmark drawings, extracted from the blog article:</p>
<p><img class="aligncenter" title="Performance graph (Test with 50 and 100 threads)" alt="" src="https://lh3.googleusercontent.com/VLrFe5kdzKHPa_nw_pOKqUSsxcJMnLut6ph7z91VnqntOm720-ZG_S06ihS1HGw1GoTODsR-ukufuI-OJ1WSrNRl2MmRkg21S261C8uwJ4gIden3_Zrq" height="345px;" width="622px;" /></p>
<p>In fact, <em>mORMot</em> did only use 50% of the Server side - the
bottleneck on this test was the Client.<br />
With a better client, we may have reached even better results!</p>
<p><img class="aligncenter" alt="" src="https://lh6.googleusercontent.com/vrpvtjxDVx0T2CzJCNUDV6e9fygLEZnswIVrCX__pfWzq565g0Ft5MXAbpJshnPqX-OID3GLZUkakDMRkQU2e3ZgIGkAh120V74y8NO66U0jfGrZkDAe" height="335px;" width="643px;" /></p>
<p>Feedback <a href="http://synopse.info/forum/viewtopic.php?id=943">welcome on
our forum, as usual</a>.</p>DataSnap-like Client-Server JSON RESTful Services in Delphi 6-XE5urn:md5:44777c81aa0e3d15be45197d517de9502011-09-01T20:02:00+02:002020-07-03T09:29:59+02:00AB4327-GANDImORMot FrameworkauthenticationblogDataSnapDelphiDocumentationexceptionmORMotperformanceRestsessionSOASource<p><strong><em>Article update</em>:<br />
The server side call back signature changed since this article was first
published in 2010. <br />
Please refer to the documentation or <a href="http://synopse.info/forum/viewtopic.php?id=958">this forum article and
associated commit</a>.<br />
The article was totally rewritten to reflect the enhancements.<br />
And do not forget to see <em>mORMot</em>'s <a href="https://blog.synopse.info?post/post/2012/03/07/Interface-based-services">interface-based
services</a>!</strong></p>
<p><strong>Note that the main difference with previous implementation is the
signature of the service implementation event, which should be now
exactly:<br /></strong><strong style="font-family: 'Courier New', Courier, monospace; font-size: 1.1em;">procedure
MyService</strong><span style="background-color: yellow; font-family: 'Courier New', Courier, monospace; font-size: 1.1em;">(Ctxt:
TSQLRestServerURIContext);</span><strong><br />
(note that there is one unique <code>class</code> parameter, with no
<code>var</code> specifier)<br />
Please update your code if you are using method-based
services!</strong></p>
<p><strong><img src="http://i.msdn.microsoft.com/dynimg/IC87875.jpg" alt="" /><br /></strong></p>
<p>You certainly knows about the new DataSnap Client-Server features, based on
JSON, introduced in Delphi 2010.<br />
<a href="http://docwiki.embarcadero.com/RADStudio/en/Developing_DataSnap_Applications">http://docwiki.embarcadero.com/RADStudi
… plications</a></p>
<p>We added such communication in our <em>mORmot</em> Framework, in a KISS
(i.e. simple) way: no expert, no new unit or new class. Just add a published
method Server-side, then use easy functions about JSON or URL-parameters to get
the request encoded and decoded as expected, on Client-side.</p> <p>To implement a service in the <em>Synopse mORMot framework</em>, the first
method is to define published method Server-side, then use easy functions about
JSON or URL-parameters to get the request encoded and decoded as expected, on
Client-side.</p>
<p>We'll implement the same example as in the official Embarcadero docwiki page
above.<br />
Add two numbers.<br />
Very useful service, isn't it?</p>
<h3>Publishing a service on the server</h3>
<p>On the server side, we need to customize the standard
<code>TSQLRestServer</code> class definition (more precisely a
<code>TSQLRestServerDB</code> class which includes a <em>SQlite3</em> engine,
or a lighter <code>TSQLRestServerFullMemory</code> kind of server, which is
enough for our purpose), by adding a new <code>published</code> method:</p>
<pre>
<strong>type</strong>
TSQLRestServerTest = <strong>class</strong>(TSQLRestServerFullMemory)
(...)
<span style="background-color:yellow;"><strong>published</strong></span>
<span style="background-color:yellow;"><strong>procedure</strong> Sum(Ctxt: TSQLRestServerURIContext);</span>
<strong>end</strong>;
</pre>
<p>The method name ("Sum") will be used for the URI encoding, and will be
called remotely from <em>ModelRoot/Sum</em> URL.<br />
The <em>ModelRoot</em> is the one defined in the <code>Root</code> parameter of
the <em>model</em> used by the application.</p>
<p>This method, like all Server-side methods, MUST have the same exact
parameter definition as in the <code>TSQLRestServerCallBack</code> prototype,
i.e. only one <code>Ctxt</code> parameter, which refers to the whole
<em>execution context</em>:</p>
<pre>
<strong>type</strong>
TSQLRestServerCallBack = <strong>procedure</strong>(Ctxt: TSQLRestServerURIContext) <strong>of object</strong>;
</pre>
<p>Then we implement this method:</p>
<pre>
<strong>procedure</strong> TSQLRestServerTest.Sum(Ctxt: TSQLRestServerURIContext);
<strong>begin</strong>
<strong>with</strong> Ctxt <strong>do</strong>
Results([Input['a']+Input['b']]);
<strong>end</strong>;
</pre>
<p>The <code>Ctxt</code> variable publish some properties named
<code>InputInt[] InputDouble[] InputUTF8[]</code> and <code>Input[]</code> able
to retrieve directly a parameter value from its name, respectively as
<code>Integer/Int64</code>, <code>double</code>, <code>RawUTF8</code> or
<code>variant</code>.</p>
<p>Therefore, the code above using <code>Input[]</code> will introduce a
conversion via a <code>variant</code>, which may be a bit slower, and in case
of <code>string</code> content, may loose some content for older non Unicode
versions of Delphi.<br />
So it is a good idea to use the exact expected <code>Input*[]</code> property
corresponding to your value type. It does make sense even more when handling
text, i.e. <code>InputUTF8[]</code> is to be used in such case. For our
floating-point computation method, we may have coded it as such:</p>
<pre>
<strong>procedure</strong> TSQLRestServerTest.Sum(Ctxt: TSQLRestServerURIContext);
<strong>begin</strong>
<strong>with</strong> Ctxt <strong>do</strong>
Results([InputDouble['a']+InputDouble['b']]);
<strong>end</strong>;
</pre>
<p>The <code>Ctxt.Results([])</code> method is used to return the service value
as one JSON object with one <code>"Result"</code> member, with default
MIME-type <code>JSON_CONTENT_TYPE</code>.</p>
<p>For instance, the following request URI:</p>
<pre>
GET /root/Sum?a=3.12&b=4.2
</pre>
<p>will let our server method return the following JSON object:</p>
<pre>
{"Result":7.32}
</pre>
<p>That is, a perfectly AJAX-friendly request.</p>
<p>Note that all parameters are expected to be plain case-insensitive
<code>'A'..'Z','0'..'9'</code> characters.</p>
<p>An <em>important point</em> is to remember that the implementation of the
callback method <strong>must be thread-safe</strong>.<br />
In fact, the <code>TSQLRestServer.URI</code> method expects such callbacks to
handle the thread-safety on their side.<br />
It's perhaps some more work to handle a critical section in the implementation,
but, in practice, it's the best way to achieve performance and scalability: the
resource locking can be made at the tiniest code level.</p>
<h3>Defining the client</h3>
<p>The client-side is implemented by calling some dedicated methods, and
providing the service name (<code>'sum'</code>) and its associated
parameters:</p>
<pre>
<strong>function</strong> Sum(aClient: TSQLRestClientURI; a, b: double): double;
<strong>var</strong> err: integer;
<strong>begin</strong>
val(aClient.CallBackGetResult('sum',['a',a,'b',b]),Result,err);
<strong>end</strong>;
</pre>
<p>You could even implement this method in a dedicated client method - which
make sense:</p>
<pre>
<strong>type</strong>
TMyClient = <strong>class</strong>(TSQLHttpClient) <em>// could be TSQLRestClientURINamedPipe</em>
(...)
<strong>function</strong> Sum(a, b: double): double;
(...)
<br /><strong>function</strong> TMyClient.Sum(a, b: double): double;
<strong>var</strong> err: integer;
<strong>begin</strong>
val(CallBackGetResult('sum',['a',a,'b',b]),Result,err);
<strong>end</strong>;
</pre>
<p>This later implementation is to be preferred on real applications.</p>
<p>You have to create the server instance, and the corresponding
<code>TSQLRestClientURI</code> (or <code>TMyClient</code>), with the same
database model, just as usual...</p>
<p>On the Client side, you can use the <code>CallBackGetResult</code> method to
call the service from its name and its expected parameters, or create your own
caller using the <code>UrlEncode()</code> function.<br />
Note that you can specify most class instance into its JSON representation by
using some <code>TObject</code> into the method arguments:</p>
<pre>
<strong>function</strong> TMyClient.SumMyObject(a, b: TMyObject): double;
<strong>var</strong> err: integer;
<strong>begin</strong>
val(CallBackGetResult('summyobject',['a',a,'b',b]),Result,err);
<strong>end</strong>;
</pre>
<p>This Client-Server protocol uses JSON here, as encoded server-side via
<code>Ctxt.Results()</code> method, but you can serve any kind of data, binary,
HTML, whatever... just by overriding the content type on the server with
<code>Ctxt.Returns()</code>.</p>
<h3>Direct parameter marshalling on server side</h3>
<p>We have used above the <code>Ctxt.Input*[]</code> properties to retrieve the
input parameters.<br />
This is pretty easy to use and powerful, but the supplied <code>Ctxt</code>
gives full access to the input and output context.</p>
<p>Here is how we may implement the fastest possible parameters parsing:</p>
<pre>
<strong>procedure</strong> TSQLRestServerTest.Sum(Ctxt: TSQLRestServerURIContext);
<strong>var</strong> a,b: Extended;
<strong>if</strong> UrlDecodeNeedParameters(Ctxt.Parameters,'A,B') <strong>then begin</strong>
<strong>while</strong> Ctxt.Parameters<><strong>nil do begin</strong>
UrlDecodeExtended(Ctxt.Parameters,'A=',a);
UrlDecodeExtended(Ctxt.Parameters,'B=',b,@Ctxt.Parameters);
<strong>end</strong>;
Ctxt.Results([a+b]);
<strong>end else</strong>
Ctxt.Error('Missing Parameter');
<strong>end</strong>;
</pre>
<p>The only not obvious part of this code is the parameters marshaling, i.e.
how the values are retrieved from the incoming <code>Ctxt.Parameters</code>
text buffer, then converted into native local variables.</p>
<p>On the Server side, typical implementation steps are therefore:</p>
<ul>
<li> Use the <code>UrlDecodeNeedParameters</code> function to check that
all expected parameters were supplied by the caller in
<code>Ctxt.Parameters</code>; </li>
<li> Call <code>UrlDecodeInteger / UrlDecodeInt64 / UrlDecodeExtended /
UrlDecodeValue / UrlDecodeObject</code> functions (all defined in
<code>SynCommons.pas</code>) to retrieve each individual parameter from
standard JSON content; </li>
<li> Implement the service (here it is just the <code>a+b</code>
expression); </li>
<li> Then return the result calling <code>Ctxt.Results()</code> method or
<code>Ctxt.Error()</code> in case of any error.</li>
</ul>
<p>The powerful <code>UrlDecodeObject</code> function (defined in
<code>mORMot.pas</code>) can be used to un-serialize most class instance from
its textual JSON representation (<code>TPersistent, TSQLRecord,
TStringList</code>...).</p>
<p>Using <code>Ctxt.Results()</code> will encode the specified values as a JSON
object with one <code>"Result"</code> member, with default mime-type
<code>JSON_CONTENT_TYPE</code>:</p>
<pre>
{"Result":"OneValue"}
</pre>
<p>or a JSON object containing an array:</p>
<pre>
{"Result":["One","two"]}
</pre>
<h3>Returns non-JSON content</h3>
<p>Using <code>Ctxt.Returns()</code> will let the method return the content in
any format, e.g. as a JSON object (via the overloaded
<code>Ctxt.Returns([])</code> method expecting field name/value pairs), or any
content, since the returned MIME-type can be defined as a parameter to
<code>Ctxt.Returns()</code> - it may be useful to specify another mime-type
than the default constant <code>JSON_CONTENT_TYPE</code>, i.e.
<code>'application/json; charset=UTF-8'</code>, and returns plain text, HTML or
binary.</p>
<p>For instance, you can return directly a value as plain text:</p>
<pre>
<strong>procedure</strong> TSQLRestServer.TimeStamp(Ctxt: TSQLRestServerURIContext);
<strong>begin</strong>
Ctxt.Returns(Int64ToUtf8(ServerTimeStamp),HTML_SUCCESS,TEXT_CONTENT_TYPE_HEADER);
<strong>end</strong>;
</pre>
<p>Or you can return some binary file, retrieving the corresponding MIME type
from its binary content:</p>
<pre>
<strong>procedure</strong> TSQLRestServer.GetFile(Ctxt: TSQLRestServerURIContext);
<strong>var</strong> fileName: TFileName;
content: RawByteString;
contentType: RawUTF8;
<strong>begin</strong>
fileName := 'c:\data\'+ExtractFileName(Ctxt.Input['filename']);
content := StringFromFile(fileName);
<strong>if</strong> content='' <strong>then</strong>
Ctxt.Error('',HTML_NOTFOUND) <strong>else</strong>
Ctxt.Returns(content,HTML_SUCCESS,HEADER_CONTENT_TYPE+
GetMimeContentType(pointer(content),Length(content),fileName));
<strong>end</strong>;
</pre>
<p>The corresponding client method may be defined as such:</p>
<pre>
<strong>function</strong> TMyClient.GetFile(<strong>const</strong> aFileName: RawUTF8): RawByteString;
<strong>begin</strong>
<strong>if</strong> CallBackGet('GetFile',['filename',aFileName],RawUTF8(result))<>HTML_SUCCESS <strong>then</strong>
<strong>raise</strong> Exception.CreateFmt('Impossible to get file: %s',[result]);
<strong>end</strong>;
</pre>
<p>If you use HTTP as communication protocol, you can consume these services,
implemented Server-Side in fast Delphi code, with any AJAX application on the
client side.</p>
<p>Using <code>GetMimeContentType()</code> when sending non JSON content (e.g.
picture, pdf file, binary...) will be interpreted as expected by any standard
Internet browser: it could be used to serve some good old HTML content within a
page, not necessary consume the service via JavaScript .</p>
<h3>Advanced process on server side</h3>
<p>On server side, method definition has only one <code>Ctxt</code> parameter,
which has several members at calling time, and publish all service calling
features and context, including <em>RESTful</em> URI routing, session handling
or low-level HTTP headers (if any).</p>
<p>At first, <code>Ctxt</code> may indicate the expected
<code>TSQLRecord</code> ID and <code>TSQLRecord</code> class, as decoded from
<em>RESTful</em> URI.<br />
It means that a service can be related to any table/class of our ORM framework,
so you would be able to create easily any RESTful compatible requests on URI
like <code>ModelRoot/TableName/ID/MethodName</code>.<br />
The ID of the corresponding record is decoded from its <em>RESTful</em> scheme
into <code>Ctxt.ID</code>, and the table is available in
<code>Ctxt.Table</code> or <code>Ctxt.TableIndex</code> (if you need its index
in the associated server Model).</p>
<p>For example, here we return a BLOB field content as hexadecimal, according
to its <code>TableName/Id</code>:</p>
<pre>
<strong>procedure</strong> TSQLRestServerTest.DataAsHex(Ctxt: TSQLRestServerURIContext);
<strong>var</strong> aData: TSQLRawBlob;
<strong>begin</strong>
<strong>if</strong> (self=<strong>nil</strong>) <strong>or</strong> (Ctxt.Table<>TSQLRecordPeople) <strong>or</strong> (Ctxt.ID<0) <strong>then</strong>
Ctxt.Error('Need a valid record and its ID') <strong>else</strong>
<strong>if</strong> RetrieveBlob(TSQLRecordPeople,Ctxt.ID,'Data',aData) <strong>then</strong>
Ctxt.Results([SynCommons.BinToHex(aData)]) <strong>else</strong>
Ctxt.Error('Impossible to retrieve the Data BLOB field');
<strong>end</strong>;
</pre>
<p>A corresponding client method may be:</p>
<pre>
<strong>function</strong> TSQLRecordPeople.DataAsHex(aClient: TSQLRestClientURI): RawUTF8;
<strong>begin</strong>
Result := aClient.CallBackGetResult('DataAsHex',[],RecordClass,fID);
<strong>end</strong>;
</pre>
<p>If <a href="https://blog.synopse.info?post/post/2013/06/07/Authentication-and-Authorization">authentication is used</a>,
the current session, user and group IDs are available in <code>Session /
SessionUser / SessionGroup</code> fields.<br />
If authentication is not available, those fields are meaningless: in fact,
<code>Ctxt.Context.Session</code> will contain either 0
(<code>CONST_AUTHENTICATION_SESSION_NOT_STARTED</code>) if any session is not
yet started, or 1 (<code>CONST_AUTHENTICATION_NOT_USED</code>) if
authentication mode is not active.<br />
Server-side implementation can use the
<code>TSQLRestServer.SessionGetUser</code> method to retrieve the corresponding
user details (note that when using this method, the returned
<code>TSQLAuthUser</code> instance is a local thread-safe copy which shall be
freed when done).</p>
<p>In <code>Ctxt.Call^</code> member, you can access low-level communication
content, i.e. all incoming and outgoing values, including headers and message
body.<br />
Depending on the transmission protocol used, you can retrieve e.g. HTTP header
information.<br />
For instance, here is how you can access the caller remote IP address and
client application user agent:</p>
<pre>
aRemoteIP := FindIniNameValue(pointer(Ctxt.Call.InHead),'REMOTEIP: ');
aUserAgent := FindIniNameValue(pointer(Ctxt.Call.InHead),'USER-AGENT: ');
</pre>
<h3>Browser speed-up for unmodified requests</h3>
<p>When used over a slow network (e.g. over the Internet), you can set the
optional <code>Handle304NotModified</code> parameter of both
<code>Ctxt.Returns()</code> and <code>Ctxt.Results()</code> methods to return
the response body only if it has changed since last time.</p>
<p>In practice, result content will be hashed (using <code>crc32</code>
algorithm) and in case of no modification will return "<em>304 Not
Modified</em>" status to the browser, without the actual result content.<br />
Therefore, the response will be transmitted and received much faster, and will
save a lot of bandwidth, especially in case of periodic server pooling (e.g.
for client screen refresh).</p>
<p>Note that in case of hash collision of the <code>crc32</code> algorithm (we
never did see it happen, but such a mathematical possibility exists), a false
positive "not modified" status may be returned; this option is therefore unset
by default, and should be enabled only if your client does not handle any
sensitive accounting process, for instance.</p>
<p>Be aware that you should <em>disable authentication</em> for the methods
using this <code>Handle304NotModified</code> parameter, via a
<code>TSQLRestServer.ServiceMethodByPassAuthentication()</code> call.<br />
In fact, our RESTful authentication uses a per-URI signature, which change very
often (to avoid men-in-the-middle attacks).<br />
Therefore, any browser-side caching benefit will be voided if authentication is
used: browser internal cache will tend to grow for nothing since the previous
URIs are deprecated, and it will be a cache-miss most of the time.<br />
But when serving some static content (e.g. HTML content, fixed JSON values or
even UI binaries), this browser-side caching can be very useful.</p>
<h3>Handling errors</h3>
<p>When using <code>Ctxt.Input*[]</code> properties, any missing parameter will
raise an <code>EParsingException</code>.<br />
It will therefore be intercepted by the server process (as any other
exception), and returned to the client with an error message containing the
<code>Exception</code> class name and its associated message.</p>
<p>But you can have full access to the error workflow, if needed.<br />
In fact, calling either <code>Ctxt.Results()</code>,
<code>Ctxt.Returns()</code>, <code>Ctxt.Success()</code> or
<code>Ctxt.Error()</code> will specify the HTTP status code (e.g. 200 / "OK"
for <code>Results()</code> and <code>Success()</code> methods by default, or
400 / "Bad Request" for <code>Error()</code>) as an <code>integer</code>
value. </p>
<p>For instance, here is how a service not returning any content can handle
those status/error codes:</p>
<pre>
<strong>procedure</strong> TSQLRestServer.Batch(Ctxt: TSQLRestServerURIContext);
<strong>begin</strong>
<strong>if</strong> (Ctxt.Method=mPUT) <strong>and</strong> RunBatch(<strong>nil</strong>,<strong>nil</strong>,Ctxt) <strong>then</strong>
Ctxt.Success <strong>else</strong>
Ctxt.Error;
<strong>end</strong>;
</pre>
<p>In case of an error on the server side, you may call
<code>Ctxt.Error()</code> method (only the two valid status codes are
<code>200</code> and <code>201</code>).</p>
<p>The <code>Ctxt.Error()</code> method has an optional parameter to specify a
custom error message in plain English, which will be returned to the client in
case of an invalid status code.<br />
If no custom text is specified, the framework will return the corresponding
generic HTTP status text (e.g. <code>"Bad Request"</code> for default status
code <code>HTML_BADREQUEST</code> = 400).</p>
<p>In this case, the client will receive a corresponding serialized JSON error
object, e.g. for <code>Ctxt.Error('Missing
Parameter',HTML_NOTFOUND)</code>:</p>
<pre>
{
"ErrorCode":404,
"ErrorText":"Missing Parameter"
}
</pre>
<p>If called from an AJAX client, or a browser, this content should be easy to
interpret.</p>
<p>Note that the framework core will catch any exception during the method
execution, and will return a <code>"Internal Server Error" /
HTML_SERVERERROR</code> = 500 error code with the associated textual exception
details.</p>
<h3>Benefits and limitations of this implementation</h3>
<p>Method-based services allow fast and direct access to all
<code>mORMot</code> Client-Server <code>RESTful</code> features, over all usual
protocols of our framework: HTTP/1.1, Named Pipe, Windows GDI messages, direct
in-memory/in-process access.</p>
<p>The <em>mORMot</em> implementation of method-based services gives full
access to the lowest-level of the framework core, so it has some
advantages:</p>
<ul>
<li> It can be tuned to fit any purpose (such as retrieving or returning
some HTML or binary data, or modifying the HTTP headers on the fly); </li>
<li> It is integrated into the RESTful URI model, so it can be related to
any table/class of our ORM framework (like <code>DataAsHex</code> service
above), or it can handle any remote query (e.g. any AJAX or SOAP
requests); </li>
<li> It has a very low performance overhead, so can be used to reduce
server workload for some common tasks.</li>
</ul>
<p>Note that due to this implementation pattern, the <em>mORMot</em> service
implementation is very fast, and not sensitive to the "Hash collision attack"
security issue, as reported with <em>Apache</em> - see <a href="http://blog.synopse.info/post/2011/12/30/Hash-collision-attack">http://blog.synopse.info/post/2011/12/30/Hash-collision-attack</a>
for details.</p>
<p>But with this implementation, a lot of process (e.g. parameter marshalling)
is to be done by hand on both client and server side code. In addition,
building and maintaining a huge SOA system with a "method by method" approach
could be difficult, since it publishes one big "flat" set of services.<br />
This is were <code>interface</code>s enter the scene.</p>
<p>See <em>mORMot</em>'s <a href="https://blog.synopse.info?post/post/2012/03/07/Interface-based-services">interface-based services</a>, which
are even more user-friendly and easy to work with than those method-based
services.</p>
<p>Full source code is available in <a href="http://synopse.info/fossil">our
Source Code Repository</a>.<br />
It should work from Delphi 6 to Delphi XE5.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=958">welcome
on our forum, as usual</a>.</p>