Skip to content

Commit ebffd2b

Browse files
committed
feat(*): add Error and exception handling
1 parent 0ff8489 commit ebffd2b

File tree

9 files changed

+140
-18
lines changed

9 files changed

+140
-18
lines changed

src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public Document Build(IIdentifiable entity)
3434
public Documents Build(IEnumerable<IIdentifiable> entities)
3535
{
3636
var entityType = entities
37-
.GetType()
38-
.GenericTypeArguments[0];
39-
37+
.GetType()
38+
.GenericTypeArguments[0];
39+
4040
var contextEntity = _contextGraph.GetContextEntity(entityType);
4141

4242
var documents = new Documents
@@ -47,7 +47,7 @@ public Documents Build(IEnumerable<IIdentifiable> entities)
4747
foreach (var entity in entities)
4848
documents.Data.Add(_getData(contextEntity, entity));
4949

50-
return documents;
50+
return documents;
5151
}
5252

5353
private DocumentData _getData(ContextEntity contextEntity, IIdentifiable entity)
@@ -89,7 +89,7 @@ private void _addRelationships(DocumentData data, ContextEntity contextEntity, I
8989
}
9090
};
9191

92-
if (_jsonApiContext.IncludedRelationships.Contains(r.RelationshipName.ToProperCase()))
92+
if (_hasRelationship(r.RelationshipName))
9393
{
9494
var navigationEntity = _jsonApiContext.ContextGraph
9595
.GetRelationship(entity, r.RelationshipName);
@@ -105,6 +105,13 @@ private void _addRelationships(DocumentData data, ContextEntity contextEntity, I
105105
data.Relationships.Add(r.RelationshipName.Dasherize(), relationshipData);
106106
});
107107
}
108+
109+
private bool _hasRelationship(string relationshipName)
110+
{
111+
return _jsonApiContext.IncludedRelationships != null &&
112+
_jsonApiContext.IncludedRelationships.Contains(relationshipName.ToProperCase());
113+
}
114+
108115
private List<Dictionary<string, string>> GetRelationships(IEnumerable<object> entities, string relationshipName)
109116
{
110117
var objType = entities.GetType().GenericTypeArguments[0];

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Threading.Tasks;
55
using JsonApiDotNetCore.Extensions;
6+
using JsonApiDotNetCore.Internal;
67
using JsonApiDotNetCore.Internal.Query;
78
using JsonApiDotNetCore.Models;
89
using JsonApiDotNetCore.Services;
@@ -124,7 +125,12 @@ public virtual async Task<bool> DeleteAsync(TId id)
124125

125126
public IQueryable<TEntity> Include(IQueryable<TEntity> entities, string relationshipName)
126127
{
127-
return entities.Include(relationshipName);
128+
var entity = _jsonApiContext.RequestEntity;
129+
if(entity.Relationships.Any(r => r.RelationshipName == relationshipName))
130+
return entities.Include(relationshipName);
131+
132+
throw new JsonApiException("400", "Invalid relationship",
133+
$"{entity.EntityName} does not have a relationship named {relationshipName}");
128134
}
129135
}
130136
}

src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public static class IApplicationBuilderExtensions
77
public static IApplicationBuilder UseJsonApi(this IApplicationBuilder app)
88
{
99
app.UseMvc();
10+
1011
return app;
1112
}
1213
}

