Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/JsonApiDotNetCore/Middleware/JsonApiMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ private static string GetBasePath(string resourceName, IJsonApiOptions options,
builder.Append(httpRequest.Host);
}

if (httpRequest.PathBase.HasValue)
{
builder.Append(httpRequest.PathBase);
}

string customRoute = GetCustomRoute(resourceName, options.Namespace, httpRequest.HttpContext);
if (!string.IsNullOrEmpty(customRoute))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
using Microsoft.Extensions.Logging;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
public sealed class ArtGalleriesController : JsonApiController<ArtGallery>
{
public ArtGalleriesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<ArtGallery> resourceService)
: base(options, loggerFactory, resourceService)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Generic;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
public sealed class ArtGallery : Identifiable
{
[Attr]
public string Theme { get; set; }

[HasMany]
public ISet<Painting> Paintings { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
public sealed class HostingDbContext : DbContext
{
public DbSet<ArtGallery> ArtGalleries { get; set; }
public DbSet<Painting> Paintings { get; set; }

public HostingDbContext(DbContextOptions<HostingDbContext> options)
: base(options)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using Bogus;
using TestBuildingBlocks;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
internal sealed class HostingFakers : FakerContainer
{
private readonly Lazy<Faker<ArtGallery>> _lazyArtGalleryFaker = new Lazy<Faker<ArtGallery>>(() =>
new Faker<ArtGallery>()
.UseSeed(GetFakerSeed())
.RuleFor(artGallery => artGallery.Theme, f => f.Lorem.Word()));

private readonly Lazy<Faker<Painting>> _lazyPaintingFaker = new Lazy<Faker<Painting>>(() =>
new Faker<Painting>()
.UseSeed(GetFakerSeed())
.RuleFor(painting => painting.Title, f => f.Lorem.Sentence()));

public Faker<ArtGallery> ArtGallery => _lazyArtGalleryFaker.Value;
public Faker<Painting> Painting => _lazyPaintingFaker.Value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCoreExampleTests.Startups;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
public sealed class HostingStartup<TDbContext> : TestableStartup<TDbContext>
where TDbContext : DbContext
{
public HostingStartup(IConfiguration configuration) : base(configuration)
{
}

protected override void SetJsonApiOptions(JsonApiOptions options)
{
base.SetJsonApiOptions(options);

options.Namespace = "public-api";
options.IncludeTotalResourceCount = true;
}

public override void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
{
app.UsePathBase("/iis-application-virtual-directory");

base.Configure(app, environment);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using FluentAssertions;
using JsonApiDotNetCore.Serialization.Objects;
using TestBuildingBlocks;
using Xunit;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
public sealed class HostingTests
: IClassFixture<ExampleIntegrationTestContext<HostingStartup<HostingDbContext>, HostingDbContext>>
{
private const string HostPrefix = "http://localhost";

private readonly ExampleIntegrationTestContext<HostingStartup<HostingDbContext>, HostingDbContext> _testContext;
private readonly HostingFakers _fakers = new HostingFakers();

public HostingTests(ExampleIntegrationTestContext<HostingStartup<HostingDbContext>, HostingDbContext> testContext)
{
_testContext = testContext;
}

[Fact]
public async Task Get_primary_resources_with_include_returns_links()
{
// Arrange
var gallery = _fakers.ArtGallery.Generate();
gallery.Paintings = _fakers.Painting.Generate(1).ToHashSet();

await _testContext.RunOnDatabaseAsync(async dbContext =>
{
await dbContext.ClearTableAsync<ArtGallery>();
dbContext.ArtGalleries.Add(gallery);
await dbContext.SaveChangesAsync();
});

const string route = "/iis-application-virtual-directory/public-api/artGalleries?include=paintings";

// Act
var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync<Document>(route);

// Assert
httpResponse.Should().HaveStatusCode(HttpStatusCode.OK);

responseDocument.Links.Self.Should().Be(HostPrefix + route);
responseDocument.Links.Related.Should().BeNull();
responseDocument.Links.First.Should().Be(HostPrefix + route);
responseDocument.Links.Last.Should().Be(HostPrefix + route);
responseDocument.Links.Prev.Should().BeNull();
responseDocument.Links.Next.Should().BeNull();

string galleryLink = HostPrefix + $"/iis-application-virtual-directory/public-api/artGalleries/{gallery.StringId}";

responseDocument.ManyData.Should().HaveCount(1);
responseDocument.ManyData[0].Links.Self.Should().Be(galleryLink);
responseDocument.ManyData[0].Relationships["paintings"].Links.Self.Should().Be(galleryLink + "/relationships/paintings");
responseDocument.ManyData[0].Relationships["paintings"].Links.Related.Should().Be(galleryLink + "/paintings");

// TODO: The next link is wrong: it should use the custom route.
string paintingLink = HostPrefix + $"/iis-application-virtual-directory/public-api/paintings/{gallery.Paintings.ElementAt(0).StringId}";

responseDocument.Included.Should().HaveCount(1);
responseDocument.Included[0].Links.Self.Should().Be(paintingLink);
responseDocument.Included[0].Relationships["exposedAt"].Links.Self.Should().Be(paintingLink + "/relationships/exposedAt");
responseDocument.Included[0].Relationships["exposedAt"].Links.Related.Should().Be(paintingLink + "/exposedAt");
}

[Fact]
public async Task Get_primary_resources_with_include_on_custom_route_returns_links()
{
// Arrange
var painting = _fakers.Painting.Generate();
painting.ExposedAt = _fakers.ArtGallery.Generate();

await _testContext.RunOnDatabaseAsync(async dbContext =>
{
await dbContext.ClearTableAsync<Painting>();
dbContext.Paintings.Add(painting);
await dbContext.SaveChangesAsync();
});

const string route = "/iis-application-virtual-directory/custom/path/to/paintings?include=exposedAt";

// Act
var (httpResponse, responseDocument) = await _testContext.ExecuteGetAsync<Document>(route);

// Assert
httpResponse.Should().HaveStatusCode(HttpStatusCode.OK);

responseDocument.Links.Self.Should().Be(HostPrefix + route);
responseDocument.Links.Related.Should().BeNull();
responseDocument.Links.First.Should().Be(HostPrefix + route);
responseDocument.Links.Last.Should().Be(HostPrefix + route);
responseDocument.Links.Prev.Should().BeNull();
responseDocument.Links.Next.Should().BeNull();

string paintingLink = HostPrefix + $"/iis-application-virtual-directory/custom/path/to/paintings/{painting.StringId}";

responseDocument.ManyData.Should().HaveCount(1);
responseDocument.ManyData[0].Links.Self.Should().Be(paintingLink);
responseDocument.ManyData[0].Relationships["exposedAt"].Links.Self.Should().Be(paintingLink + "/relationships/exposedAt");
responseDocument.ManyData[0].Relationships["exposedAt"].Links.Related.Should().Be(paintingLink + "/exposedAt");

// TODO: The next link is wrong: it should not use the custom route.
string galleryLink = HostPrefix + $"/iis-application-virtual-directory/custom/path/to/artGalleries/{painting.ExposedAt.StringId}";

responseDocument.Included.Should().HaveCount(1);
responseDocument.Included[0].Links.Self.Should().Be(galleryLink);
responseDocument.Included[0].Relationships["paintings"].Links.Self.Should().Be(galleryLink + "/relationships/paintings");
responseDocument.Included[0].Relationships["paintings"].Links.Related.Should().Be(galleryLink + "/paintings");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
public sealed class Painting : Identifiable
{
[Attr]
public string Title { get; set; }

[HasOne]
public ArtGallery ExposedAt { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Controllers.Annotations;
using JsonApiDotNetCore.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace JsonApiDotNetCoreExampleTests.IntegrationTests.HostingInIIS
{
[DisableRoutingConvention, Route("custom/path/to/paintings")]
public sealed class PaintingsController : JsonApiController<Painting>
{
public PaintingsController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<Painting> resourceService)
: base(options, loggerFactory, resourceService)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ protected override void SetJsonApiOptions(JsonApiOptions options)
{
base.SetJsonApiOptions(options);

options.IncludeExceptionStackTraceInErrors = true;
options.Namespace = "public-api";
options.UseRelativeLinks = true;
options.IncludeTotalResourceCount = true;
Expand Down