Feature/graphql (#312)

* #298 initial hacking around better aggregation

* #298 bit more hacking around

* #298 abstraction over httpresponsemessage

* #298 tidying up

* #298 docs

* #298 missed this

* #306 example of how to do GraphQL
This commit is contained in:
Tom Pallister
2018-04-12 17:48:43 +01:00
committed by GitHub
parent a15f75dda8
commit b46ef1945d
7 changed files with 270 additions and 4 deletions

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Update="configuration.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.6"/>
<PackageReference Include="Ocelot" Version="5.5.1"/>
<PackageReference Include="GraphQL" Version="2.0.0-alpha-870"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Ocelot.Middleware;
using Ocelot.DependencyInjection;
using GraphQL.Types;
using GraphQL;
using Ocelot.Requester;
using Ocelot.Responses;
using System.Net.Http;
using System.Net;
using Microsoft.Extensions.DependencyInjection;
using System.Threading;
namespace OcelotGraphQL
{
public class Hero
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Query
{
private List<Hero> _heroes = new List<Hero>
{
new Hero { Id = 1, Name = "R2-D2" },
new Hero { Id = 2, Name = "Batman" },
new Hero { Id = 3, Name = "Wonder Woman" },
new Hero { Id = 4, Name = "Tom Pallister" }
};
[GraphQLMetadata("hero")]
public Hero GetHero(int id)
{
return _heroes.FirstOrDefault(x => x.Id == id);
}
}
public class GraphQlDelegatingHandler : DelegatingHandler
{
private readonly ISchema _schema;
public GraphQlDelegatingHandler(ISchema schema)
{
_schema = schema;
}
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//try get query from body, could check http method :)
var query = await request.Content.ReadAsStringAsync();
//if not body try query string, dont hack like this in real world..
if(query.Length == 0)
{
var decoded = WebUtility.UrlDecode(request.RequestUri.Query);
query = decoded.Replace("?query=", "");
}
var result = _schema.Execute(_ =>
{
_.Query = query;
});
//maybe check for errors and headers etc in real world?
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(result)
};
//ocelot will treat this like any other http request...
return response;
}
}
public class Program
{
public static void Main(string[] args)
{
var schema = Schema.For(@"
type Hero {
id: Int
name: String
}
type Query {
hero(id: Int): Hero
}
", _ => {
_.Types.Include<Query>();
});
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
s.AddSingleton<ISchema>(schema);
s.AddOcelot()
.AddSingletonDelegatingHandler<GraphQlDelegatingHandler>();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
})
.UseIISIntegration()
.Configure(app =>
{
app.UseOcelot().Wait();
})
.Build()
.Run();
}
}
}

View File

@ -0,0 +1,71 @@
# Ocelot using GraphQL example
Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together.
I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorisation / authentication or I would
bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that.
## Example
If you run this project with
$ dotnet run
Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together...
GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } }
RESPONSE
```json
{
"data": {
"hero": {
"id": 4,
"name": "Tom Pallister"
}
}
}
```
POST http://localhost:5000/graphql
BODY
```json
{ hero(id: 4) { id name } }
```
RESPONSE
```json
{
"data": {
"hero": {
"id": 4,
"name": "Tom Pallister"
}
}
}
```
## Notes
Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in configuration.json e.g.
```json
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/graphql",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "yourgraphqlhost.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/graphql",
"DelegatingHandlers": [
"GraphQlDelegatingHandler"
]
}
]
}
```

View File

@ -0,0 +1,19 @@
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "jsonplaceholder.typicode.com",
"Port": 80
}
],
"UpstreamPathTemplate": "/graphql",
"DelegatingHandlers": [
"GraphQlDelegatingHandler"
]
}
]
}