Skip to content

Commit a8f41aa

Browse files
authored
Merge pull request #98 from Research-Institute/fix/routing-convention
Fix/routing convention
2 parents 11450e5 + d93c625 commit a8f41aa

File tree

56 files changed

+326
-346
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+326
-346
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
\.vs/
2+
3+
*.user

JsonApiDotnetCore.sln

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ EndProject
1515
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C5B4D998-CECB-454D-9F32-085A897577BE}"
1616
ProjectSection(SolutionItems) = preProject
1717
.gitignore = .gitignore
18+
README.md = README.md
1819
EndProjectSection
1920
EndProject
2021
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoEntityFrameworkExample", "src\NoEntityFrameworkExample\NoEntityFrameworkExample.csproj", "{570165EC-62B5-4684-A139-8D2A30DD4475}"

README.md

+19-2
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,10 @@ services.AddJsonApi<AppDbContext>(
248248
#### Disable Convention
249249

250250
You can disable the dasherized convention and specify your own template
251-
by using the `DisableRoutingConvention` Attribute:
251+
by using the `DisableRoutingConvention` Attribute.
252252

253253
```csharp
254+
[Route("[controller]")]
254255
[DisableRoutingConvention]
255256
public class CamelCasedModelsController : JsonApiController<CamelCasedModel>
256257
{
@@ -263,6 +264,22 @@ public class CamelCasedModelsController : JsonApiController<CamelCasedModel>
263264
}
264265
```
265266

267+
It is important to note that your routes *must* still end with the model name in the same format
268+
as the resource name. This is so that we can build accurrate resource links in the json:api document.
269+
For example, if you define a resource as `MyModels` the controller route must match:
270+
271+
```csharp
272+
// resource definition
273+
builder.AddResource<TodoItem>("myModels");
274+
275+
// controller definition
276+
[Route("api/myModels")]
277+
[DisableRoutingConvention]
278+
public class TodoItemsController : JsonApiController<TodoItem>
279+
{ //...
280+
}
281+
```
282+
266283
### Defining Custom Data Access Methods
267284

268285
By default, data retrieval is distributed across 3 layers:
@@ -287,7 +304,7 @@ public void ConfigureServices(IServiceCollection services)
287304
services.AddJsonApi(options => {
288305
options.Namespace = "api/v1";
289306
options.BuildContextGraph((builder) => {
290-
builder.AddResource<MyModel>("my-models");
307+
builder.AddResource<MyModel>("my-models");1
291308
});
292309
}, mvcBuilder);
293310
// ...

src/JsonApiDotNetCore/Builders/ContextGraphBuilder.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ namespace JsonApiDotNetCore.Builders
1010
{
1111
public class ContextGraphBuilder : IContextGraphBuilder
1212
{
13-
private List<ContextEntity> Entities;
13+
private List<ContextEntity> _entities;
1414
private bool _usesDbContext;
1515
public ContextGraphBuilder()
1616
{
17-
Entities = new List<ContextEntity>();
17+
_entities = new List<ContextEntity>();
1818
}
1919

2020
public IContextGraph Build()
2121
{
2222
var graph = new ContextGraph()
2323
{
24-
Entities = Entities,
24+
Entities = _entities,
2525
UsesDbContext = _usesDbContext
2626
};
2727
return graph;
@@ -30,7 +30,7 @@ public IContextGraph Build()
3030
public void AddResource<TResource>(string pluralizedTypeName) where TResource : class
3131
{
3232
var entityType = typeof(TResource);
33-
Entities.Add(new ContextEntity
33+
_entities.Add(new ContextEntity
3434
{
3535
EntityName = pluralizedTypeName,
3636
EntityType = entityType,
@@ -108,7 +108,7 @@ public void AddDbContext<T>() where T : DbContext
108108
}
109109
}
110110

111-
Entities = entities;
111+
_entities = entities;
112112
}
113113

114114
private string GetResourceName(PropertyInfo property)

src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

+12-11
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace JsonApiDotNetCore.Builders
99
{
1010
public class DocumentBuilder : IDocumentBuilder
1111
{
12-
private IJsonApiContext _jsonApiContext;
13-
private IContextGraph _contextGraph;
12+
private readonly IJsonApiContext _jsonApiContext;
13+
private readonly IContextGraph _contextGraph;
1414
private readonly IRequestMeta _requestMeta;
1515

1616
public DocumentBuilder(IJsonApiContext jsonApiContext)
@@ -50,14 +50,15 @@ public Documents Build(IEnumerable<IIdentifiable> entities)
5050

5151
var contextEntity = _contextGraph.GetContextEntity(entityType);
5252

53+
var enumeratedEntities = entities as IList<IIdentifiable> ?? entities.ToList();
5354
var documents = new Documents
5455
{
5556
Data = new List<DocumentData>(),
56-
Meta = GetMeta(entities.FirstOrDefault()),
57+
Meta = GetMeta(enumeratedEntities.FirstOrDefault()),
5758
Links = _jsonApiContext.PageManager.GetPageLinks(new LinkBuilder(_jsonApiContext))
5859
};
5960

60-
foreach (var entity in entities)
61+
foreach (var entity in enumeratedEntities)
6162
{
6263
documents.Data.Add(GetData(contextEntity, entity));
6364
documents.Included = AppendIncludedObject(documents.Included, contextEntity, entity);
@@ -155,9 +156,9 @@ private void AddRelationships(DocumentData data, ContextEntity contextEntity, II
155156
if(navigationEntity == null)
156157
relationshipData.SingleData = null;
157158
else if (navigationEntity is IEnumerable)
158-
relationshipData.ManyData = GetRelationships((IEnumerable<object>)navigationEntity, r.InternalRelationshipName);
159+
relationshipData.ManyData = GetRelationships((IEnumerable<object>)navigationEntity);
159160
else
160-
relationshipData.SingleData = GetRelationship(navigationEntity, r.InternalRelationshipName);
161+
relationshipData.SingleData = GetRelationship(navigationEntity);
161162
}
162163

163164
data.Relationships.Add(r.PublicRelationshipName, relationshipData);
@@ -174,9 +175,9 @@ private List<DocumentData> GetIncludedEntities(ContextEntity contextEntity, IIde
174175

175176
var navigationEntity = _jsonApiContext.ContextGraph.GetRelationship(entity, r.InternalRelationshipName);
176177

177-
if (navigationEntity is IEnumerable)
178-
foreach (var includedEntity in (IEnumerable)navigationEntity)
179-
AddIncludedEntity(included, (IIdentifiable)includedEntity);
178+
if (navigationEntity is IEnumerable hasManyNavigationEntity)
179+
foreach (IIdentifiable includedEntity in hasManyNavigationEntity)
180+
AddIncludedEntity(included, includedEntity);
180181
else
181182
AddIncludedEntity(included, (IIdentifiable)navigationEntity);
182183
});
@@ -216,7 +217,7 @@ private bool RelationshipIsIncluded(string relationshipName)
216217
_jsonApiContext.IncludedRelationships.Contains(relationshipName);
217218
}
218219

219-
private List<Dictionary<string, string>> GetRelationships(IEnumerable<object> entities, string relationshipName)
220+
private List<Dictionary<string, string>> GetRelationships(IEnumerable<object> entities)
220221
{
221222
var objType = entities.GetType().GenericTypeArguments[0];
222223

@@ -232,7 +233,7 @@ private List<Dictionary<string, string>> GetRelationships(IEnumerable<object> en
232233
}
233234
return relationships;
234235
}
235-
private Dictionary<string, string> GetRelationship(object entity, string relationshipName)
236+
private Dictionary<string, string> GetRelationship(object entity)
236237
{
237238
var objType = entity.GetType();
238239

src/JsonApiDotNetCore/Builders/LinkBuilder.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
using JsonApiDotNetCore.Extensions;
21
using JsonApiDotNetCore.Services;
32
using Microsoft.AspNetCore.Http;
43

54
namespace JsonApiDotNetCore.Builders
65
{
76
public class LinkBuilder
87
{
9-
IJsonApiContext _context;
8+
private readonly IJsonApiContext _context;
109

1110
public LinkBuilder(IJsonApiContext context)
1211
{

src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ public void BuildContextGraph<TContext>(Action<IContextGraphBuilder> builder)
2020

2121
contextGraphBuilder.AddDbContext<TContext>();
2222

23-
if(builder != null)
24-
builder(contextGraphBuilder);
23+
builder?.Invoke(contextGraphBuilder);
2524

2625
ContextGraph = contextGraphBuilder.Build();
2726
}

src/JsonApiDotNetCore/Controllers/JsonApiControllerMixin.cs

-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ namespace JsonApiDotNetCore.Controllers
44
{
55
public abstract class JsonApiControllerMixin : Controller
66
{
7-
protected JsonApiControllerMixin()
8-
{ }
9-
107
protected IActionResult UnprocessableEntity()
118
{
129
return new StatusCodeResult(422);

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading.Tasks;
44
using JsonApiDotNetCore.Extensions;
55
using JsonApiDotNetCore.Internal;
6+
using JsonApiDotNetCore.Internal.Generics;
67
using JsonApiDotNetCore.Internal.Query;
78
using JsonApiDotNetCore.Models;
89
using JsonApiDotNetCore.Services;
@@ -58,8 +59,8 @@ public virtual IQueryable<TEntity> Filter(IQueryable<TEntity> entities, FilterQ
5859

5960
if(filterQuery.IsAttributeOfRelationship)
6061
return entities.Filter(new RelatedAttrFilterQuery(_jsonApiContext, filterQuery));
61-
else
62-
return entities.Filter(new AttrFilterQuery(_jsonApiContext, filterQuery));
62+
63+
return entities.Filter(new AttrFilterQuery(_jsonApiContext, filterQuery));
6364
}
6465

6566
public virtual IQueryable<TEntity> Sort(IQueryable<TEntity> entities, List<SortQuery> sortQueries)
@@ -69,9 +70,10 @@ public virtual IQueryable<TEntity> Sort(IQueryable<TEntity> entities, List<SortQ
6970

7071
var orderedEntities = entities.Sort(sortQueries[0]);
7172

72-
if(sortQueries.Count() > 1)
73-
for(var i=1; i < sortQueries.Count(); i++)
74-
orderedEntities = orderedEntities.Sort(sortQueries[i]);
73+
if (sortQueries.Count <= 1) return orderedEntities;
74+
75+
for(var i=1; i < sortQueries.Count; i++)
76+
orderedEntities = orderedEntities.Sort(sortQueries[i]);
7577

7678
return orderedEntities;
7779
}

src/JsonApiDotNetCore/Data/IEntityRepository.cs

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq;
33
using System.Threading.Tasks;
4-
using JsonApiDotNetCore.Internal;
54
using JsonApiDotNetCore.Internal.Query;
65
using JsonApiDotNetCore.Models;
76

src/JsonApiDotNetCore/Extensions/IApplicationBuilderExtensions.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using JsonApiDotNetCore.Middleware;
22
using Microsoft.AspNetCore.Builder;
33

4-
namespace JsonApiDotNetCore.Routing
4+
namespace JsonApiDotNetCore.Extensions
55
{
6+
// ReSharper disable once InconsistentNaming
67
public static class IApplicationBuilderExtensions
78
{
89
public static IApplicationBuilder UseJsonApi(this IApplicationBuilder app, bool useMvc = true)

src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs

+10-12
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,21 @@
88

99
namespace JsonApiDotNetCore.Extensions
1010
{
11+
// ReSharper disable once InconsistentNaming
1112
public static class IQueryableExtensions
1213
{
1314
public static IOrderedQueryable<TSource> Sort<TSource>(this IQueryable<TSource> source, SortQuery sortQuery)
1415
{
15-
if (sortQuery.Direction == SortDirection.Descending)
16-
return source.OrderByDescending(sortQuery.SortedAttribute.InternalAttributeName);
17-
18-
return source.OrderBy(sortQuery.SortedAttribute.InternalAttributeName);
16+
return sortQuery.Direction == SortDirection.Descending
17+
? source.OrderByDescending(sortQuery.SortedAttribute.InternalAttributeName)
18+
: source.OrderBy(sortQuery.SortedAttribute.InternalAttributeName);
1919
}
2020

2121
public static IOrderedQueryable<TSource> Sort<TSource>(this IOrderedQueryable<TSource> source, SortQuery sortQuery)
2222
{
23-
if (sortQuery.Direction == SortDirection.Descending)
24-
return source.ThenByDescending(sortQuery.SortedAttribute.InternalAttributeName);
25-
26-
return source.ThenBy(sortQuery.SortedAttribute.InternalAttributeName);
23+
return sortQuery.Direction == SortDirection.Descending
24+
? source.ThenByDescending(sortQuery.SortedAttribute.InternalAttributeName)
25+
: source.ThenBy(sortQuery.SortedAttribute.InternalAttributeName);
2726
}
2827

2928
public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
@@ -178,10 +177,9 @@ private static Expression GetFilterExpressionLambda(Expression left, Expression
178177
return body;
179178
}
180179

181-
182-
public static IQueryable<TSource> Select<TSource>(this IQueryable<TSource> source, IEnumerable<string> columns)
180+
public static IQueryable<TSource> Select<TSource>(this IQueryable<TSource> source, List<string> columns)
183181
{
184-
if (columns == null || columns.Count() == 0)
182+
if (columns == null || columns.Any() == false)
185183
return source;
186184

187185
var sourceType = source.ElementType;
@@ -201,7 +199,7 @@ public static IQueryable<TSource> Select<TSource>(this IQueryable<TSource> sourc
201199
var selector = Expression.Lambda(body, parameter);
202200

203201
return source.Provider.CreateQuery<TSource>(
204-
Expression.Call(typeof(Queryable), "Select", new Type[] { sourceType, resultType },
202+
Expression.Call(typeof(Queryable), "Select", new[] { sourceType, resultType },
205203
source.Expression, Expression.Quote(selector)));
206204
}
207205
}

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using JsonApiDotNetCore.Data;
55
using JsonApiDotNetCore.Formatters;
66
using JsonApiDotNetCore.Internal;
7+
using JsonApiDotNetCore.Internal.Generics;
8+
using JsonApiDotNetCore.Middleware;
79
using JsonApiDotNetCore.Serialization;
810
using JsonApiDotNetCore.Services;
911
using Microsoft.AspNetCore.Http;
@@ -13,6 +15,7 @@
1315

1416
namespace JsonApiDotNetCore.Extensions
1517
{
18+
// ReSharper disable once InconsistentNaming
1619
public static class IServiceCollectionExtensions
1720
{
1821
public static void AddJsonApi<TContext>(this IServiceCollection services)

src/JsonApiDotNetCore/Formatters/JsonApiReader.cs

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using JsonApiDotNetCore.Serialization;
55
using JsonApiDotNetCore.Services;
66
using Microsoft.AspNetCore.Mvc.Formatters;
7-
using Microsoft.EntityFrameworkCore;
87
using Microsoft.Extensions.Logging;
98
using Newtonsoft.Json;
109

src/JsonApiDotNetCore/Internal/DasherizedRoutingConvention.cs

+5-19
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
using System.Reflection;
44
using JsonApiDotNetCore.Controllers;
55
using JsonApiDotNetCore.Extensions;
6-
using Microsoft.AspNetCore.Mvc;
76
using Microsoft.AspNetCore.Mvc.ApplicationModels;
87

98
namespace JsonApiDotNetCore.Internal
109
{
1110
public class DasherizedRoutingConvention : IApplicationModelConvention
1211
{
13-
private string _namespace;
12+
private readonly string _namespace;
1413
public DasherizedRoutingConvention(string nspace)
1514
{
1615
_namespace = nspace;
@@ -20,14 +19,11 @@ public void Apply(ApplicationModel application)
2019
{
2120
foreach (var controller in application.Controllers)
2221
{
23-
var template = string.Empty;
24-
25-
if (IsDasherizedJsonApiController(controller))
26-
template = $"{_namespace}/{controller.ControllerName.Dasherize()}";
27-
else
28-
template = GetTemplate(controller);
22+
if (IsDasherizedJsonApiController(controller) == false)
23+
continue;
2924

30-
controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel()
25+
var template = $"{_namespace}/{controller.ControllerName.Dasherize()}";
26+
controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel
3127
{
3228
Template = template
3329
};
@@ -40,15 +36,5 @@ private bool IsDasherizedJsonApiController(ControllerModel controller)
4036
var notDisabled = type.GetCustomAttribute<DisableRoutingConventionAttribute>() == null;
4137
return notDisabled && type.IsSubclassOf(typeof(JsonApiControllerMixin));
4238
}
43-
44-
private string GetTemplate(ControllerModel controller)
45-
{
46-
var type = controller.ControllerType;
47-
var routeAttr = type.GetCustomAttribute<RouteAttribute>();
48-
if(routeAttr != null)
49-
return ((RouteAttribute)routeAttr).Template;
50-
51-
return controller.ControllerName;
52-
}
5339
}
5440
}

src/JsonApiDotNetCore/Internal/Error.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ public Error(string status, string title, string detail)
3030
public string Status { get; set; }
3131

3232
[JsonIgnore]
33-
public int StatusCode { get { return int.Parse(Status); } }
33+
public int StatusCode => int.Parse(Status);
3434
}
3535
}

0 commit comments

Comments
 (0)