Piranha CMS middleware to capture requests to /uploads - asp.net-core

When a request is made for an image|file it doesn't reach my middleware.
I can see that when UseCms is called it adds PiranhaStartupFilter
serviceBuilder.Services.AddTransient<IStartupFilter, PiranhaStartupFilter>();
Which adds UseStaticFiles() to the ApplicationBuilder.
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
builder
.UseSecurityMiddleware()
.UseStaticFiles()
.UseMiddleware<RoutingMiddleware>()
.UseMiddleware<SitemapMiddleware>();
next(builder);
};
}
How could I overwrite this functionality so that my middleware is called for requests?
I'm expecting a call to /uploads/foo.jpg would be picked up in the InvokeAsync method of my middleware, registered like so:
app.UsePiranha(options =>
{
options.Builder.CustomImageTools();
});
At present only files & favicon requests reach the InvokeAsync method in my middleware.

As middleware in asp.net is executed in the order they are added into the pipeline it should be sufficient to add your interceptor before making any calls to Piranha.
You can add the middleware by adding a service above the call to UseCms() in startup.cs.
services.AddPiranha(options =>
{
options.CustomImageFilter();
options.UseCms();
}
public class CustomImageFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseWhen(
context => some_condition,
appbuilder => appbuilder.CustomImageTools()
);
// Call the next configure method
next(app);
};
}
}

Related

Mocking an api call for testing in Laravel

I have a controller that calls an api outside like this:
public function getUserData(Request $request)
{
// api connection is established using guzzle and into $client variable
$userData = $client->get($request->user_id);
// ... process data
}
then in my test I do this.
public function testUserData()
{
$userData = $this->post('user.data', [
'user_id' => 1
]);
$this->assertEquals($userData['user_id'], 1);
}
The test is working but I want to mock the api call so that it will not actually fire that call outside of the application.
You could do it like this:
Inject your HTTP client to the controller through the constructor
Swap the implementation of your client with a mocked one in your test
So, basically:
// Controller
protected $client;
public function __construct(MyHttpClient $client)
{
// Config your client
// Then set it to the class property
$this->client = $client;
}
public function getUserData()
{
$this->client->post-> ...
}
Then in your test:
public function testUserData()
{
$clientMock = \Mockery::mock(MyHttpClient::class);
// You specify here your assertions for the API call
$clientMock->shouldReceive('post')->with(...)->andReturn(...);
$this->app->instance(MyHttpClient::class, $clientMock);
$userData = $this->post('user.data', [
'user_id' => 1
]);
$this->assertEquals($userData['user_id'], 1);
}
In Laravel 9, there is a mocking implementation of Illuminate\Support\Facades\Http where you can mock responses that are sent to specific hosts/urls.
This is taken from the example in the documentation:
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
// Stub a string response for Google endpoints...
'google.com/*' => Http::response('Hello World', 200, $headers),
]);
Once the fakes are stubbed, you can then run your test normally and it will detect any requests sent to those faked endpoints.
It should also be mentioned that you will have to use the Laravel Http client in your own external requests for this to work correctly.

How to resolve ~/ aka application relative url in a middleware?

I'm struggling to redirect a user to the correct location in an ASP.NET Core middleware.
What I'm trying to archieve is an application relative redirect, so if my app runs as https://www.example.com/app any link generated should be rooted there.
E.g. ~/path should resolve to /app/path.
I tried using UrlHelperFactory, but that's MVC and needs an ActionContext, which does not exist inside a middleware.
Heres a code sample of my middleware:
app.Map(pathMatch, appMap =>
{
// [... more code ...]
appMap.Use((context, next) =>
{
var nextUrl = context.Request.Query["nextUrl"];
var applicationBase = context.Request.PathBase;
if (nextUrl == StringValues.Empty || !UrlUtil.IsLocalUrl(nextUrl[0]))
{
context.Response.Redirect(applicationBase);
}
else
{
context.Response.Redirect(applicationBase.Add(nextUrl[0]));
}
return Task.CompletedTask;
});
});

Understand how middleware works in Moleculer

I understand Mix-ins are to extend a common functionality across services. But Im not able to understand how middleware works in molecular and what problems It could help me solve.
Check the documentation how middleware works in Moleculer framework: https://moleculer.services/docs/0.13/middlewares.html
With middlewares you can extend the framework functionality with your custom logic.
Here is an example how a middleware looks like which extend service action handling:
const MyCustomMiddleware = {
// Wrap local action handlers (legacy middleware handler)
localAction(next, action) {
return function(ctx) {
// Change context properties or something
return next(ctx)
.then(res => {
// Do something with the response
return res;
})
.catch(err => {
// Handle error or throw further
throw err;
});
}
}
};

