-
Notifications
You must be signed in to change notification settings - Fork 224
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ASP.NET Core sample #59
Conversation
6950088
to
4ab35b3
Compare
This is ready for review, I know it's huge but I have no other idea on how to commit such a large sample other than this type of PRs (suggestions are more than welcome). BTW take a look at the PoA, there is a pending question:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are comments after first pass. To-be-continued ;).
} | ||
else | ||
{ | ||
await Task.WhenAll(pending); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens when one of the appenders fails? Should we return partial response or fail the whole request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the whole thing blows up, we talked about it when the very first demo was built and decided that to keep the demo as simple as possible failure handling was not in scope.
&& !routeData.Values.ContainsKey("id"); | ||
} | ||
|
||
public async Task Append(dynamic vm, RouteData routeData, IQueryCollection query) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we pass vm here or change the approach so that the appender returns dynamic? This would prevent any cross-appender type of communication and name clashes between appenders.
In addition the structure of the end response could be controlled by the caller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point. If appenders create their own VM that is returned the issue is that who manages the appender then needs to act as a coordinator in order to merge together all the returned view models. The result is that the coordinator knows the structure and we don't want that.
The used approach on the contrary makes the coordinator totally unaware of the structure, leaving the thing with the possibility that 2 appenders clash on the same property name for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First of all, I don't see that as a big issue but what the coordinator could do is:
a. simply append vm returned from appenders to the response (no knowledge about appenders needed)
b. detect clashes and do some namespacing (add a json-node derived from appender name etc.)
{ | ||
public class OrdersListViewModelAppender : IViewModelAppender | ||
{ | ||
public bool Matches(RouteData routeData, string verb) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we provide some base class to make this more declarative?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes and no, probably in a production ready thing would be a nice to have, in a sample not necessarily.
I like the approach! There are some high level questions that I had during the review which I would also like to ask:
|
<div class="panel-body"> | ||
|
||
</div> | ||
</div>*@ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mauroservienti this file belongs to the existing solution, did this change creep into this PR by accident?
(Same with the .suo file above.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooops. I made those changes while demoing the thing to you and @DavidBoike and then pushed them by accident.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.suo
files are there for the only purpose of sharing startup projects settings, so that we can provide a F5
experience. I hate to be forced to commit that stuff.
Any other idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think committing the .suo files is OK for a demo project, but the .sou for the ASP.NET MVC demo should not be changed as part of this PR. I'd prefer to have that change reverted in this branch (as well the change to this file).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having multiple demo solution should in the same repo has this side effect, not fun.
Which change/commit is "that" change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mauroservienti I would like to revert the changes to these files:
- demos/ASP.Net MVC/.vs/ASP.Net MVC/v14/.suo
- demos/ASP.Net MVC/Divergent.Frontend/Views/Orders/Details.cshtml
I don't think a specific commit needs to be reverted. These files can both be reverted to what's in master
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean by manually adding back what I removed previously, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are many ways to do it. Personally I'd open the TortoiseGit log view, then diff the tip of the branch to master, and then right click each file and "Revert to 2cbcea1".
no, even if it's a thing that would be interesting to explorer. When I started this the goal was to demo a gateway-only composition to return json structures. But on top of that now I don't see why such an evolution should not be tested. @tmasternak can you expand a little bit on the idea? (maybe in its own issue)
There is no failure handling approach at all ATM.
Appenders can retrieve security related headers from the incoming request and pass along to their own backend. I haven't thought about it all
uhm. Interesting, tell me more
in a large system one may end up with thousands of appenders, that's the first thing that needs to be cached since they can be easily cached by URL |
@adamralph @seanfarmar since you saw the presentation at DDD SouthWest, thoughts on this:
I'm on the fence, from the implementation perspective is a reverse proxy, conceptually is an API Gateway. The decisions affects the components names. |
Will do.
I think you already answered my question when talking about security. Appenders already have access to the request so they can grab pretty much anything that client sends. |
Isn't an API gateway an implementation of a reverse proxy? They're not mutually exclusive. |
I have another question @dvdstelt @adamralph @tmasternak:
Thoughts? |
@mauroservienti 👍 for using Which leads us to other data source which are
|
@mauroservienti Don't you think I'll let @mauroservienti respond to @tmasternak his questions, because he knows best. But afaik most of it is because of the framework and the fact that it's really hard to come up with a good solution that doesn't involve writing TONS of interfaces and classes, without providing anything additional. |
I haven't thought to
What about, starting from this, add a new "demo" or create a PoC? |
@dvdstelt I agree but it's not aligned with the HTTP behavior, so it might create a lot of confusion when it comes to strage HTTP usages such as POST requests to overcome GET URL length limitations. That means that you might want to have a |
I'm playing with the new syntax here with the goal of building a slightly more real life sample that handles reads and writes. |
Found a bug #64, I'll fix before merging. |
@mauroservienti any chance you can do a screencast to give an overview of the solution? (similar to your presentation in DDD)? |
Good idea @seanfarmar, I can do that this Friday |
public class OrdersController : ApiController | ||
{ | ||
[HttpGet] | ||
public dynamic Get(int id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why dynamic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, probably back in time the return type was an anonymous class an di we never changed the action return type, that I should always serilized.
I'll fix it. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked and we return an anonymous type:
var order = db.Orders
.Include(o => o.Items)
.Where(o => o.Id == id)
.Select(o => new
{
Number = o.Id,
o.Id,
ItemsCount = o.Items.Count
})
.SingleOrDefault();
return order;
in which case the return type can only be dynamic
or object
{ | ||
string json = JsonConvert.SerializeObject(result.ViewModel, GetSettings(context)); | ||
context.Response.ContentType = "application/json; charset=utf-8"; | ||
await context.Response.WriteAsync(json); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be a direct return? ie no await
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that await
is useless
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it can be, but it doesn't compile because at line 13 there is an await
:
var result = await CompositionHandler.HandleGetRequest(context);
|
||
namespace ITOps.ViewModelComposition | ||
{ | ||
abstract class Subscription |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not an interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, changed to be an interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the solution and project naming, the one that makes the most sense to me is
Divergent.WebApi.sln
Divergent.WebApi.csproj
Divergent.Website.sln
Divergent.Website.csproj
Thanks for the explanation regarding the ITOps projects. One thing I still don't understand is why ITOps.ViewModelComposition.csproj
has types in a ITOps.ViewModelComposition.Gateway
namespace, whereas ITOps.ViewModelComposition.Gateway.csproj
also exists. Is it that the ITOps.ViewModelComposition.Gateway
types in ITOps.ViewModelComposition.csproj
are generic gateway components, whereas the types in ITOps.ViewModelComposition.Gateway.csproj
are the ASP.NET Core specific implementations?
Regarding ITOps.ViewModelComposition.Json.csproj
, how about renaming it to ITOps.Json.csproj
?
{ | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mauroservienti what's your opinion? Should we remove it or leave it? I'm probably leaning towards removing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
public IActionResult Index() | ||
{ | ||
return RedirectToRoute(new { controller = "Orders" }); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.Where(t => | ||
{ | ||
var typeInfo = t.GetTypeInfo(); | ||
return !typeInfo.IsInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return Task.CompletedTask; | ||
}); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking less intrusive, like this:
public static void RunCompositionGatewayWithDefaultRoutes(this IApplicationBuilder app) =>
app.RunCompositionGateway(routes =>
{
routes.MapComposableGet(template: "{controller}/{id:int?}");
routes.MapRoute("*", context =>
{
context.Response.StatusCode = StatusCodes.Status404NotFound;
return Task.CompletedTask;
});
});
I.e. the braces disappear and =>
is appended to the declaration. Everything else stays the same, even the method body indentation.
BTW - I just noticed that there is an extra space before each of the braces that surrounds the handler delegate that's passed to the MapRoute
method.
if (result.StatusCode == StatusCodes.Status200OK) | ||
{ | ||
string json = JsonConvert.SerializeObject(result.ViewModel, GetSettings(context)); | ||
context.Response.ContentType = "application/json; charset=utf-8"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
static JsonSerializerSettings GetSettings(HttpContext context) | ||
{ | ||
if (!context.Request.Headers.TryGetValue("Accept-Casing", out StringValues casing)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention used to be an X-
, prefix, but that was deprecated some time ago.
However, is this overkill? We're already ignoring the Accept header and assuming JSON is OK. Shall we simplify and just assume that a specific casing is also OK?
|
||
namespace ITOps.ViewModelComposition.Gateway | ||
{ | ||
public static class RouteBuilderExtentions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "Extentions" -> "Extensions"
the thing is that it's not WebAPI, WebAPI is already an established term that refers, when we talk in the context of .NET, to ASP.Net WebAPI. It is a
OK.
I don't understand what you mean by
In
OK |
I should have addressed all the comments @adamralph, please take a look. |
1894925
to
aee1537
Compare
rebased on master |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the naming, Divergent.CompositionGateway
seems good. (And thanks for updating the PR to match the new folder convention.)
WebAPI is already an established term that refers, when we talk in the context of .NET, to ASP.Net WebAPI.
Yes, this naming masterpiece by Microsoft has made me weep repeatedly. In my old team, we were calling our HTTP JSON/XML endpoints "web APIs" well before Microsoft released the beloved "WebAPI". 😢
In
ITOps.ViewModelComposition.Gateway
there are just 3 classes that strictly related to how a reverse proxy works in .NET Core, they act as a bridge allowing the composition engine to be hosted in a ASP.Net Core reverse proxy. The composition engine is hosting aware, it just composes.
That tells me that ITOps.ViewModelComposition.Gateway.csproj
contains the ASP.NET Core-specific implementation of abstractions defined in ITOps.ViewModelComposition.csproj
. Is that correct? In that case, I would consider renaming ITOps.ViewModelComposition.Gateway.csproj
to ITOps.ViewModelComposition.AspNetCore
.
Although, I'm unsure about that, since I see that ITOps.ViewModelComposition.csproj
also has a dependency on Microsoft.AspNetCore.Routing.nupkg
.
Somewhat related to the last point, in ITOps.ViewModelComposition.csproj
, there is only one type in each of the namespaces ITOps.ViewModelComposition.Engine
and ITOps.ViewModelComposition.Gateway
. Should we collapse those two types into the ITOps.ViewModelComposition
namespace?
Did you get anywhere with the JSON casing spike?
In other news, I'm starting to develop an in-depth understanding of this sample! 🎓
|
||
namespace ITOps.ViewModelComposition.Gateway | ||
{ | ||
public static class RouteBuilderExtensions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class name typo "extentions" was fixed, but the file name is still wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooops, missed that
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Divergent.CompositionGateway", "Divergent.CompositionGateway\Divergent.CompositionGateway.csproj", "{C0E4CD4F-657A-4872-969C-D97768636BDA}" | ||
EndProject | ||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IT-Ops", "IT-Ops", "{DB3EFAF6-6B45-48ED-BCB9-5F44FAB30CF9}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: "ITOps"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All over the place (in slides) we IT/Ops
, with the /
, that cannot be used in file names. I'll align it with project names.
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Divergent.Sales.Data", "Divergent.Sales.Data\Divergent.Sales.Data.csproj", "{80F82F7D-3F0A-4316-87CD-49F19DBD5B85}" | ||
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Divergent.Sales.API.Host", "Divergent.Sales.API.Host\Divergent.Sales.API.Host.csproj", "{DC8ADA37-837C-490F-BDFC-FC8D1001417E}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: "API" should really be cased "Api". Same for Shipping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said they are the copy of the endpoint used by the exercises. So 👎 to rename them unless we fix them all. Nitpick, API is an acronym thus conventions say it should be uppercase
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .NET capitalisation conventions state PascalCasing for any acronym over two letters in length (although the BCL violates that in numerous places). However, it's only a nitpick and I agree that we should fix everything or nothing, so let's leave this as-is for now.
{ | ||
public static class AssemblyLoader | ||
{ | ||
public static Assembly Load(string assemblyFullPath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this method is only used in one place (ServiceCollectionExtensions
), shall we make it a private method there and drop this class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes and no, it's a generic way to load assemblies in .NET Core not connected to what we do here, it should go in its own "helper" assembly. It is required if we want (as I did for the DDD SouthWest demo) to introduce ViewComponents that are a very interesting Mvc Core feature for UI Composition
{ | ||
public class CompositionHandler | ||
{ | ||
public static async Task<(dynamic ViewModel, int StatusCode)> HandleGetRequest(HttpContext context) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this method is only used in one place (ComposableRouteHandler
), shall we make it a private method there and drop this class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used by the CompositionActionFilter
as well, in the MVC project. This is the main reason I was reluctant to split the solution and make 2 with shared projects, it's impossible to see the whole picture.
public ICollection<Item> Items { get; set; } | ||
} | ||
|
||
public class Item |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment regarding 1:1 files and types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we split these types into separate files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we split these types into separate files?
yes and no. The back-end projects (WebAPI) used by this demo, right now are an exact copy of the back-end projects used by exercises. They all should be changed probably to .NET Core as soon as NServiceBus on .NET Core is out of beta, till then I prefer not to diverge. It's indeed not a big change, but still I prefer not to diverge. Can we postpone this refactoring to a later time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 fine by me.
{ | ||
/* | ||
* matching is a bit weak in this sample, it's designed | ||
* this way to satisfy both the Gateway and the Mvc sample |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this updated to say "both the composite gateway and website samples"?
Same in three other places.
{ | ||
var id = (string)routeData.Values["id"]; | ||
|
||
var url = $"http://localhost:20295/api/orders/{id}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels a bit weak having the host and port hard coded in here, since the values depend on deployment, and what is in the app config for the host. Is there anyway we can configure this for the hosts that invoke this assembly?
The same comment applies in a few other places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's super hard for a sample, I thought about it, the thing is the way .NET Core allows to inject (via DI) configuration options is not really nice and it might complicate a lot the sample. What if we accept this as is for now, raise an issue to track it and spike something later? so not to complicate even more this PR
{ | ||
dynamic vm = new ExpandoObject(); | ||
vm.OrderNumber = order.Number; | ||
vm.OrderItemsCount = order.ItemsCount; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use an initialisier to set these properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to, an initializer would look like:
dynamic vm = new ExpandoObject()
{
//set props here
};
but compiler complains because ExpandoObject
doesn't have those properties we're trying to set.
{ | ||
using (var db = new ShippingContext()) | ||
{ | ||
var _ids = ids.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(s=>int.Parse(s)).ToArray(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies for this, but _ids
is setting off my OCD. The underscore convention is usually used for fields (although my preference is this.
). Perhaps idArray
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above these are copies (or were originally copies) of the APIs used in the exercise. I can change it.
Yeah, we should. I believe I noticed another couple of things that are in the wrong namespace, I'll do another pass.
No, not yet. It affects a lot of projects in this demo, to keep changes to this PR in scope I propose we revisit as we have this merged. It's something that we can drop but it's also transparent to the logic of the sample, the problem is that changes will be pervasive to all projects, only complicating this PR. |
Originally
|
@adamralph addressed most of the changes, and replied to a couple of comments. |
9672662
to
228522e
Compare
{ | ||
class Subscription<T> : ISubscription | ||
{ | ||
private Func<dynamic, T, RouteData, IQueryCollection, Task> subscription; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mauroservienti did you intend to rename this?
Personally I like using a verb for the name of a delegate, since then invocation is of the form handle()
, but I won't oppose handler
if that's what you prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
public ICollection<Item> Items { get; set; } | ||
} | ||
|
||
public class Item |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we split these types into separate files?
using System.Dynamic; | ||
using System.Threading.Tasks; | ||
|
||
namespace ITOps.ViewModelComposition.Engine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per #59 (comment), should this be moved to ITOps.ViewModelComposition
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
namespace ITOps.ViewModelComposition.Gateway |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per #59 (comment), should this be moved to ITOps.ViewModelComposition
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ITOps", "ITOps", "{DB3EFAF6-6B45-48ED-BCB9-5F44FAB30CF9}" | ||
EndProject | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ITOps.ViewModelComposition.Gateway", "ITOps.ViewModelComposition.Gateway\ITOps.ViewModelComposition.Gateway.csproj", "{1B2BC5FF-3F95-4429-8AE1-5AC6C99B689A}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this project be removed from the Website solution?
All comments addressed. |
should this be upgraded to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mauroservienti the diff LGTM.
I added a couple of commits to tweak the README.md. If you're happy with those changes, can you please squash all commits on this branch into one before we merge.
should this be upgraded to
netcoreapp2.0
?
Yes, I think so, but shall we get this PR merged first and do that in a separate PR?
6e831dd
to
1a81adc
Compare
squashed into 1 single commit @adamralph. And yes, |
🎆 🎉 |
PoA
support POST requestssupport PUT requestssupport PATCH requestssupport DELETE requestssuper simple SPA to interact with back-endIn the solution that demos the Gateway alone add a super-simple js clientreplaced by [WIP] Vanilla TypeScript sample #62Description
This PR introduces a demo of a composite UI using as composition infrastructure a gateway/reverse proxy built on top of ASP.Net Core 1.1.
This composition infrastructure allows to run a .Net Core web server as simple as:
The above configures a default route, whose template is
{controller}/{id:int?}
, that mimics the WebAPI default behavior. All HTTP requests are intercepted and if matchingViewModelAppenders
are found they are invoked, allowing composition to happen.Similarly to the full MVC Classic sample lists are handled via subscribing to events raised by appenders.