Does anyone know how to manipulate the HTTP Status text in Scala Play 2.2?
I see that it's easy to specify the status code but not the accompanying text.
The reason I'm interested is that I'm trying to emulate exactly a web service I need to consume, and it puts specific information in the status text.
For example, when failing a login attempt I'll get the following snippet from this service (curl output):
< HTTP/1.1 401 username or password invalid
< ...
When I return an Unauthorized response from my Mock service I just get the following:
< HTTP/1.1 401 Unauthorized
< ...
I'm clearly missing the real way to do this if it's even possible in the first place.
Here's how I'm constructing the unauthorized response:
Unauthorized(views.html.invalidlogon(message)).withHeaders(
CONTENT_TYPE -> "text/plain"
)
Here's what I'd like to do in my fictional naive world:
Unauthorized(views.html.invalidlogon(message)).withHeaders(
CONTENT_TYPE -> "text/plain"
).setStatusText(message)
Thanks for the help!
Edit - Additional Info
So it turns out what I'm really looking for is the Reason Phrase.
According to the RFC they say the following:
The reason phrases listed here are only recommendations -- they MAY be replaced by local equivalents without affecting the protocol.
Of particular interest is the use of MAY with regard to the existing error codes.
However if in Play I return a custom 4XX error then the reason phrase is just Client Error following the classification of the 4XX status. It would be nice to have control over the reason phrase so that it accompanies the custom response status code.
De facto response status is a numeric value by HTTP specification and therefore it's hardcoded in Play.
If you really need this so as suggested in answer to other question it's better to add custom header, ie:
Unauthorized("You can't login now, sorry...").withHeaders(
CONTENT_TYPE -> "text/plain; charset=utf-8",
"X-Error-Message" -> "Login or password invalid"
)
TIP: For security reasons be careful with too descriptive error messages during login proccess.
After review of the Play code I don't think this is possible at the moment.
If you look here they do the following:
def createNettyResponse(header: ResponseHeader, closeConnection: Boolean, httpVersion: HttpVersion) = {
val nettyResponse = new DefaultHttpResponse(httpVersion, HttpResponseStatus.valueOf(header.status))
...
The call to HttpResponseStatus.valueOf(header.status) doesn't allow for Reason Phrase to be added.
In a purely fictional (and possibly dubious) world the following change might allow this:
def createNettyResponse(header: ResponseHeader, closeConnection: Boolean, httpVersion: HttpVersion) = {
val nettyResponse = header.reasonPhrase match {
case Some(reasonPhrase) => new DefaultHttpResponse(httpVersion, HttpResponseStatus(header.status, header.reasonPhrase))
case _ => new DefaultHttpResponse(httpVersion, HttpResponseStatus.valueOf(header.status))
}
...
However a change like that has a big ripple effect.
So If I'm correct, this is just not possible and I will see what the Play folks think.
thanks
Related
I have learned you can "decorate" the HTTP transport so that you can details of the request, however I can't quite figure out how you log the URL in the same line.
https://play.golang.org/p/g-ypQN9ceGa
results in
INFO[0000] Client request dns_start_ms=0 first_byte_ms=590 response_code=200 total_ms=590 url=
INFO[0000] 200
I'm perpetually confused if I should be using https://golang.org/pkg/context/#WithValue to pass around the context in a struct, especially in light where https://blog.golang.org/context-and-structs concludes with pass context.Context in as an argument.
Go through the behaviour of how the request is constructed in request.go from net/http. You see that the RequestURI field is never set there. Quoting from same reference,
Usually the URL field should be used instead.
It is an error to set this field in an HTTP client request
So, I would suggest you to use request.URL instead.
It is a parsed from the request uri. It should have the data you need.
You can construct the output as following:
f := log.Fields{
"url": fmt.Sprintf("%s %s%s", r.Method, r.URL.Host, r.URL.Path),
}
Also, in my experience, it is far more easier to use context.WithValue and pass the context as an argument.
Replace r.RequestURI by r.URL.String() in your code to log the full, valid URL (https://golang.org/pkg/net/url/#URL.String). RequestURI is empty on the client side (https://golang.org/pkg/net/http/#Request), as the output from your code is showing.
I don't see how context.Context relates to your question, but I believe https://blog.golang.org/context-and-structs is considered "best practice".
I am writing a new endpoint in my HTTP service that is built on the Play framework, and am returning a custom status code for a particular error (442 in this case). When I test the endpoint with cURL, I receive the error code as expected:
...
< HTTP/1.1 442 Client Error (442)
....
And the same with Postman REST Client:
What I would like to do is customize the "Client Error" text, such that the response would actually read something like:
442 Forced Password Reset
Is this possible, or is it in the spec somewhere that any custom status codes of the 4xx class are to be interpreted by all clients simply as "Client Error"?
(I have been looking through the relevant Play documentation on Statuses but don't see any option to customize the text—only the status code integer itself.)
Looking through the source code it didn't take me long to find the following:
Play just stores the status code in the RepsonseHeader, not the string
Play uses Netty, and turns the status code into a HttpResponseStatus using valueOf, which is basically just a case statement over the status codes, with defaults based on the range if it isn't standard.
Although you could define a new Netty HttpResponseStatus with the reason phrase you want, there isn't any way to add it to the valueOf method (it's static).
So, there isn't any really good solution without rewriting some parts of Play!.
You might be able to use some sort of post-filter to modify the response and change the reason phrase, but I don't know how that would work, or even if it is possible to write those kinds of filters in Play!.
Finally, the reason phrase isn't really that important, as clients generally don't (and shouldn't) actually parse it.
I know that in most MVC frameworks, for example, both query string params and form params will be made available to the processing code, and usually merged into one set of params (often with POST taking precedence). However, is it a valid thing to do according to the HTTP specification? Say you were to POST to:
http://1.2.3.4/MyApplication/Books?bookCode=1234
... and submit some update like a change to the book name whose book code is 1234, you'd be wanting the processing code to take both the bookCode query string param into account, and the POSTed form params with the updated book information. Is this valid, and is it a good idea?
Is it valid according HTTP specifications ?
Yes.
Here is the general syntax of URL as defined in those specs
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
There is no additional constraints on the form of the http_URL. In particular, the http method (i.e. POST,GET,PUT,HEAD,...) used don't add any restriction on the http URL format.
When using the GET method : the server can consider that the request body is empty.
When using the POST method : the server must handle the request body.
Is it a good idea ?
It depends what you need to do. I suggest you this link explaining the ideas behind GET and POST.
I can think that in some situation it can be handy to always have some parameters like the user language in the query part of the url.
I know that in most MVC frameworks, for example, both query string params and form params will be made available to the processing code, and usually merged into one set of params (often with POST taking precedence).
Any competent framework should support this.
Is this valid
Yes. The POST method in HTTP does not impose any restrictions on the URI used.
is it a good idea?
Obviously not, if the framework you are going to use is still clue-challenged. Otherwise, it depends on what you want to accomplish. The major use case (redirection of a data subset to a new POST target) has been irretrievably broken by browser implementations (all mechanically following the broken lead of Mosaic/Netscape), so the considerations here are mostly theoretical.
Let's say you want to get list of users by calling GET to api/users, but currently the table was truncated so there are no users. What is the proper response for this scenario: 404 or 204?
I'd say, neither.
Why not 404 (Not Found) ?
The 404 status code should be reserved for situations, in which a resource is not found. In this case, your resource is a collection of users. This collection exists but it's currently empty. Personally, I'd be very confused as an author of a client for your application if I got a 200 one day and a 404 the next day just because someone happened to remove a couple of users. What am I supposed to do? Is my URL wrong? Did someone change the API and neglect to leave a redirection.
Why not 204 (No Content) ?
Here's an excerpt from the description of the 204 status code by w3c
The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation.
While this may seem reasonable in this case, I think it would also confuse clients. A 204 is supposed to indicate that some operation was executed successfully and no data needs to be returned. This is perfect as a response to a DELETE request or perhaps firing some script that does not need to return data. In case of api/users, you usually expect to receive a representation of your collection of users. Sending a response body one time and not sending it the other time is inconsistent and potentially misleading.
Why I'd use a 200 (OK)
For reasons mentioned above (consistency), I would return a representation of an empty collection. Let's assume you're using XML. A normal response body for a non-empty collection of users could look like this:
<users>
<user>
<id>1</id>
<name>Tom</name>
</user>
<user>
<id>2</id>
<name>IMB</name>
</user>
</users>
and if the list is empty, you could just respond with something like this (while still using a 200):
<users/>
Either way, a client receives a response body that follows a certain, well-known format. There's no unnecessary confusion and status code checking. Also, no status code definition is violated. Everybody's happy.
You can do the same with JSON or HTML or whatever format you're using.
I'd answer one of two codes depending on runtime situation:
404 (Not Found)
This answer is pretty correct if you have no table. Not just empty table but NO USER TABLE. It confirms exact idea - no resource. Further options are to provide more details WHY your table is absent, there is couple of more detailed codes but 404 is pretty good to refer to situation where you really have no table.
200 (OK)
All cases where you have table but it is empty or your request processor filtered out all results. This means 'your request is correct, everything is OK but you do not match any data just because either we have no data or we have no data which matches your request. This should be different from security denial answer. I also vote to return 200 in situation where you have some data and in general you are allowed to access table but have no access to all data which match your request (data was filtered out because of object level security but in general you are allowed to request).
If you are expecting list of user object, the best solution is returning an empty list ([]) with 200 OK than using a 404 or a 204 response.
definitely returns 200.
404 means resource not found. But the resource exists. And also, if the response has 404 status. How can you know users list empty or filled?
'/users' if is empty should return '200'.
'/users/1' if the id is not found. should return 404.
It must 200 OK with empty list.
Why: Empty table means the table exists but does not have any records.
404 Not Found means requested end point does not exist.
I need to invoke a process which doesn't require any input from the user, just a trigger. I plan to use POST /uri without a body to trigger the process. I want to know if this is considered bad from both HTTP and REST perspectives?
I asked this question on the IETF HTTP working group a few months ago. The short answer is: NO, it's not a bad practice (but I suggest reading the thread for more details).
Using a POST instead of a GET is perfectly reasonable, since it also instructs the server (and gateways along the way) not to return a cached response.
POST is completely OK. In difference of GET with POST you are changing the state of the system (most likely your trigger is "doing" something and changing data).
I used POST already without payload and it "feels" OK. One thing you should do when using POST without payload: Pass header Content-Length: 0. I remember problems with some proxies when I api-client didn't pass it.
If you use POST /uri without a body it is something like using a function which does not take an argument .e.g int post (void); so it is reasonable to have function to your resource class which can change the state of an object without having an argument. If you consider to implement the Unix touch function for a URI, is not it be good choice?
Yes, it's OK to send a POST request without a body and instead use query string parameters. But be careful if your parameters contain characters that are not HTTP valid you will have to encode them.
For example if you need to POST 'hello world' to and end point you would have to make it look like this: http://api.com?param=hello%20world
Support for the answers that POST is OK in this case is that in Python's case, the OpenAPI framework "FastAPI" generates a Swagger GUI (see image) that doesn't contain a Body section when a method (see example below) doesn't have a parameter to accept a body.
the method "post_disable_db" just accepts a path parameter "db_name" and doesn't have a 2nd parameter which would imply a mandatory body.
#router.post('/{db_name}/disable',
status_code=HTTP_200_OK,
response_model=ResponseSuccess,
summary='',
description=''
)
async def post_disable_db(db_name: str):
try:
response: ResponseSuccess = Handlers.databases_handler.post_change_db_enabled_state(db_name, False)
except HTTPException as e:
raise (e)
except Exception as e:
logger.exception(f'Changing state of DB to enabled=False failed due to: {e.__repr__()}')
raise HTTPException(HTTP_500_INTERNAL_SERVER_ERROR, detail=e.__repr__())
return response