My server is built on top of Akka HTTP. If I don't set the Server header, the externally configurable default of akka-http/10.1.8 will be automatically added by Akka. I know how to override that with my own server by adding the respondWithHeaders directive around my entire routs tree:
respondWithHeaders(Server(myProductVersion)) {
// my routs here
}
This works as expected; the Server response header now reads my product. What I want though is to include akka's header as well, as I like it and don't mind telling the world about my server stack. Given the signature of the Server.apply method, I should be able to do that like so:
respondWithHeaders(Server(myProductVersion, akkaProductVersion)) {
// my routs here
}
... my problem is that I can't figure out how to get at that akkaProductVersion object!
Try reading akka.http.version configuration property like so
system.settings.config.getString("akka.http.version")
so you could try
Server(
myProductVersion,
system.settings.config.getString("akka.http.version")
)
According to default configuration
# The default value of the `Server` header to produce if no
# explicit `Server`-header was included in a response.
# If this value is the empty string and no header was included in
# the request, no `Server` header will be rendered at all.
server-header = akka-http/${akka.http.version}
We can see how akka-http reads server-header when constructing ServerSettings here:
c.getString("server-header").toOption.map(Server(_)),
Related
We have a FastApi application that is hosted behind a reverse proxy.
The proxy authenticates the user using Kerberos and adds a X-Remote-User HTTP header to the request.
This header is required by the FastApi application. Here is an example route:
#app.get("/user/me")
async def get_user_me(x_remote_user: str = Header(...)):
return {"User": x_remote_user}
The X-Remote-User header is required for the request which is expected behavior.
When we now open the Swagger Ui, the header is documented and when clicking on "Try it out", we can provide the header value.
This behavior is great for development, but in all other cases it is undesired, because that header is provided by the reverse proxy. For instance, we generate clients using OpenAPI Generator and the clients then all require the X-Remote-User parameter in their requests.
Hence, it would be useful to have a configuration that distinguishes between the environments. If we are behind a reverse proxy, then the generated OpenAPI Schema by FastApi should not include the X-Remote-Header, otherwise if we are in development, it should be included.
What I did so far:
I checked the documentation about security and also some source code of these modules, but I was not able to find a solution.
In the documentation, I read the section Behind a Proxy, but nothing there points me to a potential solution.
I also read about Middleware, but again, no solution.
We could change the generated OpenApi schema. I sketched this in my answer below, but this is not a very elegant solution
Does anyone have a good solution to this problem?
We can use APIKeyHeader to remove the X-Remote-User header from the API signature, but still enforcing the header to be present.
from fastapi.security import APIKeyHeader
apiKey = APIKeyHeader(name="X-Remote-User")
#app.get("/user/me")
async def get_user_me(x_remote_user: str = Depends(apiKey)):
return {"User": x_remote_user}
When the header is not present, we get a "403 Forbidden". If it is present, we retrieve the header value.
The Swagger UI now has a button "Authorize" where we can fill-in the value of the X-Remote-User for testing purposes.
One approach is to generate the OpenApi schema as described in the documentation Extending OpenAPI. After the generation, remove the X-Remote-User from the schema. In the configuration could be a flag that the application it is behind a reverse proxy to execute the code conditionally:
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from MyConfig import Config
app = FastAPI()
#app.get("/items/")
async def read_items():
return [{"name": "Foo"}]
if Config.reverse_proxy:
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Custom title",
version="2.5.0",
description="This is a very custom OpenAPI schema",
routes=app.routes,
)
// remove X-Remote-User here
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
However this is not a very elegant solution, as we need to parse the Json string and remove the different deeply-nested occurrences of the X-Remote-User header everywhere. This is prone to bugs resulting in an invalid schema. Furthermore it could break if new Rest endpoints are added.
A new param will be soon available for Header, Query and other to exclude elements from the openAPI output: include_in_schema=False
Example:
def test(x_forwarded_for: str = Header(None, include_in_schema=False)):
...
Here the patch state: https://github.com/tiangolo/fastapi/pull/3144
I have tried to create a record of my customized object through REST service in IBM Maximo.
The problem is that I created the record but I can't assign values to the attributes.
Next I will show what I did and what happened:
I have an Object Structure called oxidato that represents my customized object.
I did a POST using POSTMAN to this URL:
http://hostname:port/maximo/oslc/os/oxidato?lean=1
In the body section this is the JSON I was trying to send:
{
"attribute1":"205",
"attribute2":"206"
}
The record was created but none of the attributes was filled.
In my opinion, the REST service received the POST but canĀ“t read the body.
What am I missing? I add an image of the POSTMAN as example:
EDIT1: I update the POST in order to use the newest API RES (Thanks Dex!)
EDIT2: I add an image of the header
I have found that Maximo will often ignore incoming attributes that aren't in the Maximo namespace (http://www.ibm.com/maximo). You could go through the trouble of setting up your VALOR1 and VALOR2 attributes to be in that namespace, but it's easier to just tell OSLC to ignore namespaces. You do that by setting the "lean" parameter to "1".
In your case, go to the "Params" tab and add an entry with a name of "lean". Give it a value of "1" and then send your POST again. You should see "?lean=1" appear at the end of the POST URL along the top there, but your body content should remain unchanged.
EDIT:
On the other hand, it looks like (based on your URL) that you aren't actually using the newer JSON/OSLC REST API; It looks like you are using the older REST services. This IBM page gives you a lot of information on the newer JSON REST API, including the correct URLs for it: https://developer.ibm.com/static/site-id/155/maximodev/restguide/Maximo_Nextgen_REST_API.html.
You should change your URL to /maximo/oslc/os/oxidato to use the newer API that naturally supports JSON and the lean parameter described above. This does required Maximo 7.6 to use though.
EDIT 2:
The attributes are often oddly case sensitive, requiring lowercase. Your example in your question of "attribute1" and "attribute2" are properly lowercase, but your screenshot shows uppercase attribute names. Try changing them to "valor1" and "valor2". Also, these are persistent attributes, right?
The response code received back (e.g. 200 - OK) and the response body will detail the record that was created.
I think you are correct in that the body of the post request is being ignored. Provided there are no required fields on the custom MBO your POST is probably creating an empty record with the next value in the sequence for the key field but you should see that in the response.
The following POST should create a record with values provided for attribute1 and attribute2 and provide a response with the record's identifier so that you can look it up in Maximo and show the values that were stored for attribute1 and attribute2:
http://hostname:port/maximo/rest/os/oxidato/?_format=json&_compact=1&attribute1=205&attribute2=206
Response: 200 OK
Reponse Body:
{ "CreateOXIDATOResponse": {
"rsStart": 0,
"rsCount": 1,
"rsTotal": 1,
"OXIDATOSet": {
"OXIDATO": {
"rowstamp": "[0 0 0 0 0 -43 127 13]",
"ATTRIBUTE1": "205",
"ATTRIBUTE2": "206",
"OXIDATOID": 13
}
} } }
You may also want to turn on debug logging for the REST interface in System Configuration -> Platform Configuration -> Logging for additional detail on what's happening in the log file.
I have setup my first REST API and I am new to using the Taffy framework.
I have a site which is working on ColdFusion 10, IIS and using ColdBox. I have setup a hello world example in a directory. I am getting // two slashes in the response. Here is an example of the response:
//["hello","world"]
My hello.cfc looks like this:
component extends="taffy.core.resource" taffy_uri="/hello" {
function get(){
return representationOf(['hello','world']);
}
}
My application.cfc looks like this:
<cfcomponent extends="taffy.core.api">
<cfscript>
this.name = hash(getCurrentTemplatePath());
this.mappings["/resources"] = listDeleteAt(cgi.script_name, listLen(cgi.script_name, "/"), "/") & "/resources";
variables.framework = {};
variables.framework.reloadKey = "reload";
variables.framework.reloadPassword = "test";
variables.framework.serializer = "taffy.core.nativeJsonSerializer";
variables.framework.returnExceptionsAsJson = true;
function onApplicationStart(){
return super.onApplicationStart();
}
function onRequestStart(TARGETPATH){
// reload app to make any envoirnmental changes
if(structkeyexists(url,'reloadApp')){
applicationStop();
location('index.cfm');
}
// load Taffy onRequestStart before our stuff
super.onRequestStart();
if (request.taffyReloaded) {
// reload ORM as well
ORMReload();
}
}
function onTaffyRequest(verb, cfc, requestArguments, mimeExt){
return true;
}
function onError(Exception)
{
writeDump(Exception);
abort;
}
</cfscript>
</cfcomponent>
Can anyone tell me where I am going wrong?
Does this have something to do with using ColdBox?
That is coming from a server side setting in the ColdFusion admin, under settings. Prefix serialized JSON with. Beginning with ColdFusion 10 it is enabled by default for security. (I believe the feature was added with ColdFusion 9.) Protects web services, which return JSON data from cross-site scripting attacks by prefixing serialized JSON strings with a custom prefix. You could turn it off there but I do not recommend that. Instead you should handle it with your code.
See this post from Raymond Camden - Handling JSON with prefixes in jQuery and jQueryUI
NOTE: this setting can also be set per-application by setting secureJSON and secureJSONPrefix in your Application.cfc file. See the documentation about that here - Application variables.
secureJSON - A Boolean value that specifies whether to add a security prefix in front of the value that a ColdFusion function returns in JSON-format in response to a remote call.
The default value is the value of the Prefix serialized JSON setting in the Administrator Server Settings > Settings page (which defaults to false). You can override this value in the cffunction tag.
secureJSONPrefix - The security prefix to put in front of the value that a ColdFusion function returns in JSON-format in response to a remote call if the secureJSON setting is true.
The default value is the value of the Prefix serialized JSON setting in the Administrator Server Settings > Settings page (which defaults to //, the JavaScript comment character).
I have http requests such as the one below being sent to an nginx server:
GET /app/handler?id=1234¶m1=cbd¶m2=234
Now, I want to rewrite the request to a different handler depending on the id param in the request. eg. redirect to handler_even for even ids and handler_odd for odd ids. This is shown below:
GET /app/handler?id=1234¶m1=cbd¶m2=234 => /app/handler_even?id=1234¶m1=cbd¶m2=234
GET /app/handler?id=123¶m1=cbd¶m2=234 => /app/handler_odd?id=123¶m1=cbd¶m2=234
I can do the rewrite using proxy_pass, but I'm unsure how to redirect using the id parameter value. Any idea how I could go about this? Would using "if" be the best way to go about this?
Any pointers would be useful
Rather than use an if directive, you could use a map. To internally rewrite the URI use:
map $arg_id $handler {
default /app/handler_even;
~[13579]$ /app/handler_odd;
}
server {
...
location = /app/handler {
rewrite ^ $handler last;
}
...
}
The map should be located at the same level as your server directive (as shown above), i.e. within the http container.
See this document for details.
I am following the directions in the docs, here:
http://grails.org/doc/2.3.8/guide/webServices.html#hypermedia
Why won't grails produce HAL-formatted output, as shown in the documentation?
I have a domain object which I have mapped with the #Resource annotation:
#Resource(uri='/documentCatalogs', formats = ['json', 'xml'], readOnly = true)
class DocumentCatalog {
String entityType
String actionCode
...
}
...and in my conf/spring/resources.groovy, I have configured the HAL JSON renderer beans:
import com.cscinfo.platform.api.formslibrary.DocumentCatalog
import grails.rest.render.hal.HalJsonCollectionRenderer
import grails.rest.render.hal.HalJsonRenderer
// Place your Spring DSL code here
beans = {
halDocumentCatalogRenderer(HalJsonRenderer, DocumentCatalog)
halDocumentCatalogCollectionRenderer(HalJsonCollectionRenderer, DocumentCatalog)
}
Using the debugger, I confirmed that the initialize() method on HalJsonRenderer is called and that it is constructed with the correct targetType.
I send a rest call using Postman:
http://localhost:8080/formslibrary/documentCatalogs/3
Accept application/hal+json
And I get back a response which is regular JSON and doesn't contain any links:
{
"class": "com.cscinfo.platform.api.formslibrary.DocumentCatalog",
"id": 3,
"actionCode": "WITH",
"entityType": "LLP",
...
}
What did I miss? Is there some plugin or configuration setting I have to enable for this behavior? Is there some additional mapping property somewhere that's not documented?
Figured it out! There are multiple aspects of the fix...
I had to add "hal" as one of the listed formats in the #Resource annotation:
#Resource(uri='/documentCatalogs', formats = ['json', 'xml', 'hal'])
Some hunting around in the debugger revealed that Grails will blithely ignore the Accept header, based on the UserAgent string that is sent from the client. (In my case, since I'm using Postman, it was the Google Chrome UA string.)
One workaround for the Accept header issue is to add ".hal" to the end of the URL:
http://localhost:8080/formslibrary/documentCatalogs/3.hal
This isn't a very good solution IMO, since the HAL URLs generated by the renderer don't end in ".hal" by default.
A better solution is to fix Grails' handling of the accept header by updating the config. In Config.groovy, you will see a line that says:
grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
Change it to:
grails.mime.disable.accept.header.userAgents = ['None']
This forces Grails to honor the Accept header, regardless of the user agent.
Hope this helps somebody else who's hitting the same issue.
P.S. It's really helpful to put a breakpoint in the ResponseMimeTypesApi#getMimeTypesFormatAware(...) method.