I send following headers in a request to my akka-http api: "Content-type": "application/json", "Accept": "application/json", "AppId": "some_id".
How do I get "AppId" custom header in my akka-http route?
(get & parameters("id")) { (id) =>
complete {
val appId = ?? // I want to get custom header here.
}
}
Thanks.
You need to use one of the HeaderDirectives (HeaderDirectives docs) to extract the header. For example, if it's a custom one you can use headerValueByName which yields the value of the header, and rejects the route if the header was not present (if the header is optional you can use optionalHeaderValueByName):
headerValueByName("AppId") { appId =>
complete(s"The AppId was: $appId")
}
Happy hakking!
I actually prefer creating custom directive for things like authentication tokens, app ids and other parameters that are sort of mandatory for serving client's request. In your case it might look like this
val extractAppId = (headerValueByName("AppId") | headerValueByName("AppId2")).tflatMap[Tuple1[String]] {
case Tuple1(appId) =>
if (!appId.equalsIgnoreCase("BannedAppId"))
provide(appId)
else
complete(StatusCodes.Forbidden -> "Your application is banned")
}.recover {
case rejections => reject(ValidationRejection("AppId is not provided"))
}
which is used like
extractAppId { appId =>
get {
complete {
"Your AppId is " + appId
}
}
}
To make my example more interesting I added support of conditional response based on provided AppId.
Related
I am trying to run a few automated testing using the Postman tool. For regular scenarios, I understand how to write pre-test and test scripts. What I do not know (and trying to understand) is, how to write scripts for checking 409 error (let us call it duplicate resource check).
I want to run a create resource api like below, then run it again and ensure that the 2nd invocation really returns 409 error.
POST /myservice/books
Is there a way to run the same api twice and check the return value for 2nd invocation. If yes, how do I do that. One crude way of achieving this could be to create a dependency between two tests, where the first one creates a resource, and the second one uses the same payload once again to create the same resource. I am looking for a single test to do an end-to-end testing.
Postman doesn't really provide a standard way, but is still flexible. I realized that we have to write javascript code in the pre-request tab, to do our own http request (using sendRequest method) and store the resulting data into env vars for use by the main api call.
Here is a sample:
var phone = pm.variables.replaceIn("{{$randomPhoneNumber}}");
console.log("phone:", phone)
var baseURL = pm.variables.replaceIn("{{ROG_SERVER}}:{{ROG_PORT}}{{ROG_BASE_URL}}")
var usersURL = pm.variables.replaceIn("{{ROG_SERVICE}}/users")
var otpURL = `${baseURL}/${phone}/_otp_x`
// Payload for partner creation
const payload = {
"name": pm.variables.replaceIn("{{username}}"),
"phone":phone,
"password": pm.variables.replaceIn("{{$randomPassword}}"),
}
console.log("user payload:", payload)
function getOTP (a, callback) {
// Get an OTP
pm.sendRequest(otpURL, function(err, response) {
if (err) throw err
var jsonDaata = response.json()
pm.expect(jsonDaata).to.haveOwnProperty('otp')
pm.environment.set("otp", jsonDaata.otp)
pm.environment.set("phone", phone);
pm.environment.set("username", "{{$randomUserName}}")
if (callback) callback(jsonDaata.otp)
})
}
// Get an OTP
getOTP("a", otp => {
console.log("OTP received:", otp)
payload.partnerRef = pm.variables.replaceIn("{{$randomPassword}}")
payload.otp = otp
//create a partner user with the otp.
let reqOpts = {
url: usersURL,
method: 'POST',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify(payload)
}
pm.sendRequest(reqOpts, (err, response) => {
console.log("response?", response)
pm.expect(response).to.have.property('code', 201)
})
// Get a new OTP for the main request to be executed.
getOTP()
})
I did it in my test block. Create your normal request as you would send it, then in your tests, validate the original works, and then you can send the second command and validate the response.
You can also use the pre and post scripting to do something similar, or have one test after the other in the file (they run sequentially) to do the same testing.
For instance, I sent an API call here to create records. As I need the Key_ to delete them, I can make a call to GET /foo at my API
pm.test("Response should be 200", function () {
pm.response.to.be.ok;
pm.response.to.have.status(200);
});
pm.test("Parse Key_ values and send DELETE from original request response", function () {
var jsonData = JSON.parse(responseBody);
jsonData.forEach(function (TimeEntryRecord) {
console.log(TimeEntryRecord.Key_);
const DeleteURL = pm.variables.get('APIHost') + '/bar/' + TimeEntryRecord.Key_;
pm.sendRequest({
url: DeleteURL,
method: 'DELETE',
header: { 'Content-Type': 'application/json' },
body: { TimeEntryRecord }
}, function (err, res) {
console.log("Sent Delete: " + DeleteURL );
});
});
});
I have set up an OpenAPI connector in Loopback 4 as described here and for unauthorized requests, it is working well; I managed to create the respective datasource, service and controller. My service is similar to the GeocoderProvider example, but, let's say, with the following service interface.
export interface MyExternalService {
search_stuff(params: {query?: string}): Promise<MyExternalServiceResponse>;
}
export interface MyExternalServiceResponse {
text: string;
}
From my controller, I invoke it like this, where this.myExternalService is the injected service (kind of unrelated, but can Loopback also implicitly parse a JSON response from an external API datasource?):
#get('/search')
async searchStuff(#param.query.string('query') query: string): Promise<void> {
return JSON.parse(
(await this.myExternalService.search_stuff({query})).text,
);
}
Now, the external endpoint corresponding to myExternalService.search_stuff needs an Authorization: Bearer <token> header, where the token is sent to Loopback by the client, i.e. it's not a static API key or so. Assuming I added #param.query.string('token') token: string to the parameter list of my searchStuff controller method, how can I forward that token to the OpenAPI connector? This is the relevant part of the underlying OpenAPI YAML definition file:
paths:
/search:
get:
security:
- Authorization: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResults'
operationId: search-stuff
components:
securitySchemes:
Authorization:
type: http
scheme: Bearer
I am now using the underlying execute function of the OpenAPI connector and manually intercept the request (the object that is passed to requestInterceptor is later passed directly to the http module by Swagger):
return JSON.parse(
(
await this.myExternalService.execute(
'search_stuff',
{query},
{
requestInterceptor: (req: {headers: {Authorization: string}}) => {
req.headers.Authorization = 'Bearer ' + token;
return req;
},
},
)
).text,
);
I also added the following method to the MyExternalService interface, inspired by the connector's actual execute function:
execute(
operationId: string,
parameters: object,
options: object,
): Promise<MyExternalServiceResponse>;
Some things I found:
Loopback internally uses the swagger-client module to do OpenAPI-based requests.
Specifically the securities option of Swagger's execute function expects a Security Definitions Object. There are some quirks with actually passing it to Swagger as well.
Internally, Swagger builds the final HTTP request that is sent out here in its source code. There, the securities key is mentioned, yet is is never actually used for the request. This means that manually specifying it in the third parameter of this.myExternalService.execute will change nothing.
I'll not accept this answer yet and I'm looking forward to finding a more Loopback-like approach.
I configured my service like this, to inject the basic authentication.
import {inject, lifeCycleObserver, LifeCycleObserver} from '#loopback/core';
import {juggler} from '#loopback/repository';
const SwaggerClient = require('swagger-client');
const config = {
name: 'jira',
connector: 'openapi',
spec: 'swagger-v2.json',
validate: false,
httpClient: (request: any) => {
request.headers["Authorization"] = "Basic " + Buffer.from("test:test").toString('base64');
return SwaggerClient.http(request);
},
};
#lifeCycleObserver('datasource')
export class JiraDataSource extends juggler.DataSource
implements LifeCycleObserver {
static dataSourceName = 'jira';
static readonly defaultConfig = config;
constructor(
#inject('datasources.config.jira', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}
I had logined in my server with fetch(),I want to know how I get the cookies.
I know that I can use "document.cookie" to get the cookies in a web browser development,but in react native develop how?
thank you very much.
I just came across the same problem.
My first approach was to manually get the cookies from the response headers.
This become more difficult since Headers.prototype.getAll was removed (see this issue).
The details are shown further down below.
Getting and parsing cookies might be unnecessary
First, I want to mention that all the below cookie parsing turned out to be unnecessary because the implementation of fetch on React Native sends the cookies automatically (if the credentials key is set correctly).
So the session is kept (just like in the browser) and further fetches will work just fine.
Unfortunately, the React Native documentation on Networking does not explicitly tell you that it'll work out of the box. It only says: "React Native provides the Fetch API for your networking needs."
First approach
Thus, I wrote a helper function:
// 'headers' is iterable
const get_set_cookies = function(headers) {
const set_cookies = []
for (const [name, value] of headers) {
if (name === "set-cookie") {
set_cookies.push(value)
}
}
return set_cookies
}
fetch(url, {
method: "POST",
credentials: "same-origin", // or 'include' depending on CORS
// ...
})
.then(response => {
const set_cookies = get_set_cookies(response.headers)
})
To parse the cookie strings into objects I used set-cookie-parser.
This way I wanted send the cookies back manually like
import SetCookieParser from "set-cookie-parser"
const cookies_to_send = set_cookies
.map(cookie => {
const parsed_cookie = SetCookieParser.parse(cookie)
return `${cookie.name}=${cookie.value}`
})
.join('; ')
fetch(url, {
method: "POST",
credentials: "same-origin", // or 'include' depending on CORS
headers: {
Cookie: cookies_to_send,
// ...
},
// ...
})
Inspired by jneuendorf, I created a helper method that returns a key/value pair to easily look up the value of a cookie
export const getCookies = function(response) {
const cookies = {}
for (const [name, values] of response.headers) {
if (name === 'set-cookie') {
for (const cookie of values.split(';')) {
const [key, value] = cookie.split('=')
cookies[key] = value
}
}
}
return cookies
}
I am trying out the new Fetch API but is having trouble with Cookies. Specifically, after a successful login, there is a Cookie header in future requests, but Fetch seems to ignore that headers, and all my requests made with Fetch is unauthorized.
Is it because Fetch is still not ready or Fetch does not work with Cookies?
I build my app with Webpack. I also use Fetch in React Native, which does not have the same issue.
Fetch does not use cookie by default. To enable cookie, do this:
fetch(url, {
credentials: "same-origin"
}).then(...).catch(...);
In addition to #Khanetor's answer, for those who are working with cross-origin requests: credentials: 'include'
Sample JSON fetch request:
fetch(url, {
method: 'GET',
credentials: 'include'
})
.then((response) => response.json())
.then((json) => {
console.log('Gotcha');
}).catch((err) => {
console.log(err);
});
https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials
Have just solved. Just two f. days of brutforce
For me the secret was in following:
I called POST /api/auth and see that cookies were successfully received.
Then calling GET /api/users/ with credentials: 'include' and got 401 unauth, because of no cookies were sent with the request.
The KEY is to set credentials: 'include' for the first /api/auth call too.
If you are reading this in 2019, credentials: "same-origin" is the default value.
fetch(url).then
Programmatically overwriting Cookie header in browser side won't work.
In fetch documentation, Note that some names are forbidden. is mentioned. And Cookie happens to be one of the forbidden header names, which cannot be modified programmatically. Take the following code for example:
Executed in the Chrome DevTools console of page https://httpbin.org/, Cookie: 'xxx=yyy' will be ignored, and the browser will always send the value of document.cookie as the cookie if there is one.
If executed on a different origin, no cookie is sent.
fetch('https://httpbin.org/cookies', {
headers: {
Cookie: 'xxx=yyy'
}
}).then(response => response.json())
.then(data => console.log(JSON.stringify(data, null, 2)));
P.S. You can create a sample cookie foo=bar by opening https://httpbin.org/cookies/set/foo/bar in the chrome browser.
See Forbidden header name for details.
Just adding to the correct answers here for .net webapi2 users.
If you are using cors because your client site is served from a different address as your webapi then you need to also include SupportsCredentials=true on the server side configuration.
// Access-Control-Allow-Origin
// https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api
var cors = new EnableCorsAttribute(Settings.CORSSites,"*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
This works for me:
import Cookies from 'universal-cookie';
const cookies = new Cookies();
function headers(set_cookie=false) {
let headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
};
if (set_cookie) {
headers['Authorization'] = "Bearer " + cookies.get('remember_user_token');
}
return headers;
}
Then build your call:
export function fetchTests(user_id) {
return function (dispatch) {
let data = {
method: 'POST',
credentials: 'same-origin',
mode: 'same-origin',
body: JSON.stringify({
user_id: user_id
}),
headers: headers(true)
};
return fetch('/api/v1/tests/listing/', data)
.then(response => response.json())
.then(json => dispatch(receiveTests(json)));
};
}
My issue was my cookie was set on a specific URL path (e.g., /auth), but I was fetching to a different path. I needed to set my cookie's path to /.
If it still doesn't work for you after fixing the credentials.
I also was using the :
credentials: "same-origin"
and it used to work, then it didn't anymore suddenly, after digging much I realized that I had change my website url to http://192.168.1.100 to test it in LAN, and that was the url which was being used to send the request, even though I was on http://localhost:3000.
So in conclusion, be sure that the domain of the page matches the domain of the fetch url.
Is there a way in Ember.js (and Ember-data) to send credentials to an api that requires Basic HTTP Authentication? I can see how it's done in JQuery here, but don't see any straightforward way to do it in Ember. I thought maybe adding something to the header would work (see below in coffeescript), but no success:
App.AuthAdapter = DS.RESTAdapter.extend(
host: 'https://my-api.example.com'
namespace: 'v1'
headers:
"Authorization Basic fooUsername:barPassword"
...
You can extend the default Rest adapter and add a headers hash which will be included in the ajax that's sent.
App.ApplicationAdapter = DS.RESTAdapter.extend(
headers:
withCredentials: true
Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
)
Or you could take it a step farther and override the ajax method
App.ApplicationAdapter = DS.RESTAdapter.extend(
ajax: (url, type, hash) ->
adapter = this
new Ember.RSVP.Promise((resolve, reject) ->
hash = hash or {}
hash.url = url
hash.type = type
hash.dataType = "json"
hash.context = adapter
if hash.data and type isnt "GET"
hash.contentType = "application/json; charset=utf-8"
hash.data = JSON.stringify(hash.data)
if adapter.headers isnt `undefined`
headers = adapter.headers
hash.beforeSend = (xhr) ->
forEach.call Ember.keys(headers), (key) ->
xhr.setRequestHeader key, headers[key]
hash.success = (json) ->
Ember.run null, resolve, json
hash.error = (jqXHR, textStatus, errorThrown) ->
Ember.run null, reject, adapter.ajaxError(jqXHR)
Ember.$.ajax hash
)
)
Can you use $.ajaxPrefilter? e.g.
Ember.$.ajaxPrefilter (options) ->
options.xhrFields = { withCredentials: true }
options.username = 'fooUsername'
options.password = 'barPassword'
true # need to return non-falsy here
As #gerry3 stated $.ajaxPrefilter is a valid solution.
But if you want to solve a problem of dynamically changing your Headers AFTER an event, for instance, a successful LOGIN attempt, then you need to put more wires. In my case I need to send back a 'Token' Header that is provided by the server after a successful AJAX-login. But, of course, when the user initiates the App he's not logged-in already.
The problem is that once you reopen or extend the RESTAdapter, or define an ajaxPrefilter, even if you're binding it to a value (or localStorage as in my case) the class won't be following the current variable value. It's like a snapshot taken at some moment. So it's useless in my scenario.
I'm following Embercast Client Authentication which is a good start (code available), but instead of jQuery data-fetching I'm using Ember-Data.
So the trick is to observe the token and re-define the ajaxPrefilter as many times as you need it.
tokenChanged: function() {
this.get('token')=='' ?
localStorage.removeItem('token') :
localStorage.token = this.get('token');
$.ajaxPrefilter(function(options, originalOptions, xhr) {
return xhr.setRequestHeader('Token', localStorage.token);
});
}.observes('token')
Therefore, when the user logs-in he'll have a valid token and send it in every request to the server via the RESTAdapter.
Hope this helps someone.