yarp rewriting hardcoded service urls - ms-yarp

I don't know, if my question is well formulated, but I try...
I'm using yarp as a reverse proxy.
Behind the proxy there is a asp.net core service (order-service) on port 5048 e.g.
So the config looks like the following:
"ReverseProxy": {
"Routes": {
"orderService": {
"ClusterId": "orderServiceProps",
"Match": {
"Path": "/order-service/{**Remainder}"
},
"Transforms": [
{
"PathRemovePrefix": "/order-service"
}
]
}
},
"Clusters": {
"orderServiceProps": {
"Destinations": {
"destination1": {
"Address": "http://localhost:5048"
}
}
}
}
}
If I type e.g. http://localhost:8080/order-service/api/collection the request gets forwarded to http://localhost:5048/api/collection
All well so far.
But the order-service also has a simple html gui, which is showing some simple links to some special endpoints.
Now if I click on e.g. the settings link, the service navigates to http://localhost:5048/settings but I want the service navigates to http://localhost:8080/order-service/settings
I don't know if I can reach such a bahavior.
I don't want the user is seeing the 5048 in his browser!
And can I do this without let the service know that it is behind a proxy?

On the order-service you should configure the X-Forwarded middleware like this:
// in ConfigureServices
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
// in Configure
app.UseForwardedHeaders();
Check this for more info:
Configure ASP.NET Core to work with proxy servers and load balancers

Related

Allowing to create multiple entries only on internal service calls

I want to create multiple entries on an internal service call. But for external transports (rest, websockets) this functionality should still be blocked.
I know that the multi option can be set to true or ['create'] in the service options but this does not fix the problem, because external transports could then create multiple entries.
My first solutions was this:
someService.hooks.js
...
before: {
create: [
context => {
if (!context.params.provider) {
context.service.options.multi = true;
}
return context;
}
],
}
...
But this completely overwrites the service options for all service calls.
The only other solutions I came up with, is to set service.multi to true and validate each external service call with a hook.
Would this be the only solution which would work or did I missed something?
What you can currently do is enable multi: [ 'create' ] and check in a hook if it is an external call and throw an error for arrays in that case:
const { BadRequest } = require('#feathersjs/errors');
// ...
create: [
async context => {
if (context.params.provider && Array.isArray(context.data)) {
throw new BadRequest('Not allowed');
}
return context;
}
],
In upcoming versions this will be possible by just passing the multi option in params (tracked in this issue)

has been blocked by CORS policy: Request header field x-xhr-logon is not allowed by Access-Control-Allow-Headers in preflight response

I am trying to consume external restful web service in sap ui5. When I consume the same in fiori launchpad it throws below error in cosole and no data comes in the tile app. How can I over come with that? I have checked many blogs relted to that but didn't get any help from that.
Error :
Access to XMLHttpRequest at 'https://api.myjson.com/bins/ijyy2' from origin 'url2' has been blocked by CORS policy: Request header field x-xhr-logon is not allowed by Access-Control-Allow-Headers in preflight response.
Note : url2= https://sapmobile.mycompanyname.com is nothing but our fiori
launchpad url.
This is a known Fiori Launchpad problem. There is a file abap.js which overrides the default send method of XMLHttpRequest.
If you add the external API as a new destination in the SAP Cloud Platform (or use a Web Dispatcher in an on-premise environment) then there will be no more CORS calls and thus no more CORS problems.
If you want a pure JavaScript solution you can restore the original implementation with two functions. Add these to your controller.
Call the following immediately before accessing your external API
_overrideRequestPrototype: function () {
if (!XMLHttpRequest._SAP_ENHANCED) {
return;
}
this.__send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function (oBody) {
let oChannel = {};
this._checkEventSubscriptions();
try {
oChannel = this._channel;
this._saveParams(oBody);
this._send(oBody);
if (oChannel) {
oChannel.sent();
}
} catch (oError) {
if (oChannel) {
oChannel["catch"](oError);
} else {
throw oError;
}
}
};
}
After the call, restore the SAP code with the following function:
_restoreRequestPrototype: function () {
if (!XMLHttpRequest._SAP_ENHANCED) {
return;
}
XMLHttpRequest.prototype.send = this.__send;
}

JIRA trigger a workflow transition via REST

