Add HTTP status code return on APIs with OpenEdge
Figure: from the author

Add HTTP status code return on APIs with OpenEdge

In the previous article, I talked about API pagination with OpenEdge on PASOE. Today, I'll use that same API to add HTTP status code in the return. If you didn't read the previous article, I strongly encourage you to do so, then we'll be in the same page here.

Sections of this article:

  1. Main HTTP status code
  2. Code (the .p file)
  3. Mapping the?.p?as an endpoint (retVal)
  4. Testing the endpoint on PASOE


1. Main HTTP status code

Here I'll pass briefly through the main codes used, note that this is not a complete list! And thanks to ChatGPT, here goes a summary:

2xx Success:

  • 200 OK: The request was successful, and the response contains the requested information.
  • 201 Created: The request was successful, and a new resource was created as a result.
  • 204 No Content: The request was successful, but there is no content to send back.

3xx Redirection:

  • 301 Moved Permanently: The requested resource has been permanently moved to a new location.
  • 302 Found: The requested resource has been temporarily moved to a different location.
  • 304 Not Modified: The resource has not been modified since the last request.

4xx Client Errors:

  • 400 Bad Request: The server cannot process the request due to a client error, such as malformed syntax.
  • 401 Unauthorized: Authentication is required to access the requested resource.
  • 403 Forbidden: The server understood the request, but the client does not have permission to access the resource.
  • 404 Not Found: The requested resource could not be found on the server.

5xx Server Errors:

  • 500 Internal Server Error: A generic error message indicating that something unexpected went wrong on the server.
  • 502 Bad Gateway: The server acting as a gateway or proxy received an invalid response from an upstream server.
  • 503 Service Unavailable: The server is temporarily unable to handle the request due to maintenance or overload.
  • 504 Gateway Timeout: The server acting as a gateway or proxy did not receive a timely response from an upstream server.
  • 505 HTTP Version Not Supported: The server does not support the HTTP protocol version used in the request.

To get details about it, I recommend you to take a look here.


2. Code (the .p file)

I duplicated the previous item.p into an item2.p file.

First, I changed the property useReturnValue to "true" in the REST main annotation. Setting it to true, allow us to use the retVal interface parameter to return our custom HTTP status code on the REST Resource URI Editor:

@openapi.openedge.export FILE(type="REST", executionMode="external", useReturnValue="true", writeDataSetBeforeImage="false")

/*------------------------------------------------------------------------
? ? File? ? ? ? : item2.p
? ? Purpose? ? ?: Improvement of item.p program, to use HTTP Status Code
? ? Syntax? ? ? :
? ? Description :?
? ? Author(s)? ?: Lucas Bicalho
? ? Created? ? ?: Sun Jul 09 12:55:18 BRT 2023
? ? Notes? ? ? ?: Added:
? ? ? ? ? ? ? ? ? ? - http status code
? ? ? ? ? ? ? ? ? ? - finnaly block?
? ? ? ? ? ? ? ? ? ? - qty of records to the target table (item)
? ----------------------------------------------------------------------*/.        

Then I added a few things:

  1. Imported the TableRowsCounter class – that I created to count the number of rows of a given table – with the USING statement;
  2. Defined the variable tableRowsCounter with my class type: AppServer.Utils.TableRowsCounter;
  3. Defined the cStatusCode variable to hold the different HTTP status code during the code execution;
  4. Added the output parameter iTotalRows, to return to the client the table total rows amount.

USING AppServer.Utils.TableRowsCounter.

BLOCK-LEVEL ON ERROR UNDO, THROW.

DEFINE TEMP-TABLE ttItem NO-UNDO
? ? FIELD ItemNum? ? LIKE Item.ItemNum?
? ? FIELD ItemName? ?LIKE Item.ItemName?
? ? FIELD Price? ? ? LIKE Item.Price?
? ? INDEX ItemNum IS PRIMARY IS UNIQUE ItemNum.
? ??
DEFINE DATA-SOURCE srcItem FOR Item.
DEFINE DATASET dsItem FOR ttItem.
DEFINE QUERY qItem FOR ttItem.

DEFINE VARIABLE tableRowsCounter AS AppServer.Utils.TableRowsCounter NO-UNDO.
DEFINE VARIABLE cStatusCode? ? ? AS CHARACTER NO-UNDO INIT "400".