How to ignore routes in ASP.NET Core?

Previously, one would add something like this to Global.aspx.cs, which is gone in .NET Core:
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" });
Here's what I currently have in my Startup.cs (for .NET Core):
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
The problem is that in MVC (pre-Core) routes was a RouteCollection and in .NET Core it's a Microsoft.AspNetCore.Routing.IRouteBuilder so IgnoreRoute is not a valid method.
You could write middleware for this.
public void Configure(IApplciationBuilder app) {
app.UseDefaultFiles();
// Make sure your middleware is before whatever handles
// the resource currently, be it MVC, static resources, etc.
app.UseMiddleware<IgnoreRouteMiddleware>();
app.UseStaticFiles();
app.UseMvc();
}
public class IgnoreRouteMiddleware {
private readonly RequestDelegate next;
// You can inject a dependency here that gives you access
// to your ignored route configuration.
public IgnoreRouteMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
if (context.Request.Path.HasValue &&
context.Request.Path.Value.Contains("favicon.ico")) {
context.Response.StatusCode = 404;
Console.WriteLine("Ignored!");
return;
}
await next.Invoke(context);
}
}
.NET Core 3.1
For .NET Core 3.1 with endpoint routing, this seems like the easiest way. You don't need to build a middleware just for this simple case.
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/favicon.ico", async (context) =>
{
context.Response.StatusCode = 404;
});
// more routing
});
The original question is about ignoring routes. If you do want to serve a favicon, you can add HTML markup in _Layout.cshtml like this. This technique allows more control over where the icon is served from.
<link rel="icon" href="#Url.Content("~/images/favicon.ico")" />
I am using both of these techniques in my code.
If you want to make a static file accessible without the routing condition, simply use the build-in StaticFiles Middleware.
Activate it with app.UseStaticFiles();
in Configure Method and put your static files in wwwroot directory.
They're availible on HOST/yourStaticFile
For more information, refer here
inside public void Configure
add
app.Map("/favicon.ico", delegate { });
Allow favicon requests to be parsed by the route handler, and keep your routes to a minimum. Avoid using middleware, this just adds additional complexity to your code and means all other requests must go through the middleware before the route handler, which is worse in terms of performance for busy websites. For websites that aren't busy you would just be wasting your time worrying about this.
See https://github.com/aspnet/Routing/issues/207
In ASP.NET Core, you can write a constrained catch-all route template. To do so, in your ASP.NET Core example, replace the call to routes.MapSpaFallbackRoute with the following:
// Returns the home/index page for unknown files, except for
// favicon.ico, in which case a 404 error is returned.
routes.MapRoute(
name: "spa-fallback",
template: "{*url:regex(^(?!favicon.ico).*$)}",
defaults: new { Controller = "Home", action = "Index" });

Can I set authorization headers with RequireJS?

We want to have 2 sets of resources for our AngularJS app (public/private) which uses RequireJS for dependency management. Basically everything on the login page would be public and once logged in, another angularjs app would be loaded (new requirejs config) that would load resources that require authentication to access.
Is there a way to configure requirejs to set an authorization header when loading resources?
It depends on what you mean by "resources" and how your server is configured. But in general - yes, since you are using AngularJS you can use the $httpProvider to inject an interceptor service.
For example, in a service:
var dependencies = ['$rootScope', 'userService'];
var service = function ($rootScope, userService) {
return {
request: function(config) {
var currentUser = userService.getCurrentUser();
var access_token = currentUser ? currentUser.access_token : null;
if(access_token) {
config.headers.authorization = access_token;
}
return config;
},
responseError: function (response) {
if(response.status === 401) {
$rootScope.$broadcast('unauthorized');
}
return response;
}
};
};
module.factory(name, dependencies.concat(service));
Then, after you configure your routes, you can use:
$httpProvider.interceptors.push( 'someService');
You can find some more information on interceptors here: https://docs.angularjs.org/api/ng/service/$http#interceptors
UPDATE
You might be able to use the text plugin to try and receive it, but I don't see the point in protecting client side code. Plus, if you want to use optimization the resources will just come in one file anyway...
config: {
text: {
onXhr: function (xhr, url) {
xhr.setRequestHeader('Authorization','Basic ' + token);
}
}
}
Refer to: custom-xhr-hooks
Another UPDATE
You could also use urlArgs (mainly used for cache invalidation) without using the text plugin:
require.config({
urlArgs: 'token='+token,
...
)}