I want to trigger my test issue's current status, for example A to B via Rest call.
I've searched on the web and come across Atlassian Documentation. What it says:
->You must use POST method.
->You must define transition id in rest call body. Like following:
{
"update": {
"comment": [
{
"add": {
"body": "Aok was here"
}
}
]
},
"transitions": {
"id": "471"
}
}
->You must construct an url like: http://test/jira/rest/api/latest/issue/{ISSUE-KEY}/transitions
When i test above with post-man, i get nothing but a white page response body.
What may be wrong here?
Thanks
Anyone who faces this problem, here is the solution:
You need to make http request with Content-Type:application/json in header.

convenience method in spray (soon to be akka-http) to create a Location header w/ host port & contextRoot ?

When I create an object through POST in my Spray app I'd like to return a 201 status, together with a Location header that has the absolute URI of the newly created resource (including host port & contextRoot of my app)
Here is an example code fragment from my application...
post {
respondWithHeaders(Location( fullyQualifiedUri("/movies"))) {
entity(as[MovieImpl]) { (movieToInsert: MovieImpl) => {
addMovies(movieToInsert)
complete("OK")
}
}
}
}
Note that I now have to write the method 'fullyQualifiedUri' to return
a URI with host, port, etc. It would be nice if Spray did that for me
automagically.
Side note:
I think that having the Location header include the absolute URI of the newly
created resource makes it easier on my REST API's clients (although it does seem that there are a variety of opinions on this.)
Thanks in advance for any guidance you can provide.
-chris
To build the URI you'll need the Id of the newly created resource. Then you can use the requestInstance directive to get the incoming request URI and build the new resource URI from it. You also need to set the return code to Created to meet your requirements:
post {
requestInstance { request =>
val movieId = ???
respondWithHeaders(Location( request.uri.withPath(request.uri.path / movieId))) {
entity(as[MovieImpl]) { (movieToInsert: MovieImpl) => {
addMovies(movieToInsert)
complete(StatusCodes.Created)
}
}
}
}
}

how to resolve optional url path using ng-resource

There are restful APIs, for instance:
/players - to get list for all players
/players{/playerName} - to get info for specific player
and I already have a function using ng-resource like:
function Play() {
return $resource('/players');
}
Can I reuse this function for specific player like:
function Play(name) {
return $resource('/players/:name', {
name: name
});
}
so I want to...
send request for /players if I didn't pass name parameter.
send request for /players/someone if I passed name parameter with someone
Otherwise, I have to write another function for specific play?
Using ngResource it's very, very simple (it's basically a two-liner). You don't need even need to create any custom actions here*.
I've posted a working Plunkr here (just open Chrome Developer tools and go to the Network tab to see the results).
Service body:
return $resource('/users/:id/:name', { id:'#id', name: '#name' })
Controller:
function( $scope, Users ){
Users.query(); // GET /users (expects an array)
Users.get({id:2}); // GET /users/2
Users.get({name:'Joe'}); // GET /users/Joe
}
of course, you could, if you really wanted to :)
This is how I did it. This way you don't have to write a custom resource function for each one of your endpoints, you just add it to your list resources list. I defined a list of the endpoints I wanted to use like this.
var constants = {
"serverAddress": "foobar.com/",
"resources": {
"Foo": {
"endpoint": "foo"
},
"Bar": {
"endpoint": "bar"
}
}
}
Then created resources out of each one of them like this.
var service = angular.module('app.services', ['ngResource']);
var resourceObjects = constants.resources;
for (var resourceName in resourceObjects) {
if (resourceObjects.hasOwnProperty(resourceName)) {
addResourceFactoryToService(service, resourceName, resourceObjects[resourceName].endpoint);
}
}
function addResourceFactoryToService (service, resourceName, resourceEndpoint) {
service.factory(resourceName, function($resource) {
return $resource(
constants.serverAddress + resourceEndpoint + '/:id',
{
id: '#id',
},
{
update: {
method: 'PUT',
params: {id: '#id'}
},
}
);
});
}
The nice thing about this is that it takes 2 seconds to add a new endpoint, and I even threw in a put method for you. Then you can inject any of your resources into your controllers like this.
.controller('homeCtrl', function($scope, Foo, Bar) {
$scope.foo = Foo.query();
$scope.bar = Bar.get({id:4});
}
Use Play.query() to find all players
Use Play.get({name:$scope.name}) to find one player