DEFINE INPUT PARAM? ? ? ? ? ? ? iQty? ? ? ?AS INTEGER? ?NO-UNDO.
DEFINE INPUT-OUTPUT PARAM? ? ? ?pRowId? ? ?AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAM? ? ? ? ? ? ?iTotalRows AS INTEGER? ?NO-UNDO.
DEFINE OUTPUT PARAM? ? ? ? ? ? ?cStatus? ? AS CHARACTER NO-UNDO INIT "Unknown error!".
DEFINE OUTPUT PARAM DATASET FOR dsItem.
        

Note that the default code for cStatusCode is 400 (bad request), I'll update its value depending on the part of the code:

/* Initial validation */
IF iQty = ? THEN DO: // if null, the client didn't informed the param on url
? ? ASSIGN
? ? ? ? cStatus = "Quantity parameter must be informed!".
? ? RETURN cStatusCode. // bad request
END.
ELSE IF iQty <= 0 THEN DO:
? ? ASSIGN
? ? ? ? cStatus = "Quantity parameter must be greater than 0!".
? ? RETURN cStatusCode. // bad request
END.
ELSE IF iQty > 200 THEN
DO:
? ? ASSIGN
? ? ? ? cStatus = "Not alowed! Maximum of 200 records per request!".
? ? RETURN cStatusCode. // bad request
END.        

Then, the main logic is executed (nothing changed in this part from the original) and after all, I set the code 200, if nothing unexpected happened:

// ... main logic block ... //

ASSIGN
? ? tableRowsCounter = NEW TableRowsCounter()
? ? iTotalRows = tableRowsCounter:CountRows('sports2020.item')
? ? cStatus = "Success!"
? ? cStatusCode = "200".        

I'm planning to explain the tableRowsCounter() in a next article. But its function is just to count the amount of records of the item table in the sports2020 database.

So the CATCH error is added, with code 500 (internal server error):

CATCH err AS Progress.Lang.Error: 
? ? DEFINE VARIABLE iMessage AS INTEGER NO-UNDO.
? ? ASSIGN?
? ? ? ? cStatusCode = "500".
? ? ? ??
? ? DO WHILE iMessage < err:NumMessages:
? ? ? ? ASSIGN?
? ? ? ? ? ? cStatus = SUBSTITUTE ("&1~n&2", cStatus, err:GetMessage(iMessage))
? ? ? ? ? ? iMessage = iMessage + 1.
? ? END.
? ? IF err:CallStack <> ? THEN DO:
? ? ? ? ASSIGN?
? ? ? ? ? ? cStatus = SUBSTITUTE ("&1~n~nCall Stack:~n&2", cStatus, err:CallStack).
? ? END.
END CATCH.        

And at the end, on the FINALLY block, the current value of HTTP status held in the cStatusCode variable is returned:

FINALLY:
? ? RETURN cStatusCode.? ??
END FINALLY.        

To see the full code, click here.


3. Mapping the .p as an endpoint (retVal)

Here I added a new resource (item2), and picked the item2.p file on the GET verb. Then I linked the input parameters:

No alt text provided for this image
Link input parameter

Now, on the output tab, we see the retVal value. This interface parameter holds the cStatusCode variable value:

No alt text provided for this image

Ok, now that our API is mapped, the next step is testing on a PASOE local instance.



4. Testing on a PASOE local instance


Testing initial validation, the expected result is to return a 400 HTTP status code (bad request). See the three validations tested: i) to pass iQty parameter; ii) iQty parameter must be greater than 0; iii) iQty must be between 1 and 200.

No alt text provided for this image
Testing not passing the iQty parameter. Getting HTTP Status = 400.
No alt text provided for this image
Testing passing iQty = 0. Getting HTTP Status = 400.
No alt text provided for this image
Testing passing iQty = 300. Getting HTTP Status = 400.

Now, testing with: i) Passing iQty = 1; ii) Passing the pRowId to get the next page.

All of those returning the status code 200 (OK), as expected:

No alt text provided for this image
Passing iQty = 1. Getting HTTP Status = 200.
No alt text provided for this image

Now, testing CATCH error.

To raise an unexpected condition, I pass a pRowId that ditn't exist:

No alt text provided for this image
Passing inexistent pRowId. Getting HTTP Status = 500.

The result is HTTP status code = 500 (internal server error).

Great! All the expected results worked just fine. We see here an example of how to return custom HTTP status code on an API, depending on the conditions, validations, or anything you may consider on the API design stage.

And again, if you're interested in exploring the complete code, you can find it by?click here.


???? Aos meus colegas do Brasil, se for do seu interesse que eu traduza este artigo, por favor, mencione nos comentários que o farei com o maior prazer!

Hope to see you around!

要查看或添加评论,请登录

社区洞察

其他会员也浏览了