From 8524da6e69b936d3dab32a98f9fe0ab258dc4736 Mon Sep 17 00:00:00 2001
From: Harro van der Kroft
Date: Tue, 26 Nov 2019 13:19:24 +0100
Subject: [PATCH 1/8] docs: remove old index.md, add/restructure main readme.md
---
README.md | 40 +++++++++++++++++++++-------------
src/JsonApiDotNetCore/index.md | 4 ----
2 files changed, 25 insertions(+), 19 deletions(-)
delete mode 100644 src/JsonApiDotNetCore/index.md
diff --git a/README.md b/README.md
index 3c91c6492e..866c88f48a 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,25 @@
-# JSON API .Net Core
+# JSON API .Net Core
-[](https://ci.appveyor.com/project/jaredcnance/jsonapidotnetcore)
-[](https://travis-ci.org/json-api-dotnet/JsonApiDotNetCore)
-[](https://www.nuget.org/packages/JsonApiDotNetCore/)
-[](https://gitter.im/json-api-dotnet-core/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[](http://www.firsttimersonly.com/)
+[](https://ci.appveyor.com/project/jaredcnance/jsonapidotnetcore) [](https://travis-ci.org/json-api-dotnet/JsonApiDotNetCore) [](https://www.nuget.org/packages/JsonApiDotNetCore/) [](https://gitter.im/json-api-dotnet-core/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](http://www.firsttimersonly.com/)
A framework for building [json:api](http://jsonapi.org/) compliant web APIs. The ultimate goal of this library is to eliminate as much boilerplate as possible by offering out-of-the-box features such as sorting, filtering and pagination. You just need to focus on defining the resources and implementing your custom business logic. This library has been designed around dependency injection making extensibility incredibly easy.
+## Table of Contents
+- [Getting Started](#getting-started)
+- [Related Projects](#related-projects)
+- [Examples](#examples)
+- [Compatibility](#compatibility)
+- [Installation And Usage](#installation-and-usage)
+ - [Models](#models)
+ - [Controllers](#controllers)
+ - [Middleware](#middleware)
+- [Development](#development)
+ - [Testing](#testing)
+ - [Cleaning](#cleaning)
+
## Getting Started
These are some steps you can take to help you understand what this project is and how you can use it:
@@ -34,6 +43,15 @@ These are some steps you can take to help you understand what this project is an
See the [examples](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/master/src/Examples) directory for up-to-date sample applications. There is also a [Todo List App](https://github.com/json-api-dotnet/TodoListExample) that includes a JADNC API and an EmberJs client.
+## Compatibility
+
+A lot of changes were introduced in v4.0.0, the following chart should help you with compatibility issues between .NET Core versions
+
+| .NET Core Version | JADNC Version |
+| ----------------- | ------------- |
+| 2.* | v3.* |
+| 3.* | v4.* |
+
## Installation And Usage
See [the documentation](https://json-api-dotnet.github.io/#/) for detailed usage.
@@ -79,7 +97,7 @@ public class Startup
}
```
-### Development
+## Development
Restore all NuGet packages with:
@@ -109,13 +127,5 @@ Sometimes the compiled files can be dirty / corrupt from other branches / failed
dotnet clean
```
-## Compatibility
-
-A lot of changes were introduced in v4.0.0, the following chart should help you with compatibility issues between .NET Core versions
-
-| .NET Core Version | JADNC Version |
-| ----------------- | ------------- |
-| 2.* | v3.* |
-| 3.* | v4.* |
diff --git a/src/JsonApiDotNetCore/index.md b/src/JsonApiDotNetCore/index.md
deleted file mode 100644
index 3ae2506361..0000000000
--- a/src/JsonApiDotNetCore/index.md
+++ /dev/null
@@ -1,4 +0,0 @@
-# This is the **HOMEPAGE**.
-Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files.
-## Quick Start Notes:
-1. Add images to the *images* folder if the file is referencing an image.
From 94c1c6abd41617a1eff3639eebffe6fc21f8ec58 Mon Sep 17 00:00:00 2001
From: Harro van der Kroft
Date: Wed, 27 Nov 2019 14:45:26 +0100
Subject: [PATCH 2/8] feat: add test for 404
---
.../Spec/NonExistentResourceTests.cs | 79 +++++++++++++++++++
1 file changed, 79 insertions(+)
create mode 100644 test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
new file mode 100644
index 0000000000..7e7f6da5ce
--- /dev/null
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Bogus;
+using JsonApiDotNetCore.Models;
+using JsonApiDotNetCoreExample;
+using JsonApiDotNetCoreExample.Data;
+using JsonApiDotNetCoreExample.Models;
+using Newtonsoft.Json;
+using Xunit;
+using Person = JsonApiDotNetCoreExample.Models.Person;
+
+namespace JsonApiDotNetCoreExampleTests.Acceptance.Spec
+{
+ [Collection("WebHostCollection")]
+ public class NonExistentResourceTests
+ {
+ private TestFixture _fixture;
+ private Faker _todoItemFaker;
+ private readonly Faker _personFaker;
+
+ public NonExistentResourceTests(TestFixture fixture)
+ {
+ _fixture = fixture;
+ _todoItemFaker = new Faker()
+ .RuleFor(t => t.Description, f => f.Lorem.Sentence())
+ .RuleFor(t => t.Ordinal, f => f.Random.Number())
+ .RuleFor(t => t.CreatedDate, f => f.Date.Past());
+
+ _personFaker = new Faker()
+ .RuleFor(p => p.FirstName, f => f.Name.FirstName())
+ .RuleFor(p => p.LastName, f => f.Name.LastName());
+ }
+
+ public class ErrorInnerMessage
+ {
+ [JsonProperty("title")]
+ public string Title;
+ [JsonProperty("status")]
+ public string Status;
+ }
+ public class ErrorMessage
+ {
+ [JsonProperty("errors")]
+ public List Errors;
+ }
+ [Fact]
+ public async Task Resource_UserNonExistent_ShouldReturn404WithCorrectError()
+ {
+ // Arrange
+ var context = _fixture.GetService();
+ context.TodoItems.RemoveRange(context.TodoItems.ToList());
+ var todoItem = _todoItemFaker.Generate();
+ context.TodoItems.Add(todoItem);
+ await context.SaveChangesAsync();
+ var nonExistentId = todoItem.Id;
+ context.TodoItems.Remove(todoItem);
+ context.SaveChanges();
+
+ var httpMethod = new HttpMethod("GET");
+ var route = $"/api/v1/todoItems/{nonExistentId}";
+ var request = new HttpRequestMessage(httpMethod, route);
+
+ // Act
+ var response = await _fixture.Client.SendAsync(request);
+ var body = await response.Content.ReadAsStringAsync();
+
+ // Assert
+ var errorResult = JsonConvert.DeserializeObject(body);
+ var title = errorResult.Errors.First().Title;
+ Assert.Contains(title, "todoitem");
+ Assert.Contains(title, "found");
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+ }
+}
From 00ac03045682e8151b9d5d78e7d26c7e3c8c0f23 Mon Sep 17 00:00:00 2001
From: Harro van der Kroft
Date: Wed, 27 Nov 2019 14:52:24 +0100
Subject: [PATCH 3/8] chore: add hasMany and HasOne tests
---
.../Spec/NonExistentResourceTests.cs | 66 +++++++++++++++++--
1 file changed, 61 insertions(+), 5 deletions(-)
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
index 7e7f6da5ce..aa962e7f0c 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
@@ -49,6 +49,37 @@ public class ErrorMessage
}
[Fact]
public async Task Resource_UserNonExistent_ShouldReturn404WithCorrectError()
+ {
+ // Arrange
+ var context = _fixture.GetService();
+ context.People.RemoveRange(context.People.ToList());
+ var person = _personFaker.Generate();
+ context.People.Add(person);
+ await context.SaveChangesAsync();
+ var nonExistentId = person.Id;
+ context.People.Remove(person);
+ context.SaveChanges();
+
+ var httpMethod = HttpMethod.Get;
+ var route = $"/api/v1/people/{nonExistentId}";
+ var request = new HttpRequestMessage(httpMethod, route);
+
+ // Act
+ var response = await _fixture.Client.SendAsync(request);
+ var body = await response.Content.ReadAsStringAsync();
+
+ // Assert
+ var errorResult = JsonConvert.DeserializeObject(body);
+ var errorParsed = errorResult.Errors.First();
+ var title = errorParsed.Title;
+ var code = errorParsed.Status;
+ Assert.Contains(title, "person");
+ Assert.Contains(title, "found");
+ Assert.Equal("404", code);
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+ [Fact]
+ public async Task ResourceRelatedHasOne_TodoItemExistentOwnerIsNonExistent_ShouldReturn200WithNullData()
{
// Arrange
var context = _fixture.GetService();
@@ -56,12 +87,37 @@ public async Task Resource_UserNonExistent_ShouldReturn404WithCorrectError()
var todoItem = _todoItemFaker.Generate();
context.TodoItems.Add(todoItem);
await context.SaveChangesAsync();
- var nonExistentId = todoItem.Id;
- context.TodoItems.Remove(todoItem);
- context.SaveChanges();
+ var existingId = todoItem.Id;
+
+ var httpMethod = HttpMethod.Get;
+ var route = $"/api/v1/todoItems/{existingId}/people";
+ var request = new HttpRequestMessage(httpMethod, route);
+
+ // Act
+ var response = await _fixture.Client.SendAsync(request);
+ var body = await response.Content.ReadAsStringAsync();
+
+ // Assert
+ var errorResult = JsonConvert.DeserializeObject(body);
+ var title = errorResult.Errors.First().Title;
+ Assert.Contains(title, "todoitem");
+ Assert.Contains(title, "found");
+ Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
+ }
+
+ [Fact]
+ public async Task ResourceRelatedHasMany_PersonExistsToDoItemDoesNot_ShouldReturn200WithNullData()
+ {
+ // Arrange
+ var context = _fixture.GetService();
+ context.TodoItems.RemoveRange(context.TodoItems.ToList());
+ var todoItem = _todoItemFaker.Generate();
+ context.TodoItems.Add(todoItem);
+ await context.SaveChangesAsync();
+ var existingId = todoItem.Id;
- var httpMethod = new HttpMethod("GET");
- var route = $"/api/v1/todoItems/{nonExistentId}";
+ var httpMethod = HttpMethod.Get;
+ var route = $"/api/v1/todoItems/{existingId}/people";
var request = new HttpRequestMessage(httpMethod, route);
// Act
From b499d9c31ef5d7736809683dd276cfed8f251fe0 Mon Sep 17 00:00:00 2001
From: Harro van der Kroft
Date: Wed, 27 Nov 2019 16:07:00 +0100
Subject: [PATCH 4/8] feat: add 404 with correct type for non-found single
resource
---
.../Controllers/BaseJsonApiController.cs | 19 +++---
.../Formatters/JsonApiWriter.cs | 58 +++++++++++++------
.../Exceptions/ResourceNotFoundException.cs | 13 +++++
.../Middleware/DefaultExceptionFilter.cs | 10 ++--
.../Spec/NonExistentResourceTests.cs | 13 ++---
5 files changed, 77 insertions(+), 36 deletions(-)
create mode 100644 src/JsonApiDotNetCore/Internal/Exceptions/ResourceNotFoundException.cs
diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
index c241c5b81d..09724375b8 100644
--- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
+++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
@@ -101,18 +101,25 @@ public virtual async Task GetAsync()
public virtual async Task GetAsync(TId id)
{
- if (_getById == null) throw Exceptions.UnSupportedRequestMethod;
+ if (_getById == null) {
+ throw Exceptions.UnSupportedRequestMethod;
+ }
var entity = await _getById.GetAsync(id);
if (entity == null)
{
- // remove the null argument as soon as this has been resolved:
- // https://github.com/aspnet/AspNetCore/issues/16969
- return NotFound(null);
+ return NoResultFound();
}
return Ok(entity);
}
+ private NotFoundObjectResult NoResultFound()
+ {
+ // remove the null argument as soon as this has been resolved:
+ // https://github.com/aspnet/AspNetCore/issues/16969
+ return NotFound(null);
+ }
+
public virtual async Task GetRelationshipsAsync(TId id, string relationshipName)
{
if (_getRelationships == null)
@@ -120,9 +127,7 @@ public virtual async Task GetRelationshipsAsync(TId id, string re
var relationship = await _getRelationships.GetRelationshipsAsync(id, relationshipName);
if (relationship == null)
{
- // remove the null argument as soon as this has been resolved:
- // https://github.com/aspnet/AspNetCore/issues/16969
- return NotFound(null);
+ return NoResultFound();
}
return Ok(relationship);
diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs b/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
index aa8b779992..dbfc22f0d3 100644
--- a/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
+++ b/src/JsonApiDotNetCore/Formatters/JsonApiWriter.cs
@@ -2,6 +2,8 @@
using System.Text;
using System.Threading.Tasks;
using JsonApiDotNetCore.Internal;
+using JsonApiDotNetCore.Internal.Contracts;
+using JsonApiDotNetCore.Managers.Contracts;
using JsonApiDotNetCore.Serialization.Server;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
@@ -18,11 +20,14 @@ namespace JsonApiDotNetCore.Formatters
public class JsonApiWriter : IJsonApiWriter
{
private readonly ILogger _logger;
+ private readonly ICurrentRequest _currentRequest;
private readonly IJsonApiSerializer _serializer;
public JsonApiWriter(IJsonApiSerializer serializer,
- ILoggerFactory loggerFactory)
+ ILoggerFactory loggerFactory,
+ ICurrentRequest currentRequest)
{
+ _currentRequest = currentRequest;
_serializer = serializer;
_logger = loggerFactory.CreateLogger();
}
@@ -30,40 +35,57 @@ public JsonApiWriter(IJsonApiSerializer serializer,
public async Task WriteAsync(OutputFormatterWriteContext context)
{
if (context == null)
+ {
throw new ArgumentNullException(nameof(context));
+ }
var response = context.HttpContext.Response;
using var writer = context.WriterFactory(response.Body, Encoding.UTF8);
string responseContent;
- if (_serializer == null)
+ if (response.StatusCode == 404)
{
- responseContent = JsonConvert.SerializeObject(context.Object);
+ var requestedModel = _currentRequest.GetRequestResource();
+ var errors = new ErrorCollection();
+ errors.Add(new Error(404, $"The resource with type '{requestedModel.ResourceName}' and id 'unknown' could not be found"));
+ responseContent = _serializer.Serialize(errors);
+ response.StatusCode = 404;
}
else
{
- response.ContentType = Constants.ContentType;
- try
+
+ if (_serializer == null)
{
- if (context.Object is ProblemDetails pd)
+ responseContent = JsonConvert.SerializeObject(context.Object);
+ }
+ else
+ {
+ response.ContentType = Constants.ContentType;
+ try
+ {
+ if (context.Object is ProblemDetails pd)
+ {
+ var errors = new ErrorCollection();
+ errors.Add(new Error(pd.Status.Value, pd.Title, pd.Detail));
+ responseContent = _serializer.Serialize(errors);
+
+ }
+ else
+ {
+ responseContent = _serializer.Serialize(context.Object);
+ }
+ }
+ catch (Exception e)
{
+ _logger?.LogError(new EventId(), e, "An error ocurred while formatting the response");
var errors = new ErrorCollection();
- errors.Add(new Error(pd.Status.Value, pd.Title, pd.Detail));
+ errors.Add(new Error(500, e.Message, ErrorMeta.FromException(e)));
responseContent = _serializer.Serialize(errors);
- } else
- {
- responseContent = _serializer.Serialize(context.Object);
+ response.StatusCode = 500;
}
}
- catch (Exception e)
- {
- _logger?.LogError(new EventId(), e, "An error ocurred while formatting the response");
- var errors = new ErrorCollection();
- errors.Add(new Error(500, e.Message, ErrorMeta.FromException(e)));
- responseContent = _serializer.Serialize(errors);
- response.StatusCode = 500;
- }
}
+
await writer.WriteAsync(responseContent);
await writer.FlushAsync();
}
diff --git a/src/JsonApiDotNetCore/Internal/Exceptions/ResourceNotFoundException.cs b/src/JsonApiDotNetCore/Internal/Exceptions/ResourceNotFoundException.cs
new file mode 100644
index 0000000000..6f72cfc9b5
--- /dev/null
+++ b/src/JsonApiDotNetCore/Internal/Exceptions/ResourceNotFoundException.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace JsonApiDotNetCore.Internal
+{
+ public class ResourceNotFoundException : Exception
+ {
+ private readonly ErrorCollection _errors = new ErrorCollection();
+
+ public ResourceNotFoundException()
+ { }
+
+ }
+}
diff --git a/src/JsonApiDotNetCore/Middleware/DefaultExceptionFilter.cs b/src/JsonApiDotNetCore/Middleware/DefaultExceptionFilter.cs
index b6c82b27e3..3ed2da84a5 100644
--- a/src/JsonApiDotNetCore/Middleware/DefaultExceptionFilter.cs
+++ b/src/JsonApiDotNetCore/Middleware/DefaultExceptionFilter.cs
@@ -1,7 +1,9 @@
-using JsonApiDotNetCore.Internal;
+using JsonApiDotNetCore.Internal;
+using JsonApiDotNetCore.Managers.Contracts;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
+using System.Collections.Generic;
namespace JsonApiDotNetCore.Middleware
{
@@ -10,19 +12,19 @@ namespace JsonApiDotNetCore.Middleware
///
public class DefaultExceptionFilter : ActionFilterAttribute, IExceptionFilter
{
+ private readonly ICurrentRequest _currentRequest;
private readonly ILogger _logger;
- public DefaultExceptionFilter(ILoggerFactory loggerFactory)
+ public DefaultExceptionFilter(ILoggerFactory loggerFactory, ICurrentRequest currentRequest)
{
+ _currentRequest = currentRequest;
_logger = loggerFactory.CreateLogger();
}
public void OnException(ExceptionContext context)
{
_logger?.LogError(new EventId(), context.Exception, "An unhandled exception occurred during the request");
-
var jsonApiException = JsonApiExceptionFactory.GetException(context.Exception);
-
var error = jsonApiException.GetError();
var result = new ObjectResult(error)
{
diff --git a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
index aa962e7f0c..a9ab8be4f9 100644
--- a/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
+++ b/test/JsonApiDotNetCoreExampleTests/Acceptance/Spec/NonExistentResourceTests.cs
@@ -48,20 +48,19 @@ public class ErrorMessage
public List Errors;
}
[Fact]
- public async Task Resource_UserNonExistent_ShouldReturn404WithCorrectError()
+ public async Task Resource_PersonNonExistent_ShouldReturn404WithCorrectError()
{
// Arrange
var context = _fixture.GetService();
- context.People.RemoveRange(context.People.ToList());
var person = _personFaker.Generate();
context.People.Add(person);
await context.SaveChangesAsync();
- var nonExistentId = person.Id;
+ var nonExistingId = person.Id;
context.People.Remove(person);
context.SaveChanges();
var httpMethod = HttpMethod.Get;
- var route = $"/api/v1/people/{nonExistentId}";
+ var route = $"/api/v1/people/{nonExistingId}";
var request = new HttpRequestMessage(httpMethod, route);
// Act
@@ -73,8 +72,9 @@ public async Task Resource_UserNonExistent_ShouldReturn404WithCorrectError()
var errorParsed = errorResult.Errors.First();
var title = errorParsed.Title;
var code = errorParsed.Status;
- Assert.Contains(title, "person");
- Assert.Contains(title, "found");
+ Assert.Contains("found", title);
+ Assert.Contains("people", title);
+ Assert.Contains(nonExistingId.ToString(), title);
Assert.Equal("404", code);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
@@ -100,7 +100,6 @@ public async Task ResourceRelatedHasOne_TodoItemExistentOwnerIsNonExistent_Shoul
// Assert
var errorResult = JsonConvert.DeserializeObject(body);
var title = errorResult.Errors.First().Title;
- Assert.Contains(title, "todoitem");
Assert.Contains(title, "found");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
From d12d1c2314569fe7419178a9aae992241e5ba752 Mon Sep 17 00:00:00 2001
From: Harro van der Kroft
Date: Mon, 23 Dec 2019 16:46:20 +0100
Subject: [PATCH 5/8] feat: working tests, fixed ID not being present in error
---
.../Controllers/BaseJsonApiController.cs | 45 +++++++++++-----
.../Formatters/JsonApiWriter.cs | 4 +-
.../Acceptance/Spec/EndToEndTest.cs | 4 +-
.../Spec/NonExistentResourceTests.cs | 53 +++++++++----------
4 files changed, 60 insertions(+), 46 deletions(-)
diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
index 09724375b8..7453f81f19 100644
--- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
+++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Extensions;
@@ -23,7 +25,7 @@ public class BaseJsonApiController
private readonly IDeleteService _delete;
private readonly ILogger> _logger;
private readonly IJsonApiOptions _jsonApiOptions;
-
+
public BaseJsonApiController(
IJsonApiOptions jsonApiOptions,
IResourceService resourceService,
@@ -101,7 +103,8 @@ public virtual async Task GetAsync()
public virtual async Task GetAsync(TId id)
{
- if (_getById == null) {
+ if (_getById == null)
+ {
throw Exceptions.UnSupportedRequestMethod;
}
var entity = await _getById.GetAsync(id);
@@ -122,21 +125,37 @@ private NotFoundObjectResult NoResultFound()
public virtual async Task GetRelationshipsAsync(TId id, string relationshipName)
{
- if (_getRelationships == null)
- throw Exceptions.UnSupportedRequestMethod;
- var relationship = await _getRelationships.GetRelationshipsAsync(id, relationshipName);
- if (relationship == null)
- {
- return NoResultFound();
- }
-
- return Ok(relationship);
+ return await GetRelationshipInternal(id, relationshipName, relationshipInUrl: true);
}
public virtual async Task GetRelationshipAsync(TId id, string relationshipName)
{
- if (_getRelationship == null) throw Exceptions.UnSupportedRequestMethod;
- var relationship = await _getRelationship.GetRelationshipAsync(id, relationshipName);
+ return await GetRelationshipInternal(id, relationshipName, relationshipInUrl: false);
+ }
+
+ protected virtual async Task GetRelationshipInternal(TId id, string relationshipName, bool relationshipInUrl)
+ {
+ if (_getRelationship == null)
+ {
+ throw Exceptions.UnSupportedRequestMethod;
+ }
+ object relationship;
+ if (relationshipInUrl)
+ {
+ relationship = await _getRelationship.GetRelationshipAsync(id, relationshipName);
+ }
+ else
+ {
+ relationship = await _getRelationship.GetRelationshipAsync(id, relationshipName);
+ }
+ if(relationship == null)
+ {
+ return Ok(null);
+ }
+ if (((IEnumerable