src/JsonApiDotNetCore/Extensions/ServiceProviderExtensions.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ private static void _addInternals<TContext>(IServiceCollection services, JsonApi
3232
{
3333
services.AddJsonApiInternals<TContext>();
3434
services.AddMvc()
35-
.AddMvcOptions(opt => opt.SerializeAsJsonApi(jsonApiOptions));
35+
.AddMvcOptions(opt => {
36+
opt.Filters.Add(typeof(JsonApiExceptionFilter));
37+
opt.SerializeAsJsonApi(jsonApiOptions);
38+
});
3639
}
3740

3841
public static void AddJsonApiInternals<TContext>(this IServiceCollection services)

src/JsonApiDotNetCore/Formatters/JsonApiOutputFormatter.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
using System;
2+
using System.Linq;
23
using System.Text;
34
using System.Threading.Tasks;
5+
using JsonApiDotNetCore.Internal;
46
using JsonApiDotNetCore.Serialization;
57
using JsonApiDotNetCore.Services;
68
using Microsoft.AspNetCore.Mvc.Formatters;
79
using Microsoft.Extensions.DependencyInjection;
10+
using Newtonsoft.Json;
811

912
namespace JsonApiDotNetCore.Formatters
1013
{
@@ -31,11 +34,22 @@ public async Task WriteAsync(OutputFormatterWriteContext context)
3134
{
3235
var jsonApiContext = context.HttpContext.RequestServices.GetService<IJsonApiContext>();
3336

34-
var responseContent = JsonApiSerializer.Serialize(context.Object, jsonApiContext);
37+
string responseContent;
38+
try
39+
{
40+
if(context.Object.GetType() == typeof(Error))
41+
responseContent = JsonConvert.SerializeObject(context.Object);
42+
else
43+
responseContent = JsonApiSerializer.Serialize(context.Object, jsonApiContext);
44+
}
45+
catch(Exception e)
46+
{
47+
responseContent = new Error("400", e.Message).GetJson();
48+
response.StatusCode = 400;
49+
}
3550

3651
await writer.WriteAsync(responseContent);
37-
38-
await writer.FlushAsync();
52+
await writer.FlushAsync();
3953
}
4054
}
4155
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using Newtonsoft.Json;
2+
3+
namespace JsonApiDotNetCore.Internal
4+
{
5+
public class Error
6+
{
7+
public Error(string status, string title)
8+
{
9+
Status = status;
10+
Title = title;
11+
}
12+
13+
public Error(string status, string title, string detail)
14+
{
15+
Status = status;
16+
Title = title;
17+
Detail = detail;
18+
}
19+
20+
[JsonProperty("title")]
21+
public string Title { get; set; }
22+
23+
[JsonProperty("detail")]
24+
public string Detail { get; set; }
25+
26+
[JsonProperty("status")]
27+
public string Status { get; set; }
28+
29+
public string GetJson()
30+
{
31+
return JsonConvert.SerializeObject(this, new JsonSerializerSettings {
32+
NullValueHandling = NullValueHandling.Ignore
33+
});
34+
}
35+
}
36+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
3+
namespace JsonApiDotNetCore.Internal
4+
{
5+
public class JsonApiException : Exception
6+
{
7+
private string _statusCode;
8+
private string _detail;
9+
private string _message;
10+
11+
public JsonApiException(string statusCode, string message)
12+
: base(message)
13+
{
14+
_statusCode = statusCode;
15+
_message = message;
16+
}
17+
18+
public JsonApiException(string statusCode, string message, string detail)
19+
: base(message)
20+
{
21+
_statusCode = statusCode;
22+
_message = message;
23+
_detail = detail;
24+
}
25+
26+
public Error GetError()
27+
{
28+
return new Error(_statusCode, _message, _detail);
29+
}
30+
}
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using JsonApiDotNetCore.Internal;
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.AspNetCore.Mvc.Filters;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace JsonApiDotNetCore.Formatters
8+
{
9+
public class JsonApiExceptionFilter : ActionFilterAttribute, IExceptionFilter
10+
{
11+
private readonly ILogger _logger;
12+
13+
public JsonApiExceptionFilter(ILoggerFactory loggerFactory)
14+
{
15+
_logger = loggerFactory.CreateLogger<JsonApiExceptionFilter>();
16+
}
17+
18+
public void OnException(ExceptionContext context)
19+
{
20+
_logger.LogError(context.Exception.Message);
21+
22+
var jsonApiException = (JsonApiException)context.Exception;
23+
if(jsonApiException != null)
24+
{
25+
var error = jsonApiException.GetError();
26+
var result = new ObjectResult(error);
27+
result.StatusCode = Convert.ToInt16(error.Status);
28+
context.Result = result;
29+
}
30+
}
31+
}
32+
}

src/JsonApiDotNetCore/Models/Inclusion.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)