Question
· Jan 13, 2021

How to set HTTP Response Status Code in httpResponse object

Hi all,

I have a passthru REST setup using EnsLib.HTTP.GenericService and EnsLib.HTTP.GenericOperation. Works well.

I am adding some logic to the Operation and for some scenarios I want to return an HTTP 40x response status code.

I create an httpResponse object within the Business Operation, but I can't figure out how to set the http response status code that is returned to my Posstman client that initiated the request. I set StatusCode=400, and StatusLine and ReasonPhrase properties - but when the ultimate requestor (Postman) receives the response back it is a 200 status code. I am quitting the OnMessage method with status of $$$OK - which I suspect is being interpreted as HTTP response status code of 200. But I thought the httpResponse.StatusCode property setting of 400 would override that (?)

So how do I control the status code of the response? Can that be done within the httpResponse object within the Business Operation? Or do I need to subclass the related Business Service?

Product version: IRIS 2020.4
Discussion (10)3
Log in or sign up to continue

Hi Mike,

I have been having the same problem and could only ever return a 200 or 500 status code from my REST Business Service, so I was very disheartened to see your post that WRC said you have to CSP layer to get round this.

However, not one to give up easily, I believe I have found a solution:

I am using the EnsLib.REST.GenericServices business service class which uses the EnsLib.HTTP.InboundAdapter  adaptor class.

It appears that the EnsLib.HTTP.InboundAdapter class uses the second and remaining pieces, separated by " ", of the StatusLine header value for the returned status code.

Therefore, in my Business Process code, that returns  EnsLib.HTTP.GenericMessage back to the Business Service, I used the following lines, in the appropriate places of my code:

Set tHttpResponse = ##class(%Net.HttpResponse).%New()

Set tHttpResponse.StatusLine = "HTTP/1.1 404 Not Known"

Set response = ##class(EnsLib.HTTP.GenericMessage).%New(tResponseStream,,tHttpResponse)

Do response.HTTPHeaders.SetAt("HTTP/1.1 400 Bad Request","STATUSLINE")

Do response.HTTPHeaders.SetAt(tHttpResponse.StatusLine,"STATUSLINE")

Hope this helps.

Hi Neil,

My code was pretty much identical to what you shared above, except for the following:

Do response.HTTPHeaders.SetAt("HTTP/1.1 400 Bad Request","STATUSLINE")

I added that, but it didn't change anything.

The only other difference is that I am doing all of this in the Business Operation in that I only have and Business Service and Business Operation (no Business Process). Not sure if that would make a difference, but I'm going to put in a Business Operation and apply these changes and see if that works.

Thanks again for the investigation and idea - I'll let you know if inserting a Business Process and the related code works for me.

Hi Mike,

I am using my own custom Business Process which creates the HTTP Response.

I am also using a Business Services, rather than a Business Operation, like you, but I'm sure the principle will be much the same.

My Business Services uses the passthrough 'EnsLib.REST.GenericServices' business service class, which passes the HTTP Request to my custom process.

The Business Process then processes the request and creates the appropriate HTTP Response which is passed back to Business Service.

Just in case someone stumbles into this thread looking for an answer (as I did) ...

Assuming the vanilla, un-extended EnsLib.HTTP.GenericService is the service handling the request, any response it receives from a business process needs to be an EnsLib.HTTP.GenericMessage. %Net.HttpResponse is not needed, nor is a CSP layer required.

The service requires that a stream is attached to the message; the stream doesn't need to contain anything.

The response message is composed in the BP something like this:

Set rstream = ##class(%Stream.GlobalCharacter).%New()
// Optional body content
Do rstream.Write("<HTML><HEAD>Uh oh.</HEAD><BODY><BR><STRONG>Error: Invalid Patient ID</STRONG></BODY></HTML>")
// Provide a stream object, empty is fine
Set response = ##class(EnsLib.HTTP.GenericMessage).%New(rstream)
// This works as expected in I4H 2023.1
Do response.HTTPHeaders.SetAt("HTTP/1.1 400 Bad Request","StatusLine")
// if you're providing a payload ...
Do response.HTTPHeaders.SetAt("text/html; charset=utf-8","Content-Type")

I've verified that it works as coded above, using curl:

< HTTP/1.1 400 Bad Request
< Content-Type: text/html; charset=utf-8
< Content-Length: 99
<
* Connection #0 to host iristest.local left intact
<HTML><HEAD>Uh oh.</HEAD><BODY><BR><STRONG>Error: Invalid Patient ID</STRONG></BODY></HTML>

The reason this 2-year-old thread floated to the top is because I found it researching an issue I had encountered with EnsLib.HTTP.GenericService.. I wanted to "pass through" a status code I had set as a property of a response message in a business process. While a solution was buried in the thread, the OP claimed it did not work in his case. I'm not sure why; I used a slight variation of that solution with success and simply felt the variation was worth sharing.

Hi, it's been quite a while since this question but yesterday I had the same problem and I'd like to share how I managed to solve it.

First of all, I have created a simple unauthenticated Web Application where the dispatch class it the EnsLib.REST.GenericService in order to user the simpler and cleaner architecture.

Then, after processing the HTTP request, I generate the HTTP response through a Business Process where I have declared the following parameters:

Parameter CONTENTTYPEJSON As STRING = "application/json";
Parameter HTTP200OK As STRING = "HTTP/1.1 200 OK";
Parameter HTTP202ACCEPTED As STRING = "HTTP/1.1 202 Accepted";
Parameter HTTP400BADREQUEST As STRING = "HTTP/1.1 400 Bad Request";
Parameter HTTP500INTERNALSERVERERROR As STRING = "HTTP/1.1 500 Internal Server Error";

It is important that the parameters related to the status line starts with "HTTP/1.1", since it is this statement that enable the return of the correct HTTP status.

Finally, I generate the response as shown:

Set ContentType = ..#CONTENTTYPEJSON
Set StatusLine = ..#HTTP500INTERNALSERVERERROR
Set HTTPStatus = 500

; Format the json with the correct indentation
Set jsonFormatter = ##class(%JSON.Formatter).%New()
Set pStream = ##class(%Stream.GlobalCharacter).%New()
Set sc = jsonFormatter.FormatToStream(json, .pStream)

; Generate an HTTP response message
Set pResponse = ##class(EnsLib.HTTP.GenericMessage).%New(pStream)

; Add headers to the HTTP response
Set sc = pResponse.HTTPHeaders.SetAt(StatusLine,"StatusLine")
Set sc = pResponse.HTTPHeaders.SetAt(HTTPStatus,"StatusCode")
Set sc = pResponse.HTTPHeaders.SetAt(ContentType, "Content-Type")

; I tested it with the following HTTP codes either and it works: 
;   202 / ..#HTTP202ACCEPTED
;   200 / ..#HTTP200OK
;   etc...

I tested with HTTP statuses 200, 202 and 500, but I'm pretty sure it will work with other statuses as well.