ServiceRequiresLogin in Delphi

I have converted a RODL based service to Code First so that I can use the new HTTP API. However, there is a lack of detail in the documentation and samples on how to do anything. For example, the documentation says I can use the ServiceRequiresLogin option to replicate the old way. But there is no mention of how, nor any reference in any of the library code. And what about the “old” RequiresSession property in the DataModule - that is still there - is that the real answer still?

Likewise there is a lack of information on the new HTTP API and the attributes and how to use them. I also note that there is an option to support HTTP response codes through exceptions, but how is this handled for the “old” function use? Do I have to define two functions, one for old, one new?

I’d much appreciate some worked examples of all of the options. It would also be good to have more complex examples of a service with a separate login service - all the samples seem to be very simple.

Thanks for moving it all forward though - looks good. I just need to know how to use it!

Looks like you read .NET based part about HTTP API.
Delphi part still uses RequiresSession .

I’ve attached Delphi sample that demonstrates usage of sessions:
httpapi_security.zip (33.1 KB)

ServiceBuilder has special editor for HTTP API attributes:

for CodeFirst based services, you need to provide these attributes manually via ROCustom attribute like

// for methods
    [ROCustom('HttpApiPath','calculate')]
    [ROCustom('HttpApiMethod','GET')]
    [ROCustom('HttpApiResult','201')]
    [ROCustom('HttpApiTags', 'tag')]
    [ROCustom('HttpApiOperationId', 'operation')]
// for parameters
    [ROCustom('HttpApiQueryParameter','1')]
    [ROCustom('HttpApiHeaderParameter','1')]

Response code can be provided via

raise EROHttpApiException.Create(HTTP_404_code, HTTP_404_status);

see also HTTP API sample at Adding HttpAPI to a Server (Delphi) article.

Note: You may need to install latest ROD beta.

Thank you for the response. Yes, it does look like the .Net info is mixed in with the Delphi info quite a bit - that page doesn’t make it clear that it is different.

I would like to know how to handle responses differently according to how the API is called - an HTTP exception causes what to happen when called with the old clients?

I shall separate my questions, so will create a new question on out parameters.

you can create http api wrappers for your existing methods that will return required codes, like

function TMyService.httpapi_wrapper_for_method1(...): ...;
begin
  try
     result := method1(...);
  except
    on e: EMYException1 do raise EROHttpApiException.Create(my_httpapi_code1);
    on e: EMYException2 do raise EROHttpApiException.Create(my_httpapi_code2);
    on e: Exception do raise EROHttpApiException.Create(my_httpapi_code3);
  end;
end;

another possibility is detect http api call in OnGetDispatchInfo event and raise correspondent error in old method

Sorry to resurrect this but I’m looking at the HttpApi stuff in Delphi myself now.

The above example doesn’t seem to work properly. The login call succeeds with a 200 result and the memo shows a session created but all the other calls fail with a 401, regardless of whether I’ve called login to create the session.

Hi,

Can you create a simple testcase that reproduces this case, pls?
you can attach it here or send directly to support@ for keeping privacy

I’m not sure what you mean by test case - I’m just using the example httpapi_security.zip above. I’ve just downloaded it, run it, then copied the swagger json it produces into Postman so I can call the endpoints.

Login works and returns 200 and I see the new session in the memo control. All three remaining calls return 401.

If I breakpoint both Login and Logoff functions in the login service, again login works fine but logoff doesn’t even get to the service function.

I feel like I might be missing a step here, like I need to supply some kind of client/session ID in the subsequent calls?

yes, you have to provide Access-Token that you have received in login:

login

C:>curl -verbose -X POST “http://localhost:8099/api/login/login” -H “accept: application/json” -H “content-type: application/json” -d “{ "NewParam": "string", "NewParam1": "string"}”

Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8099 (#0)
> POST /api/login/login HTTP/1.1
> Host: localhost:8099
> User-Agent: curl/7.56.0
> Referer: rbose
> accept: application/json
> content-type: application/json
> Content-Length: 48
>
* upload completely sent off: 48 out of 48 bytes
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: application/json; charset=utf-8
< Content-Length: 0
< Date: Fri, 24 Apr 2020 09:44:44 GMT
< Accept-Encoding: gzip, identity
< Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}
<
* Closing connection 0
sum

C:>curl -verbose -X POST “http://localhost:8099/api/test/Sum” -H “accept: application/json” -H “Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}” -H “content-type: application/json” -d “{ "A": 1, "B": 2}”

Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8099 (#0)
> POST /api/test/Sum HTTP/1.1
> Host: localhost:8099
> User-Agent: curl/7.56.0
> Referer: rbose
> accept: application/json
> Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}
> content-type: application/json
> Content-Length: 19
>
* upload completely sent off: 19 out of 19 bytes
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: application/json; charset=utf-8
< Content-Length: 1
< Date: Fri, 24 Apr 2020 09:45:06 GMT
< Accept-Encoding: gzip, identity
< Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}
<
3
* Closing connection 0
getdate

C:>curl -verbose -X POST “http://localhost:8099/api/test/getdate” -H “accept: application/json” -H “Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}”

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8099 (#0)
> POST /api/test/getdate HTTP/1.1
> Host: localhost:8099
> User-Agent: curl/7.56.0
> Referer: rbose
> accept: application/json
> Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}
>
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: application/json; charset=utf-8
< Content-Length: 26
< Date: Fri, 24 Apr 2020 09:45:49 GMT
< Accept-Encoding: gzip, identity
< Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}
<
"2020-04-24T09:45:49.774Z"
* Closing connection 0
logoff

C:>curl -verbose -X POST “http://localhost:8099/api/login/logoff” -H “accept: application/json” -H “Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}”

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8099 (#0)
> POST /api/login/logoff HTTP/1.1
> Host: localhost:8099
> User-Agent: curl/7.56.0
> Referer: rbose
> accept: application/json
> Access-Token: {3099F79B-4550-47F4-883E-82B8B275243F}
>
< HTTP/1.1 200 OK
< Connection: close
< Content-Type: application/json; charset=utf-8
< Content-Length: 0
< Date: Fri, 24 Apr 2020 09:46:24 GMT
< Accept-Encoding: gzip, identity
<
* Closing connection 0

Bingo, that works, many thanks.

Just tried implementing this in my own app but having problems with the Logoff.

Logon works fine and establishes a session but, if I then add this to an Access-Token head in my Logoff call, it doesn’t make it through to the function. Inspecting ClientID in the Logoff shows a different GUID each time it’s called.

Do I need to do something else to get the value of the Access-Token header to be used correctly?

Hi,

what language you are using for non-RO HttpApi clients?
can you show your code, i.e. setup and call logoff method?

Sorry I found it - I didn’t have the auth manager component hooked up properly. All works now :slight_smile: