ASP.NET Core permament redirect when no path specified - asp.net-core

Is this a right way of doing permanent redirect when no path specified for request?
app.Use(next => context =>
{
if (string.IsNullOrWhiteSpace(context.Request.Path))
{
var builder = new UriBuilder(context.Request.Scheme, "site to redirect");
context.Response.Redirect(builder.ToString(), true);
}
return next(context);
});
Update 1
It appears that context.Request.Path includes /
app.Use(next => context =>
{
if (context.Request.Path.Value.Length <= 1)
{
var builder = new UriBuilder(context.Request.Scheme, "www.plaMobi.com");
context.Response.Redirect(builder.ToString(), true);
}
return next(context);
});

Accordingly to UriHelper implementation, both HttpRequest.PathBase abd HttpRequest.Path should be used:
var combinedPath = (pathBase.HasValue || path.HasValue)
? (pathBase + path).ToString() : "/";
The same logic in ProxyMiddleware class:
var uriString = $"{_options.Scheme}://{_options.Host}:{_options.Port}{context.Request.PathBase}{context.Request.Path}{context.Request.QueryString}";

Related

RavenDB The database **** is currently locked because Checking if we need to recreate indexes

I am using RavenTestDriver for my unit tests .
Here is my configuration of my test :
ConfigureServer(new TestServerOptions
{
CommandLineArgs = new System.Collections.Generic.List<string> { "--RunInMemory=true", },
FrameworkVersion = null,
}) ;
IDocumentStore store = GetDocumentStore();
try
{
store.Maintenance.ForDatabase(ravenTestDatabaseName).Send(new GetStatisticsOperation());
}
catch (DatabaseDoesNotExistException)
{
store.Maintenance.Server.Send(new CreateDatabaseOperation(new DatabaseRecord(ravenTestDatabaseName)));
}
session = store.OpenAsyncSession(new SessionOptions()
{
Database=ravenTestDatabaseName,
});
var hostBuilder = easy.api.Program.CreateHostBuilder(new string[0])
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder.UseTestServer();
})
.ConfigureServices(services =>
{
services.AddScoped<ICurrentUserService, InitRequest>();
services.AddScoped<ICacheStorage>(provider=>
{
return new Mock<ICacheStorage>().Object;
});
services.AddRavenDbAsyncSession(GetDocumentStore(new GetDocumentStoreOptions()));
services.AddScoped<IAsyncDocumentSession>((c) =>
{
return store.OpenAsyncSession(new SessionOptions()
{
Database=ravenTestDatabaseName,
});
});
});
So I have several test in my solution:
When I run each test individual the test passed .But When I run all tests together I get this error :
Raven.Client.Exceptions.Database.DatabaseDisabledException : Raven.Client.Exceptions.Database.DatabaseDisabledException: The database Test-Server is currently locked because Checking if we need to recreate indexes
at Raven.Server.Documents.DatabasesLandlord.UnloadAndLockDatabase(String dbName, String reason) in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\Documents\DatabasesLandlord.cs:line 907
at Raven.Server.Web.System.AdminDatabasesHandler.Put() in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\Web\System\AdminDatabasesHandler.cs:line 327
at Raven.Server.Routing.RequestRouter.HandlePath(RequestHandlerContext reqCtx) in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\Routing\RequestRouter.cs:line 349
at Raven.Server.RavenServerStartup.RequestHandler(HttpContext context) in C:\Builds\RavenDB-Stable-5.2\52000\src\Raven.Server\RavenServerStartup.cs:line 174
The server at /admin/databases?name=Test-Server&replicationFactor=1&raft-request-id=d9a39f56-a1ad-44b7-b6e5-b195c1143c4b responded with status code: ServiceUnavailable.
I just leave the database name empty .
public class TestHostBuilder : RavenTestDriver, IAsyncLifetime
{
public HttpClient httpClient = null;
public IDocumentStore documentStore = null;
public async Task InitializeAsync()
{
documentStore = GetDocumentStore();
var hostBuilder = easy.api.Program.CreateHostBuilder(new string[0])
.ConfigureWebHost(webHostBuilder =>
{
webHostBuilder.UseTestServer();
})
.ConfigureServices(services =>
{
services.AddScoped<ICurrentUserService, InitRequest>();
services.AddScoped<ICacheStorage>(provider =>
{
return new Mock<ICacheStorage>().Object;
});
services.AddRavenDbAsyncSession(GetDocumentStore(new GetDocumentStoreOptions()));
services.AddTransient<IAsyncDocumentSession>((c) =>
{
return documentStore.OpenAsyncSession();
});
});
var host = hostBuilder.Start();
httpClient = host.GetTestClient();
}
public Task DisposeAsync()=> Task.CompletedTask;
}

Shorten the path of area in asp.net core 5 Razor pages

How can i conver routing of "Identity/Account" to "/Account" for all pages?
In your startup.cs you can change :
services.AddRazorPages();
with :
services.AddRazorPages().AddRazorPagesOptions(options =>
options.Conventions.AddAreaFolderRouteModelConvention("Identity", "/Account/", model =>
{
foreach (var selector in model.Selectors)
{
var attributeRouteModel = selector.AttributeRouteModel;
attributeRouteModel.Order = -1;
attributeRouteModel.Template = attributeRouteModel.Template.Remove(0, "Identity".Length);
}
})
);

Kendo UI Scheduler incorrectly calling WebAPI

I have been looking around the Telerik forums & Stackoverflow for an answer for this and I am completely stuck and unable to figure out the issue.
I am using the Kendo UI for Asp.Net Core Scheduler Control. I have it reading the data from my controller fine. However, I cannot get it call the HttpPut handler correctly.
When checking the traffic I get the following response, and therefor my breakpoint inside my HttpPut handler will never be hit.
400 - Bad Request
{"":["The input was not valid."]}
My code in my view is:
#(Html.Kendo().Scheduler<MeetingViewModel>()
.Name("SchedulerView")
.Height(500)
.Date(DateTime.Now.ToUniversalTime())
.StartTime(new DateTime(2018, 11, 28, 0, 00, 00).ToUniversalTime())
.MajorTick(30)
.ShowWorkHours(false)
.Footer(false)
.Editable(edit =>
{
//edit.Resize(false);
edit.Create(false);
})
.Views(views =>
{
views.TimelineView(timeline => timeline.EventHeight(50));
//views.TimelineWeekView(timeline => timeline.EventHeight(50));
//views.TimelineWorkWeekView(timeline => timeline.EventHeight(50));
//views.TimelineMonthView(timeline =>
//{
// timeline.StartTime(DateTime.Now);
// timeline.EndTime(DateTime.Now.AddMonths(1));
// timeline.MajorTick(1440);
// timeline.EventHeight(50);
//});
})
.Timezone("Etc/UTC")
.Group(group => group.Resources("WorkCenters" /*,"Attendees"*/).Orientation(SchedulerGroupOrientation.Vertical))
.Resources(resource =>
{
resource.Add(m => m.ScheduleRowID)
.Title("Work Center")
.Name("WorkCenters")
.DataTextField("Text")
.DataValueField("Value")
.DataColorField("Color")
.BindTo(#Model.AvailableWorkCenters);
})
.DataSource(d => d
.ServerOperation(true)
.WebApi()
.Model(m =>
{
m.Id(f => f.ActivityID);
m.Field(f => f.Title).DefaultValue("No title");
//m.RecurrenceId(f => f.RecurrenceID);
m.Field(f => f.Description).DefaultValue("No Description");
})
.Events(events => events.Error("error_handler"))
.Read(read => read.Action("GetActivities", "Scheduler").Data("setRequestDateTimes"))
//.Create(create => create.Action("Post", "Scheduler"))
.Update(update => update.Action("PutActivity", "Scheduler", new { id = "{0}" }).Type(HttpVerbs.Put))
//.Destroy(destroy => destroy.Action("Delete", "Scheduler", new { id = "{0}" }))
)))
And my API Controller is as follows:
[Route("Api/[controller]")]
[ApiController]
public class SchedulerController : DawnController
{
public SchedulerController(DatabaseContext context) : base(context)
{
}
[HttpGet]
public DataSourceResult GetActivities([DataSourceRequest] DataSourceRequest request, DateTime requestStartDateTime, DateTime requestEndDateTime)
{
//Kendo doesnt seem to send the full date range. so + 1 day to end
requestEndDateTime = requestEndDateTime.AddDays(1);
List<MeetingViewModel> test = new List<MeetingViewModel>();
foreach (JobTask jobTask in Context.JobTask)
{
if (JobTask.HasActivityInDateRange(jobTask, requestStartDateTime, requestEndDateTime))
{
foreach (Activites jobTaskAct in jobTask.Activites)
{
test.Add(new MeetingViewModel()
{
JobTaskID = jobTask.JobTaskId,
ActivityID = jobTaskAct.ActivityId,
Title = jobTaskAct.Name,
Description = jobTaskAct.Description,
Start = jobTaskAct.StartTime.ToUniversalTime(),
End = jobTaskAct.EndTime.ToUniversalTime(),
IsAllDay = false,
ScheduleRowID = jobTaskAct.Workcenter.WorkCenterId,
});
}
}
}
return test.ToDataSourceResult(request);
}
[HttpPut("{id}")]
public IActionResult PutActivity(int id, MeetingViewModel task)
{
if (ModelState.IsValid && id == task.ActivityID)
{
try
{
//breakpoint here
bool a = true;
//update the db here
}
catch (DbUpdateConcurrencyException)
{
return new NotFoundResult();
}
return new StatusCodeResult(200);
}
else
{
return BadRequest(ModelState.Values.SelectMany(v => v.Errors).Select(error => error.ErrorMessage));
}
}
}
Thanks
The URL exposing your controller method PutActivity in your controller example is PUT api/scheduler/{id}
To access that URL use this Update method.
.Update(update => update.Action("Put", "Scheduler", new { id = "{0}" }))
See this demo as example
Alternatively
If you want to implment the URL api/Scheduler/PutActivity/{id} (similar pattern to your GET) then you will need to modify the attribute over the put method as follows.
[HttpPut("PutActivity/{id}")]
public IActionResult PutActivity(int id, MeetingViewModel task)
Then you can call api/Scheduler/PutActivity/{id} with this asp.net action call.
.Update(update => update.Action("PutActivity", "Scheduler", new { id = "{0}" }).Type(HttpVerbs.Put))

Downloading Excel file via aurelia-http-client

I am working on a task, in which I have to download a report in xlsx format. The report file is generated successfully from server, and is received on client side (aurelia-http-client) as well but I don't know how to go further with downloading.
I would do something like in this answer https://stackoverflow.com/a/30270714/6677648
... that would end up in something like a response interceptor in Aurelia like this:
.withResponseType('blob')
.withInterceptor({
response(message) {
var defaultFileName = "default.txt";
var disposition = message.headers.headers['content-disposition']?message.headers.headers['content-disposition']:message.headers.headers['Content-Disposition'];
if (disposition) {
var match = disposition.match(/.*filename=\"?([^;\"]+)\"?.*/);
if (match[1])
defaultFileName = match[1];
}
defaultFileName = defaultFileName.replace(/[<>:"\/\\|?*]+/g, '_');
if (navigator.msSaveBlob)
return navigator.msSaveBlob(message.response, defaultFileName);
var blobUrl = window.URL.createObjectURL(message.response);
var anchor = document.createElement('a');
anchor.download = defaultFileName;
anchor.href = blobUrl;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
}
})
I used the downloadjs library. Install the library, add it to your aurelia.json and then add
import * as download from 'downloadjs'
Then write your code as follows:
this.httpClient.fetch('your/url/here')
.then((response: Response) => response.blob())
.then((blob: Blob) => download(blob, 'filename.extension', 'mime type of the file'));
And voila, your file will be downloaded.
Helo with .withInterceptor() was generated errors in the response, change it to fix the error in no responce and unload multiple files simultaneously.
getLogsCsv(param) {
this.http.configure(config => {
config
.withResponseType('blob');
});
return this.http.get("/admin/api/logs" + param)
.then(response => {
if (response.statusCode == 200) {
var defaultFileName = "FileName.csv";
var blobUrl = window.URL.createObjectURL(response.response);
var anchor = document.createElement('a');
anchor.download = defaultFileName;
anchor.href = blobUrl;
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
return response.content;
} else {
console.log('response was not ok.');
console.log(response);
}
})
.catch(error => {
console.log(error);
});
}

Single page application client and server routing

I've got the following code to handle client side navigation using HTML5 pushstate (classic combination of crossroadsjs and historyjs):
History = window.History;
History.Adapter.bind(window, 'statechange', function () {
var state = History.getState();
console.log(state);
if (state.data.urlPath) {
return crossroads.parse(state.data.urlPath);
}
else
{
if (state.hash.length > 1) {
var fullHash = state.hash;
var hashPath = fullHash.slice(0, fullHash.indexOf('?'));
return crossroads.parse(hashPath);
}
}});
crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
crossroads.parse('/');
$('body').on('click', 'a', function(e) {
var title, urlPath;
urlPath = $(this).attr('href');
if (urlPath.slice(0, 1) == '#'){
return true;
}
e.preventDefault();
title = $(this).text().trim();
return History.pushState({ urlPath: urlPath }, title, urlPath);
});
It works really well. Now, to handle url bookmarking and sharing, I added and express server to handle all requests. All it does is to redirect to index.html (a sort of catchall rule):
var env = require('./env');
var fallback = require('express-history-api-fallback');
var express = require('express');
var app = express();
var config = env.config();
var root = __dirname + '/dist';
app.use(express.static(root));
app.use(fallback('index.html', { root: root }));
var port = process.env.PORT || 9090;
var server = app.listen(port, function () {
console.log('Server started at: http://localhost:' + port);
console.log(config);
});
The problem I am facing is that it successfully redirects to index.html but it doesn't load the correct route on the client side. So a request to www.mysite.com or www.mysite.com/anotherpage will always load the home page route.
I am obviously missing some code to intercept that and load the appropriate route on the client side. I just don't know what to do.
Found where the bug was:
crossroads.parse('/');
This was always redirecting to the "home" route. I just had to refactor the code a bit:
History.Adapter.bind(window, 'statechange', this.routeCrossRoads);
routeCrossRoads() {
var state = History.getState();
if (state.data.urlPath) {
return crossroads.parse(state.data.urlPath);
}
else {
if (state.hash.length > 1) {
var fullHash = state.hash;
var pos = fullHash.indexOf('?');
if (pos > 0) {
var hashPath = fullHash.slice(0, pos);
return crossroads.parse(hashPath);
}
else {
return crossroads.parse(fullHash);
}
}
else {
return crossroads.parse('/');
}
}
}