Skip to content

Commit 0ff8489

Browse files
committed
feat(#1): hyphenated routes
1 parent 21bc1b4 commit 0ff8489

File tree

9 files changed

+80
-37
lines changed

9 files changed

+80
-37
lines changed

README.md

+22-2
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public class ThingsController : JsonApiController<Thing>
108108
}
109109
```
110110

111-
#### Non-Integer Type Keys
111+
### Non-Integer Type Keys
112112

113113
If your model is using a type other than `int` for the primary key,
114114
you should explicitly declare it in the controller
@@ -125,4 +125,24 @@ public class ThingsController : JsonApiController<Thing, Guid>
125125
: base(jsonApiContext, entityRepository, loggerFactory)
126126
{ }
127127
}
128-
```
128+
```
129+
130+
## Routing
131+
132+
By default the library will configure routes for each controller.
133+
Based on the [recommendations](http://jsonapi.org/recommendations/)
134+
outlined in the JSONAPI spec, routes are hyphenated. For example:
135+
136+
```
137+
/todo-items --> TodoItemsController
138+
NOT /todoItems
139+
```
140+
141+
### Namespacing and Versioning URLs
142+
143+
You can add a namespace to the URL by specifying it in `ConfigureServices`:
144+
145+
```
146+
services.AddJsonApi<AppDbContext>(
147+
opt => opt.Namespace = "api/v1");
148+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace JsonApiDotNetCore.Configuration
2+
{
3+
public class JsonApiOptions
4+
{
5+
public string Namespace { get; set; }
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,13 @@
1-
using System;
2-
using JsonApiDotNetCore.Internal;
31
using Microsoft.AspNetCore.Builder;
4-
using Microsoft.AspNetCore.Mvc.Internal;
5-
using Microsoft.AspNetCore.Routing;
6-
using Microsoft.Extensions.DependencyInjection;
72

83
namespace JsonApiDotNetCore.Routing
94
{
105
public static class IApplicationBuilderExtensions
116
{
127
public static IApplicationBuilder UseJsonApi(this IApplicationBuilder app)
138
{
14-
if (app == null)
15-
throw new ArgumentNullException(nameof(app));
16-
17-
// Verify if AddMvc was done before calling UseMvc
18-
// We use the MvcMarkerService to make sure if all the services were added.
19-
if (app.ApplicationServices.GetService(typeof(MvcMarkerService)) == null)
20-
throw new InvalidOperationException();
21-
22-
var middlewarePipelineBuilder = app.ApplicationServices.GetRequiredService<MiddlewareFilterBuilder>();
23-
middlewarePipelineBuilder.ApplicationBuilder = app.New();
24-
25-
var routes = new RouteBuilder(app)
26-
{
27-
DefaultHandler = app.ApplicationServices.GetRequiredService<JsonApiRouteHandler>(),
28-
};
29-
30-
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
31-
32-
return app.UseRouter(routes.Build());
9+
app.UseMvc();
10+
return app;
3311
}
3412
}
3513
}

src/JsonApiDotNetCore/Extensions/ServiceProviderExtensions.cs

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using JsonApiDotNetCore.Configuration;
13
using JsonApiDotNetCore.Data;
24
using JsonApiDotNetCore.Formatters;
35
using JsonApiDotNetCore.Internal;
@@ -13,10 +15,24 @@ public static class ServiceProviderExtensions
1315
{
1416
public static void AddJsonApi<TContext>(this IServiceCollection services)
1517
where TContext : DbContext
18+
{
19+
_addInternals<TContext>(services, new JsonApiOptions());
20+
}
21+
22+
public static void AddJsonApi<TContext>(this IServiceCollection services, Action<JsonApiOptions> options)
23+
where TContext : DbContext
24+
{
25+
var config = new JsonApiOptions();
26+
options(config);
27+
_addInternals<TContext>(services, config);
28+
}
29+
30+
private static void _addInternals<TContext>(IServiceCollection services, JsonApiOptions jsonApiOptions)
31+
where TContext : DbContext
1632
{
1733
services.AddJsonApiInternals<TContext>();
1834
services.AddMvc()
19-
.AddMvcOptions(options => options.SerializeAsJsonApi());
35+
.AddMvcOptions(opt => opt.SerializeAsJsonApi(jsonApiOptions));
2036
}
2137

2238
public static void AddJsonApiInternals<TContext>(this IServiceCollection services)
@@ -37,11 +53,13 @@ public static void AddJsonApiInternals<TContext>(this IServiceCollection service
3753
services.AddScoped<JsonApiRouteHandler>();
3854
}
3955

40-
public static void SerializeAsJsonApi(this MvcOptions options)
56+
public static void SerializeAsJsonApi(this MvcOptions options, JsonApiOptions jsonApiOptions)
4157
{
4258
options.InputFormatters.Insert(0, new JsonApiInputFormatter());
4359

4460
options.OutputFormatters.Insert(0, new JsonApiOutputFormatter());
61+
62+
options.Conventions.Insert(0, new DasherizedRoutingConvention(jsonApiOptions.Namespace));
4563
}
4664
}
4765
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// REF: https://github.com/aspnet/Entropy/blob/dev/samples/Mvc.CustomRoutingConvention/NameSpaceRoutingConvention.cs
2+
// REF: https://github.com/aspnet/Mvc/issues/5691
3+
using JsonApiDotNetCore.Extensions;
4+
using Microsoft.AspNetCore.Mvc.ApplicationModels;
5+
6+
namespace JsonApiDotNetCore.Internal
7+
{
8+
public class DasherizedRoutingConvention : IApplicationModelConvention
9+
{
10+
private string _namespace;
11+
public DasherizedRoutingConvention(string nspace)
12+
{
13+
_namespace = nspace;
14+
}
15+
16+
public void Apply(ApplicationModel application)
17+
{
18+
foreach (var controller in application.Controllers)
19+
{
20+
var template = $"{_namespace}/{controller.ControllerName.Dasherize()}";
21+
controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel()
22+
{
23+
Template = template
24+
};
25+
}
26+
}
27+
}
28+
}

src/JsonApiDotNetCoreExample/Controllers/PeopleController.cs

-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
using JsonApiDotNetCore.Data;
33
using JsonApiDotNetCore.Services;
44
using JsonApiDotNetCoreExample.Models;
5-
using Microsoft.AspNetCore.Mvc;
65
using Microsoft.Extensions.Logging;
76

87
namespace JsonApiDotNetCoreExample.Controllers
98
{
10-
[Route("api/[controller]")]
119
public class PeopleController : JsonApiController<Person>
1210
{
1311
public PeopleController(

src/JsonApiDotNetCoreExample/Controllers/TodoItemsController.cs

-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
using JsonApiDotNetCore.Data;
33
using JsonApiDotNetCore.Services;
44
using JsonApiDotNetCoreExample.Models;
5-
using Microsoft.AspNetCore.Mvc;
65
using Microsoft.Extensions.Logging;
76

87
namespace JsonApiDotNetCoreExample.Controllers
98
{
10-
[Route("api/[controller]")]
119
public class TodoItemsController : JsonApiController<TodoItem>
1210
{
1311
public TodoItemsController(

src/JsonApiDotNetCoreExample/Program.cs

-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
31
using System.IO;
4-
using System.Linq;
5-
using System.Threading.Tasks;
62
using Microsoft.AspNetCore.Hosting;
73
using Microsoft.AspNetCore.Builder;
84
using Microsoft.Extensions.Configuration;

src/JsonApiDotNetCoreExample/Startup.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public void ConfigureServices(IServiceCollection services)
3131
options.UseNpgsql(_getDbConnectionString());
3232
}, ServiceLifetime.Transient);
3333

34-
services.AddJsonApi<AppDbContext>();
34+
services.AddJsonApi<AppDbContext>(opt => opt.Namespace = "api/v1");
3535
}
3636

3737
public void Configure(

0 commit comments

Comments
 (0)