Migrating from ASP.NET Web API to MVC 6
CC Attribution - NonCommercial - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Woj, Filip
While Microsoft is calling their new web framework "MVC 6" and branding it as a unified successor of MVC / Web API / Web Pages, the reality is much more complicated than that. The newASP.NET 5 alone is a completely different managed runtime, which results in plenty of changes, compatibility issues and necessary adjustments when trying to bring your old application over to the new world. On top of that MVC 6 introduces plenty of changes. While controllers in "old" and "new" world may look similarly, a lot of core concepts that you might have been used to when working with Web API projects have changed, and finding corresponding ones is a tricky, and often frustrating, task. This talk will draw parallels between the Web API framework pipeline that you might be used to (MessageHandlers, Filters, Selectors, Formatters etc), to the one which are part of MVC 6, and explore the techniques you can use when migrating a Web API project to MVC 6 - hopefully easing your transition into the next generation of ASP.NET.
 Alright, welcome. My name is Philip and this is a session about migrating from Web API to MVC6. Before we begin, just a couple of words about myself, okay, because some of you might be wondering who this guy is, why is he trying to talk to us today, why is he dressed like a football hooligan, and in general, what's the story here? Like I said, my name is Philip, my last name, nobody can pronounce it, so let's just say it's W. These days, everyone's on the Internet, so I'm on the Internet as well. I have a blog at stratweb.com and I blog quite a lot about ASP.net, so if you are ASP on the developer, chances are maybe you have seen some of the stuff already. I'm also involved in quite a few open source projects, some larger, some smaller, so I spend quite some time on GitHub as well. And my Twitter handle is philip.dob. And as far as my professional engagement goes, I work for a software development company called Climax Media, that's a company out of Toronto, Canada. So this is something that we'll kind of tie into the stuff that we'll be talking about today. So last year, I wrote this book, it's a web API book, and you know how it is, it takes a bit of an effort to write a book and you don't do it every week, right, like you don't do it every month probably as well. And for me, it was kind of a big deal, but for a very short while, and I literally mean a short while, because you know what happened, web API is kind of gone. I mean, it's not gone, it's still there, but it is becoming legacy technology, right, so this is why we are here today to discuss this. And a couple of months ago, I was having a conversation with one of the clients of ours and he started asking some really valid questions, like you know, he said that they have invested heavily in web API and web API has become their web technology of choice and this and that. And at the same time, he's starting to hear out of Microsoft that you know there's ASPnet 5 coming, it will be cross-platform, been deployable runtime, all these kinds of cool things that ASPnet 5 promises to bring. And you know, they want to stay evergreen, so like he asked, what am I supposed to do, right, like how do I approach this? We want to be evergreen, so we want to somehow update. Do we run a tool? Does the tool upgrade us? Do we pull in a package and the package magically acts as a proxy or whatever? Like what do we do, right? And I thought maybe it will actually be interesting to take a web API project, to take an empty MPC project and just migrate piece by piece and hopefully this is something that can save your time in the future as well, okay? But before we get there, this is a brief history of HTTP services in.NET and there's a few different technologies over the years and you know, if you take the age of.NET and you divide by the number of these technologies, it probably averages out like a technology per two years or something. And of course, this is not ideal from the developer's perspective and chances are you have already done migration between those technologies. And now we are at a point where we have web API and we kind of have to start thinking about moving forward, right? So of course, you could make jokes about this, right? But the reality is MVC6 is actually a really good framework, okay? So most people probably don't realize it. And in fact, that's the story. The name is not super marketable, right? I know the marketing guys, so probably my opinion doesn't matter. But like if I'm doing Node or something, I'm not getting excited about iteration number five of active server pages or iteration number six of some framework on top of it. But the reality is everything is being rewritten from scratch, right? So ASP.NET 5 is brand new. MVC6 is a brand new framework, even though it suggests that it's a continuation of an existing framework. So, you know, we mentioned a few benefits already, like, you know, you will be able to run on any platform or to have the runtime package together with your applications, where your application becomes self-contained, those types of things. And then MVC6 itself as a framework, it's actually extremely flexible and very configurable. And it has to be. And the reason why it has to be is that it's really the only framework that Microsoft is proposing for ASP.NET 5, right? There's no more web API, there's no more web forms, there's no more web pages, and really MVC in the old shape and form. So it kind of, like, it has to be flexible because it has to cater for all these specific scenarios that you might have used some alternative frameworks for. Okay, so if you think about migration of web API, right, there's a few things you have to consider. So you have to consider that MVC6 runs on top of DNx, so what does it mean? Well, it means that if you want to run on core CLR, you will actually have to migrate all of your source code, right? You will not be able to pull in some existing binaries, like, it means that all of the source code has to be migrated because on core CLR you can only use packages that were built for core CLR. Then you have to use a new set of HTTP abstractions. This is actually quite a shame because the HTTP abstractions that were part of web API, they were actually really good. And most of the developers have actually grown to like them a lot, you know, the system-net HTTP abstractions. With web API, we had beautiful client server symmetry because you would work with HTTP request message, HTTP response message, both on client side and on the server side. So there is still a way to work with system-net HTTP abstractions with MVC6, but it requires some configuration and in many situations it's not ideal. Another thing is that MVC6, and this has to be remembered, it's kind of geared towards compatibility or semi-compatibility with MVC5. And this really means that a lot of the out of the box behaviors, a lot of the conventions, they would resemble those from MVC5. And if you think about moving your web API code over, you will quite often end up in situations where it might actually be frustrating because things don't work as they might be expected to. And in general, this could be slightly confusing. So we mentioned the flexibility. And then there is one more project that I wanted to highlight today, and that is the so-called web API compatibility scheme. And this is one of the projects from the MVC6 project family. And this project is kind of a bridge between web API and MVC6, so you can actually use this to ease your transition from web API to MVC6. So we will have a look at this in this session as well. Now the problem with web API compatibility scheme is its name, right? Name, shim. It sounds dirty. We all know the concept of shim in software development is an old concept. It's just as the name of a product or a package, it sounds like duct tape, right? Would you put duct tape in production? Probably not. But anyway, here is a tweet from my friend Darrell. And Darrell is a great contributor to the ASP.net community. And he said this, right? And I completely agree. So again, it's just about the name, but it would be nice if the name could be changed. If somebody from Microsoft is actually watching this, where is John Galloway? He was supposed to be here. Anyway, I'm done, actually. Thank you. So a while ago when I was thinking about this session, I was actually, you know, weighing different options like how we could actually do it. And I had this brilliant idea that actually instead of showing boring slides, I would just show code, right? I would just take a web API project, I would take a MVC project, and we can migrate piece by piece and we can discuss different things that maybe have different traps that you have to avoid and those types of things. And this seemed like a brilliant idea at the time. Now I'm starting to see flaws in this plan because it actually involves me live coding for about 50 minutes on stage. And, you know, to be honest, I don't think I have ever managed to go 50 minutes straight coding without making a massive mistake. So it might be actually entertaining for you. Anyway, this is the stuff that I have. I don't know if you can see it, we can make it bigger. Is it okay? I get the glasses. Okay. As you wish. Like this? Okay. So I have a web API project. And this web API project is actually a project that was created by Microsoft in 2012. It was one of the projects that Dan wrote, was using to show web API to the world. It's a contacts manager. Of course, I actually have modified it quite heavily because I wanted to get in different concepts. But the core is really the same. And I thought it would be nice to use the same project that was used to introduce web API to actually migrate away from web API today. Yeah. So this is the project. It's actually, the surface API is actually really simple. However, there is a lot of things going on in there. I have a lot of different web API components plugged in. There is a custom message handler, there's a custom filter, there's a custom formatter, there's a custom exception handler. A lot of different conventions are customized and overridden. So in general, a lot of things going on here. And we'll move them piece by piece and we'll try to get the same type of behavior in MVC6. So on the other side, I have MVC6 and, okay, this is MVC6. Oh, I can zoom here. I forgot about this. So I have a MVC6 project. It's an empty project. The only thing that I did to it, I actually migrated the models, like, you know, the contact and the error information and the repository. And I also plugged in somewhere here. I also plugged in a buffering middleware because we want to buffer in ASP.NET 5 which doesn't happen out of the box. The rest is just a project straight from the ASP.NET 5 template, okay? And in fact, we mentioned web API compatibility scheme already, okay? So in fact, we do have a little message here from the nice folks at Microsoft that says, please uncomment this line if you are migrating web API and this will actually make the web API compatibility scheme kick in, right? So there is an extension method that you can uncomment. Now, we will not do it because there is a ton of magic happening behind the scenes when you do it, right? So instead, what I want to do, I want to go piece by piece and then we can identify the problems and we can consciously apply solutions to those problems, okay? So let me run this. Actually, let's zoom this as well. So this is our web API. Let me zoom this. Okay. So just a bunch of contacts, right? That's nothing exciting but again, as we go forward, I will show you different components and what they do, okay? And you can get a contact by ID as well if you want. So how do we approach this migration? Okay. So let's first, so we have a contacts controller, okay? So I'm just going to copy this and I will only take, actually, I will only take the get action, okay? So in here, I have to remember about zooming. Oops, I did something wrong. What did I do? I think I dragged the folder. Okay. So let's add a new class. There we are, like this. And let's paste it, okay? Not very exciting at the moment but it will get better. So what do we have? Let's comment out this filter. The filter will migrate a bit later, okay? So what are the, so look, everything seems to be compiling except the route attribute. So actually, there's no more route prefix in MVC6. It's just called route, okay? And then the route from the class and the route from the action, they just get concatenated with each other. It's an easy fix, no problem. Now, somehow the API controller is actually also recognized even though it's a web API controller rather than MVC controller, okay? So what is happening here? So if I go to the definition of this thing, you will see that this is actually part of this web API compatibility scheme, okay? So we can actually use it to ease our migration process. This is especially relevant if we use stuff like methods that were defined on the base controller and things like that. Now, what you notice here is that actually this API controller doesn't inherit from an MVC6 controller. It's a so-called POCO controller. So POCO controllers, they are allowed in MVC6 and you can use them. And in fact, this base controller is actually a POCO controller. So we can inherit from it or we don't have to. So for now, I'm going to choose to not inherit from it and just use straightaway POCO controller, okay? So if I run this, it will not work, but I will just show you that we can get the message that can help us solve the problem. So the problem is obviously the eye contact repository is not recognized, okay? So this is the web API again. How was it solved in web API? So in web API, there we are, we had autofuck plugged in, right? So we were using autofuck IOC container and it was set to the web API dependency resolver, right? So that's how it was done in web API. We could do a similar thing in MVC6 because autofuck is actually supported. There is autofuck for DNx, but we don't have to do it because at this point, I think the majority of people would know that MVC6 or actually ASP.net5 comes with built-in dependency injection, right? So we can actually just use that, right? So we can register contact repository as in-memory contact repository and there we are, okay? And now I'm getting some output, okay? So this is promising. It's not identical to the stuff that we have in web API because in web API, this is web API, we actually are getting XML, right? And here we are getting JSON. So what's the story here? Well, it turns out that in web API, XML formatters and support for XML-based media types was actually built-in. In MVC6, you actually have to opt into it, right? So rather than being opt out, it's opt-in. So the way we can do it, we can go to this add MVC and there is a MVC configuration object that we can act upon. This is the MVC options. And in here we can say add XML data contract serializer formatter, okay? This will add both an output formatter and an input formatter for XML. In MVC6, the formatters have been split into input and output formatters. In web API, you only dealt with a single type of formatter. So it was bidirectional. In MVC6, the formatters are unidirectional. Anyway, this extension method will register both. So if I now run our solution, well, it's not working, right? What's happened? Well, it turns out that it's actually working. So I'm going to go to Postman, which is a bit more advanced tool, okay? And I'm going to... Oh, this is bizarre. Okay. I'm going to say accept text XML. And I'm getting XML now, which is a bit awkward because why doesn't it work from the browser, from the address bar of the browser? This is another default behavior of MVC6. So MVC6 will actually try to sniff out whether the request is coming from the browser address bar and it's going to just respond with JSON for your own convenience. So anyway, there is a setting that says respect browser accept header. And I would like to respect accept header, right? And now... Yes. Okay. Very good. So we have reached a certain milestone. We have made one action work. Okay. So let's go further. And let's migrate the rest of it. Okay? So I will... Okay, I don't need to use them here. So I will just copy all of those. Oh, what is happening? Let's just copy all of those. There's like get by ID and post and put and what have you, right? Now, the one thing that I would like to highlight in this Web API controller is that there are different ways to create an HTTP response to the client, right? So first we have actually... We have a POCO, right? So we are actually returning a POCO. And then Web API pipeline is taking over. Here we are actually returning HTTP response message. And this response message is being created by an extension method here. This extension method internally in Web API will do content negotiation and will produce an instance of HTTP response message and will... This response message will then be sent to the client. And then finally here we actually have IHTP action result which was introduced in Web API 2. And what we are using, we are using actually a method on the base class created at the route and this will create a relevant action result and that will be used to produce an HTTP response message. So there are three ways... There were three ways in Web API. So let's just move over this code. Maybe I zoom here as well. So at this point we have some issues. So some things were recognized like HTTP response message. Even though it's an HTTP abstraction that doesn't really exist in MVC6, it was brought in by this compatibility shim. So the compatibility shim will actually drag the system that HTTP abstraction over. So we have it. At least the code will compile, right? Now we have to fix those methods that were called on the base class and also the properties coming from the base class. So at this point we can actually use the compatibility shim and we can actually inherit from the API controller, right? And now somehow everything seems to be okay. The only thing that needs to be fixed is this HTTP action result which doesn't exist anymore because it's called IActionResult. So this is very exciting because the code is compiling, right? Now I can run it and everything is broken. And the error is really bizarre because it says that it cannot distinguish between a get and a post. And I'm saying, you know, get slash contacts. So whoa. What's happening? So it turns out that the way that actions are matched in MVC6 is different than how actions are matched in Web API, right? In Web API you could actually indicate the HTTP verb by the name of the action, right? That's why our actions are named like this, like get and, you know, it could be get by ID but it would still be get and then post and what have you, right? So this is no longer true in MVC6. So that's why this get and this post, they are being confused with each other, okay? So there are two ways to fix it. So first way to fix it is actually to go into these options and go into something that's called conventions. Maybe I zoom in so that you can actually see IntelliSense. And you could add Web API action conventions here, right? And this will look for all the API controllers and it will apply the typical action resolution that you might know from Web API to your MVC6 application, right? Now we don't want to do it because this type of fix we can apply manually as well. So the manual way to fix it is to actually stop using the route attribute and just use the verb specific versions of the attribute, okay? So this would become HTTP get, this would become HTTP post and so on, right? So this is just an alternative way of fixing it and we can do it. HTTP delete. There we are, okay? Whoops, I zoomed in somehow. So let's run and at least we are back where we were, right? So contact is working. But this we already had working before. Now what if I try to get a contact by ID? Ooh, something is not registered, right? And this something is I content negotiator. So this is the service that in Web API, oops, sorry, in Web API was responsible for a performing content negotiation, right? And if you look at our get by ID action, we are actually calling an extension method on the request that is supposed to produce HTTP response message, right? This extension method internally will use a service called I content negotiator. So what we can do in order to fix it, what are two ways, right? A, we could ditch HTTP response message completely and we can change it to I action result and then use something like return okay, for example. Or we can keep HTTP response message because we like it and we just have to register this I content negotiator, okay? Again, if we uncommented the Web API conventions here, all of this would be done for us but also a lot more and we don't want this, right? So we want to consciously apply what we want to be included in our application. So we can say add instance of I content negotiator and this would be new default content negotiator. And at this point, we also have to tell the content negotiator that it has a specific set of formatters to use, right? So there is another configuration option that's called the Web API compatibility and configuration options and here we can just say just use the default set of formatters. Okay, at this point, it will get us a step further, it will still not work though. So the reason I'm saying this is so that you don't think that my demos are not working, okay? So this is the problem. Now it's a bit more bizarre because it says, well, object content cannot be serialized, please mark it with data contract attribute. But how can I mark it with data contract attribute if it's a type in system HTTP which I don't control? Well, let's go back to our controller which is here and let's think for a moment what is happening here. Compare this with this get all action. So when this get all, we are returning a Poco or for that matter, a collection of Pocos and effectively the MVC6 pipeline is grabbing this Poco, serializing it to XML or JSON or whatever it's needed and then it's stuffing it into HTTP response. Whereas in this case, in the get by ID, we already have an HTTP response message but the MVC6 pipeline doesn't know it so it just tries to take this object and serialize it to JSON and stuff it into HTTP response which is incorrect behavior because it should just map it, right? So the way we can solve this is we can register a new output formatter that's called HTTP response message output formatter. This will special case situations where an HTTP response message is being returned. And there we are. So contact number four is working and in fact contact number three would also work and all of the other ones as well. So this has taken us a step further and it's actually quite nice. Now let's have a look at the other action. We don't have to look at all of them but I will show you just post because that's particularly interesting. So what I'm going to do, I actually had it already somewhere. There we are. Okay? So I'm going to post just a contact. The repository doesn't generate an ID so I'm passing an ID and the name is required. Okay? So I'm going to post this to create a new object on the server site. And what I'm getting from MVC6 is I'm getting information that it's an error 400 and that the name is required. Okay, there's a name here, right? So what is happening here? Whoops. Let's have a look at this post action. There's our post action. It turns out that out of the box, again, this is sort of compatibility with MVC rather than with Web API. Out of the box, the model binding would only work with query string and with form requests. So out of the box, you cannot bind the JSON to an object on an action. Okay? So there are several ways to fix it. Again, there is a special convention that you can apply. So if we go to our conventions, there is Web API parameter conventions, right? This is something that will kind of adjust or morph the MVC6 pipeline into something that is compatible with MVC6. Another way to do it is to actually annotate this with from body, right? And this is why when you generate a new project with Visual Studio Project Wizard and you choose the option Web API in MVC6, the samples controller will actually have those from body already enabled. So it's up to you how you want to choose it. And now if we run it, you can see that it's actually 201 created. And in fact, in the headers, we get a location from where I can actually retrieve this resource. Okay. So we have fixed this. Now, the next thing that we probably want to do is we want to deal with this filter. Okay? If we look at the response from the Web API, there is a self link. Okay? This is a poor man's hyper media. Okay? There is a self link. So in MVC6, this is actually empty right now because those links are populated by this filter. Okay? How it happens in Web API. Let me zoom in. There we are. So this is a contact link filter and it will be invoked after the action has executed. What happens is it grabs an instance of an HTTP response message. It will try to extract either an instance of a contact or a collection of contacts. And then it will annotate those objects or hydrate their self property with a link. Right? And this link is generated from a service called link provider, which is a custom service that I wrote for the purpose of this Web API. And what this service does, it just grabs a URL helper and it generates a link. Okay? Now, there is another interesting thing here. And there is this beautiful service locator call that we have here. Okay? So in Web API, doing dependency injection into filters was a bit tricky because filters were cached. So if you had a transient dependency or a per request dependency, it wasn't really that smart to inject it. So you had to inject a factory or some sort of a delegate or maybe you would have to rewrite the whole filter pipeline to make it not cached or cached based on different conditions. That's why a lot of people would use this type of service locator approach where they would just grab the dependency resolver, the current dependency resolver and just resolve an instance. Okay? So anyway, we can copy this whole filter if I managed to highlight it. And in my MVC6, I have a filters folder. And let's paste it. Okay? So some things are resolved already. What I'm going to do, I'm also going to copy this link provider because it will be required. So I'm just going to paste it here at the bottom. So what are the things that we want to fix? Actually, let's do it like this. What are the things that we want to fix? Well, first of all, there are action filter attributes in MVC6 as well. So this is actually very encouraging because it gives us a head start, right? Now, whoops. Okay. There we are. So the signature is obviously different. So I need to change it to action executed context. Okay? This will be action executed context as well. Now, on this context, there's no more response because there's no more HTTP response message, but there's something called a result. Okay? I don't know if you can see it here, but there's something called a result. Okay? And this is an I action result. Okay? Now, in this case, we know that we are dealing with contact or collection of contact. So we can actually safely cast it to object result. This will give us a little help because now we can extract either a contact or a collection of contact. And at this point, maybe, let's fix this link provider. So the link provider used to grab a URL helper of a request message, right? There's no more request message here, but there is a URL helper. So we can still use a URL helper. It's just that we will not be passing around HTTP request message. We'll be passing around HTTP context. So here is my context. And from this context, we can actually grab an instance of URL helper. It's request services, get required service, and it's called I URL helper. Okay? So this is not needed. And the method is called link. And it should probably be absolute. Actually, it should have been absolute all along. But anyway. So our link provider is fixed. Now, let's go back to the filter. So in this filter, we can get rid of this service locator call. And what we'll do for now is we'll just new up this link provider directly. We'll take care of this later. We just want to make this work. And now, link provider, it requires HTTP context, right? So this HTTP context should be here. Actually, it is. Yes. And the final thing that needs to be fixed is this casting. Actually, it's not needed at all because our response is this object result. Okay? So we can just reset it directly. Okay? So everything is correct here. And now at this point, the code is going to compile. What I need to fix is we just need to bring back this filter. And we can run. And you can see that now the self link is being populated correctly. And the same should happen for individual contacts. And that is correct. Now, the one last thing that probably requires fixing is the fact that we are not injecting it, right? So in MVC6, we can actually construct or inject it. So I could say link provider, link provider. And, whoa, this should become link provider. And then this is not needed. And this whole line is not needed. And in fact, this shouldn't be static anymore because it's using a field. And maybe this null check should go somewhere here. Okay? Now the problem is this is not going to compile because now we have injection into the constructor of a filter, right? And if I go back to our controller, now how do we annotate this, right? Because we have to pass in this instance somehow. Well, to do this, we can use one of the new things that arrived in MVC6 and that is the type filter. And I misspelled it, but it's called a type filter. And to this type filter, we should pass, guess what, a type. And this type filter will take care of instantiating the type for us. And in fact, how will the dependencies of this type get resolved? They will be resolved from the built-in IOC container, or DI container rather. And this means that this link provider should be registered somewhere here, right? And if we run this, you will see that it is indeed still working. And if we get by ID, we are still getting the sales link here. So this was another piece of functionality that we somehow managed to migrate. Now, there is another thing in our web API that we have. And that is, this is web API. I'm going to show you this. You have probably not seen it yet because it's just a header network. I'm going to zoom in here. Wow, I can't do it like this. So if I get a list of contacts, we get an extra header here. It's actually showing zero right now. But it's the execution time. Right now, it's so fast that it actually happens under one millisecond. That's why. But we have a message handler plugged into web API. That's called a timer handler. And this is a message handler that will measure how long the pipeline took to execute. And the way it works is just based on this Russian model of architecture that web API was built around where you have a handler that drops another handler, another handler, and so on. So as the request flows into the system, this is our outer handler. So we are intercepting the request. We are starting the stopwatch. Then we are dispatching the request into the inner pipeline. Everything else in web API executes. So all the filters, the controller action, everything else, the formatter. And then as the response goes out of the system, we intercept it again, we stop the stopwatch, and we populate the header. So we want to have the same type of functionality in MVC6. How can we do it? One way to do it is to maybe use a global filter. There are no more message handlers in MVC6. We could use a global filter that wraps everything, but it would only wrap the API pipeline. If we want to wrap really the entire ASP. We should use middleware, probably. So I can add a new class and let's create a new middleware. So let's call it a timer middleware. And there are different ways to write middleware components. This is just one of those. So the middleware is all convention-based, right? So it doesn't have to implement any interface. Everything that matters is just this delegate or this method here that could easily be a delegate as well. So it's just a method called invoke that takes in HTTP context and produces a task. So we can copy our code in here. And then let's just fix the response. So the response will be hanging off the HTTP context, right? So we will have headers here. Now this is actually an array in MVC6. So we should change it. And now the only thing remaining is to actually how to tell it to dispatch into the inner pipeline, right? Well, we could create a constructor here that takes in a request delegate. This will represent the next component in the pipeline. I can't type anymore. And in here we can just say, you know what? Just go to the next. And this should become async. Now the only thing that we need to do at this point is to plug in our middleware. And we already have this buffering middleware because I mentioned this before. Timer middleware. Okay. And now if I try to get something like maybe a list of contacts sxxml, we get execution time of one millisecond. So we have the same type of functionality, right? We needed just a little bit of coding to make this happen. All right. So next thing that I wanted to show you is another piece of interesting functionality that we had in Web API. Okay? So let me go to our Web API, which is 1176, I think. And let's say that our client accepts a media type that is not supported on the server side, like application PDF. So Web API is telling me 406, right? It's not acceptable. And this is a very valuable information for the client, right? Because the client knows that this particular media type should not be used to interact with the server anymore or this particular resource rather. So what happens if I do the same thing in MVC6? So I say application PDF and I'm getting JSON. So the server is very happy to tell me it's 200 and here is your data, right? So this is a bit bizarre and we want to fix it. And in fact, this is not a breaking change. In fact, this behavior was default in Web API as well. It's just that a lot of people would override it and would want to get 406 if the media type is not supported. And this fallback to JSON was a bit awkward. So how was it done in Web API? I enlarged this. In Web API, we had this default content negotiator, which is something that we have already briefly touched on. And this default content negotiator, it could be replaced with an instance that was created using its alternative constructor. And it had this constructor that allowed you to exclude matching on type only, which effectively meant exclude this fallback to JSON, okay? So because we deal with default content negotiator in MVC6 here as well, we could actually use the same constructor here. Now, this is only solving part of the problem. Why? Because if we go back to our controller, you should remember that this default content negotiator, it was only kicking in through this extension method. In the case of this, for example, or as a matter of fact here, just the regular content negotiation process that is part of MVC6 was taking over, okay? So how can we handle this? Well, there is an extra output formatter that should be registered. That's called not acceptable output formatter. And once we have this in place, we can ask for application PDF. And indeed, MVC6 is telling us 406. And the same is true actually to getting contact by its specific ID, okay? Okay. One more piece of functionality from Web API. So if I say I want a list of contacts, I'm getting a list of contacts based on the accept header. But I can say format JSON. And the accept header is being overwritten explicitly by me, okay? So this was an interesting piece of functionality. In fact, it could be done slightly different. It could be done also as.json, for example. This way you could pretend that you have a file, physical file, on your system that you don't. So how was it done in Web API? In Web API, there was something called media type mappings, okay? And you can see that we have here a JSON formatter, and we are actually adding a media type mapping, and we are telling this JSON formatter, listen, if there is a query string called format with a value called JSON, then the response will always be application JSON, which means that you always have to take over. Can this be done in MVC6? Yes, it can. There are also formatter mappings here. So you can see that we have formatter mappings where we can do stuff. But it turns out that actually the formatter mappings, they are enabled by default. So for example, for JSON, it's already enabled for us. It just happens to be that the controller has to opt into using this formatting. So there is a filter that's called format filter. And by annotating a controller, we are saying, please respect also the media type mappings. Okay, this could be registered globally if you just want to globally enable it for your entire application. And now if I say format JSON, I'm indeed getting format JSON. Okay? All right. So next thing that exists in our web API is a custom message hand, custom exception handler. This is our exception handler. So how would you handle exceptions in web API? Right? There are a few ways to do it, some better, some worse. But in web API one especially, the only way really to do it was to use an exception filter, right? So you'd have an exception filter. Similarly, how you might do it in the old MVC, you'd have an exception filter and this exception filter will catch some exceptions. The problem is it would not catch all of the exceptions. Okay? It would only catch those exceptions that originate within the action. So if the exception originates somewhere in the pipeline, maybe in the message handler, maybe in the formatter, it would be uncode. That's why if you're running on IIS, a lot of people would have another handler in the global ASAX catching all application errors because some of the exceptions were actually spilling. So in web API two, they introduce exception handler, an interface I exception handler and the base implementation exception handler. And you could use this to kind of wrap the entire web API pipeline with an exception handler. And this is exactly what is happening here. So we are catching the exception globally. We are creating a sort of a metadata object. And then if the request is local, we are adding an exception information to this metadata object. And then we are just returning a response to the client. So here I have a controller that doesn't do anything except it throws an exception. Okay? So let's see this in action. And you can see that, you know, the exception was coded by the exception handler. And because it's local, we actually get the exception information, the stack trace and what have you. Can we have the same type of functionality in VC6? Yes. But it's slightly different. Okay? So I'm going to show you. I'm going to copy this code. And here in our startup, what we can do, we can say, I don't know if you can see it. So let's do it like this. Actually, I'm going to make it 100% and I'm going to zoom. I think this would be better. So we can say use error handler. Use error handler. And what we have here is actually we are defining really a sub application in our application that will be used to handle errors. And this is really a sub application because what we have here is actually I application builder. So what we can have here, we could do something like use MVC. So we could have an MVC application to handle exceptions in our MVC application. Okay? It probably doesn't make sense. So we can just handle all of the exceptions in the same way. Okay? So I have pasted our code here. And this should be context. So let's get rid of those nasty underlines by just saying return. So what are the things that need fixing here? First of all, we have to determine somehow whether the request is local. Right? So in MVC or actually in ASP.net 5, everything about the request is actually split into the so-called features. Okay? So there is something called connection feature. So from the HTTP context, we can get I HTTP connection feature. And this connection feature will have a property is local. And it's a property not a method. Okay? So we have fixed this. Now another thing is how do we get the instance of the exception? Well, there is something called error feature. Okay? Let's just copy this. And it's I error handler feature. Okay? And on this error feature, there is a property called error, which just happens to be an exception. Okay? So this is exactly what we are looking for. I didn't come up with those names. So what else? Well, we have to write the client somehow. Right? So how can we write to the client? Well, on the HTTP context, we have response. Right? So it's actually really easy to write because we can just write to the response. And we can copy this JSON.net code and just put it here and then get rid of all this. And what we need to add is we need to add content type as well. Right? So probably what you would need to do, you would need to do some sort of content negotiation here rather than just implying JSON, but it's okay for now. And this should be a sync now because this method should be awaitable. Okay? Now the final thing to fix is actually the request URI. And this is a bit bizarre because I don't think there is a way to get a request URI from the HTTP context right now other than manually compose it. Because we have properties like path, we have properties like host, and we have properties like query string, but there is no entire request. So we should do something like this. To string plus context request path plus context request query string. Okay? And now, well, actually, we also need to copy this breaking controller, okay, just so that we can throw an exception. Let's move it to a separate file. Okay. At this point, we can run. And the result should be pretty much the same. And you can see that it is the same. We have the metadata object with the request URI with the exception. Also, the stack traces here is just dragged along because of the Watson bucket. But ultimately, the output is the same, right? So we have managed to kind of achieve the same type of functionality. All right. There is one more thing that I wanted to have a look at, I think, looking at my notes. See, I recently read a blog post by Troy Hunt. And he had those tips for the speakers, right? And he said, like, keep notes and time slots and everything. So I have everything here. So, well done, Troy. You have educated the speaker. All right. So according to my notes, I actually have one more type of functionality that we should be looking at. Okay? And that is a custom formatter. Okay? So in our web API, let's just close all these windows because they are confusing. So in our web API, which was at port 1174, we actually have CSV as well in play. Okay? Maybe not. I think I shut down the... Ah! Well done. So, by the way, well, we will do it later, actually. Don't worry. But this reminded me of something. Six. Very well. You get an award. So here is my CSV output. Okay? So if the client says it's handling CSV, web API can return CSV. And in fact, this will only work for the collections because if I say, please give me contact number one as CSV, it's not a collection, so it is not acceptable. Okay? How is it done in web API? In web API, we have something called CSV media type formatter. Okay? And this is taking care of serializing a collection into a CSV format. Okay? I actually copied this from web API project. And what is happening here? Well, as we mentioned before, formatters in web API, they were bi-directional, so they were used for reading and writing. So in this case, this formatter is declaring that I cannot read anything. I can only write stuff. And I can only write stuff if the type in question is inumerable. Okay? And then we have a method write to stream a thing, which does a bunch of things. Doesn't matter. Some reflection, blah, blah, blah. And then at the end of the day, it just writes to the response stream. Okay? So can we have the same type of functionality in MVC6? Yes, we can. And I'm going to add a new class. And this will be called CSV output formatter. And it should inherit from output formatter. There's one method to implement. And that is write response body async. This is kind of similar to what we had before, because before we had write to stream async. The difference is we had a bunch of different parameters here. Here we just have a context, but this context is pretty much the same thing. So what we can do, we can actually highlight this, just copy the verbatim and paste it here. And let's just fix the errors. So there is context. Actually, it would be better to do it like this. So there is context declared type, context declared type. There will be context object here as well. And then here, we don't have a stream, but there will be context. HCP context, whoops, HCP context, response. And then the body on the response is actually a stream. Okay? So we can write to it. And now the only thing missing is async. Okay? So let's migrate the remaining pieces like is the type inumerable? I think at this point. We can enlarge it. Is it inumerable? And then we should also say what are the media types that are supported? Well, the encoding support is actually also necessary. So we should say that it's UTF-8. And then supported media types in this case will be media type header value, text CSV. Right? So that is the same. And now we should also tell this formatter to only kick in if we are dealing with inumerable, which is exactly the same condition as we had before. Right? And this will be the runtime type. Okay? So I think we have everything. And this should build. Oh! Oh, see, actually, this is interesting. So it doesn't build. And you can see why it doesn't build. It's because the DNX core is actually failing. Because we are targeting this project for both DNX451 and DNX core as well. Because that's the reason why you want to use ASP.NET 5 to run on Core CLR. And in Core CLR, there are actually situations where you have to pull extra packages that are relevant for Core CLR. So in our case, actually, in our project JSON, we actually have some extra dependencies for DNX core that actually did it up front because I was afraid that the network might not be working here. There is actually a system reflections type extensions package. Okay? So what this means is that even though this code looks like it's compiling for DNX451, actually for DNX core, we actually need to include here system reflection. Okay? And now it's building. So what we need to do, we just need to register our new formator, new CSV, output formator, and we can get going. And now if I ask for contact SCSV, we actually get a list of contacts SCSV. And for a single contact SCSV, we get 406, which is exactly what was expected. So we have just one last thing to show, and we still have maybe three minutes, so I can do it really, really quickly. There was one interesting thing in Web API, and that is you could have a controller that would only work with a specific media type. Okay? So in this example, it was quite common actually for people to use an HTML controller, for example. Right? So I have here a controller that no matter what will always return CSV. And the way this was achieved in Web API was through this CSV-only attribute, and you could actually configure your controller to work only with a specific set of formators. Right? So in this case, we are saying that, you know what, this controller would only use CSV media type formator, no matter what. And we can have the same thing in MVC6, and it's actually much easier because we don't have to create a custom attribute. The attribute is actually already there. It's called produces. And this produces attribute allows us to specify the media type which are produced by this specific controller or the specific action can be used on the action as well. It could actually be multiple here as well, so we could have application whatever here. Right? And in this case, now that we have it in place, all I need to do is I just run this, and if I go to this specific route, look, I can remove the accept header, and I can say CSV contact, and it's actually giving me CSV no matter what. So it's actually an interesting way to solve this problem, and actually this is one of the things that I really like. I think this is everything. We have actually gone through a bunch of different Web API components, a bunch of different things that you might have used in your Web API, also a bunch of conventions. We have consciously applied them to MVC6. We haven't used the Web API compatibility in blindly. We just make sure that we want the pieces that we really need. And all this code will be on GitHub. I will put it up there, and I think that's about it. So if there are any questions, I think we still have maybe a couple minutes. We can do some questions, and if not, thank you very much. Yes, Brok has a question. There are things, for example, like parameter overloading rules that not necessarily you want there. In general, I think what you want is some of the pieces there, they are very useful conventions, but for example, the compatibility layer will only kick in if you inherit from API controller. Now, for example, I would like to use some of the conventions that they have there, but I would like to apply them to an MVC6 controller. And if you just use the compatibility layer, just use the extension method, that would not happen. So it's just a matter of cherry peaking stuff. I don't know if that answers your question. Yes? It was your buffering middle web, by the way. Credit where credit is due. The timing wouldn't. No. No. It was just for that. Good catch. Yes. Yes. Exactly. All right. So one more thing is that I actually have my last two copies of my book that I just wanted to give away. So if somebody wants a Web API book, I have two copies to give away. Just don't be shy. Just come here, say that you want a book and there's no strings attached. Nothing. All right. Thank you.
 Alright, welcome. My name is Philip and this is a session about migrating from Web API  to MVC6. Before we begin, just a couple of words about myself, okay, because some of  you might be wondering who this guy is, why is he trying to talk to us today, why is he  dressed like a football hooligan, and in general, what's the story here? Like I said, my name  is Philip, my last name, nobody can pronounce it, so let's just say it's W. These days,  everyone's on the Internet, so I'm on the Internet as well. I have a blog at stratweb.com  and I blog quite a lot about ASP.net, so if you are ASP on the developer, chances are  maybe you have seen some of the stuff already. I'm also involved in quite a few open source  projects, some larger, some smaller, so I spend quite some time on GitHub as well. And my  Twitter handle is philip.dob. And as far as my professional engagement goes, I work for  a software development company called Climax Media, that's a company out of Toronto, Canada.  So this is something that we'll kind of tie into the stuff that we'll be talking about  today. So last year, I wrote this book, it's a web API book, and you know how it is, it  takes a bit of an effort to write a book and you don't do it every week, right, like you  don't do it every month probably as well. And for me, it was kind of a big deal, but  for a very short while, and I literally mean a short while, because you know what happened,  web API is kind of gone. I mean, it's not gone, it's still there, but it is becoming legacy  technology, right, so this is why we are here today to discuss this. And a couple of months  ago, I was having a conversation with one of the clients of ours and he started asking  some really valid questions, like you know, he said that they have invested heavily in  web API and web API has become their web technology of choice and this and that. And at the same  time, he's starting to hear out of Microsoft that you know there's ASPnet 5 coming, it  will be cross-platform, been deployable runtime, all these kinds of cool things that ASPnet  5 promises to bring. And you know, they want to stay evergreen, so like he asked, what  am I supposed to do, right, like how do I approach this? We want to be evergreen, so  we want to somehow update. Do we run a tool? Does the tool upgrade us? Do we pull in a  package and the package magically acts as a proxy or whatever? Like what do we do, right?  And I thought maybe it will actually be interesting to take a web API project, to take an empty  MPC project and just migrate piece by piece and hopefully this is something that can save  your time in the future as well, okay? But before we get there, this is a brief history  of HTTP services in.NET and there's a few different technologies over the years and  you know, if you take the age of.NET and you divide by the number of these technologies,  it probably averages out like a technology per two years or something. And of course,  this is not ideal from the developer's perspective and chances are you have already done migration  between those technologies. And now we are at a point where we have web API and we kind  of have to start thinking about moving forward, right? So of course, you could make jokes  about this, right? But the reality is MVC6 is actually a really good framework, okay?  So most people probably don't realize it. And in fact, that's the story. The name is  not super marketable, right? I know the marketing guys, so probably my opinion doesn't matter.  But like if I'm doing Node or something, I'm not getting excited about iteration number  five of active server pages or iteration number six of some framework on top of it. But the  reality is everything is being rewritten from scratch, right? So ASP.NET 5 is brand new.  MVC6 is a brand new framework, even though it suggests that it's a continuation of an  existing framework. So, you know, we mentioned a few benefits already, like, you know, you  will be able to run on any platform or to have the runtime package together with your  applications, where your application becomes self-contained, those types of things. And  then MVC6 itself as a framework, it's actually extremely flexible and very configurable.  And it has to be. And the reason why it has to be is that it's really the only framework  that Microsoft is proposing for ASP.NET 5, right? There's no more web API, there's no  more web forms, there's no more web pages, and really MVC in the old shape and form.  So it kind of, like, it has to be flexible because it has to cater for all these specific  scenarios that you might have used some alternative frameworks for.  Okay, so if you think about migration of web API, right, there's a few things you have  to consider. So you have to consider that MVC6 runs on top of DNx, so what does it mean?  Well, it means that if you want to run on core CLR, you will actually have to migrate  all of your source code, right? You will not be able to pull in some existing binaries,  like, it means that all of the source code has to be migrated because on core CLR you  can only use packages that were built for core CLR.  Then you have to use a new set of HTTP abstractions. This is actually quite a shame because the  HTTP abstractions that were part of web API, they were actually really good. And most of  the developers have actually grown to like them a lot, you know, the system-net HTTP  abstractions. With web API, we had beautiful client server symmetry because you would work  with HTTP request message, HTTP response message, both on client side and on the server side.  So there is still a way to work with system-net HTTP abstractions with MVC6, but it requires  some configuration and in many situations it's not ideal.  Another thing is that MVC6, and this has to be remembered, it's kind of geared towards  compatibility or semi-compatibility with MVC5. And this really means that a lot of the out  of the box behaviors, a lot of the conventions, they would resemble those from MVC5. And if  you think about moving your web API code over, you will quite often end up in situations  where it might actually be frustrating because things don't work as they might be expected  to. And in general, this could be slightly confusing. So we mentioned the flexibility.  And then there is one more project that I wanted to highlight today, and that is the  so-called web API compatibility scheme. And this is one of the projects from the MVC6  project family. And this project is kind of a bridge between web API and MVC6, so you  can actually use this to ease your transition from web API to MVC6. So we will have a look  at this in this session as well.  Now the problem with web API compatibility scheme is its name, right? Name, shim. It  sounds dirty. We all know the concept of shim in software development is an old concept.  It's just as the name of a product or a package, it sounds like duct tape, right? Would you  put duct tape in production? Probably not. But anyway, here is a tweet from my friend  Darrell. And Darrell is a great contributor to the ASP.net community. And he said this,  right? And I completely agree. So again, it's just about the name, but it would be nice  if the name could be changed. If somebody from Microsoft is actually watching this, where  is John Galloway? He was supposed to be here. Anyway, I'm done, actually. Thank you.  So a while ago when I was thinking about this session, I was actually, you know, weighing  different options like how we could actually do it. And I had this brilliant idea that  actually instead of showing boring slides, I would just show code, right? I would just  take a web API project, I would take a MVC project, and we can migrate piece by piece  and we can discuss different things that maybe have different traps that you have to avoid  and those types of things. And this seemed like a brilliant idea at the time. Now I'm  starting to see flaws in this plan because it actually involves me live coding for about  50 minutes on stage. And, you know, to be honest, I don't think I have ever managed to  go 50 minutes straight coding without making a massive mistake. So it might be actually  entertaining for you. Anyway, this is the stuff that I have. I don't know if you can  see it, we can make it bigger. Is it okay? I get the glasses. Okay. As you wish. Like  this? Okay. So I have a web API project. And this web API project is actually a project  that was created by Microsoft in 2012. It was one of the projects that Dan wrote, was  using to show web API to the world. It's a contacts manager. Of course, I actually have  modified it quite heavily because I wanted to get in different concepts. But the core  is really the same. And I thought it would be nice to use the same project that was  used to introduce web API to actually migrate away from web API today. Yeah. So this is  the project. It's actually, the surface API is actually really simple. However, there  is a lot of things going on in there. I have a lot of different web API components plugged  in. There is a custom message handler, there's a custom filter, there's a custom formatter,  there's a custom exception handler. A lot of different conventions are customized and  overridden. So in general, a lot of things going on here. And we'll move them piece by  piece and we'll try to get the same type of behavior in MVC6. So on the other side, I  have MVC6 and, okay, this is MVC6. Oh, I can zoom here. I forgot about this. So I have  a MVC6 project. It's an empty project. The only thing that I did to it, I actually migrated  the models, like, you know, the contact and the error information and the repository. And  I also plugged in somewhere here. I also plugged in a buffering middleware because we want  to buffer in ASP.NET 5 which doesn't happen out of the box. The rest is just a project  straight from the ASP.NET 5 template, okay? And in fact, we mentioned web API compatibility  scheme already, okay? So in fact, we do have a little message here from the nice folks  at Microsoft that says, please uncomment this line if you are migrating web API and this  will actually make the web API compatibility scheme kick in, right? So there is an extension  method that you can uncomment. Now, we will not do it because there is a ton of magic  happening behind the scenes when you do it, right? So instead, what I want to do, I want  to go piece by piece and then we can identify the problems and we can consciously apply  solutions to those problems, okay? So let me run this. Actually, let's zoom this as  well. So this is our web API. Let me zoom this. Okay. So just a bunch of contacts, right?  That's nothing exciting but again, as we go forward, I will show you different components  and what they do, okay? And you can get a contact by ID as well if you want. So how  do we approach this migration? Okay. So let's first, so we have a contacts controller, okay?  So I'm just going to copy this and I will only take, actually, I will only take the get action,  okay? So in here, I have to remember about zooming. Oops, I did something wrong. What  did I do? I think I dragged the folder. Okay. So let's add a new class. There we are, like  this. And let's paste it, okay? Not very exciting at the moment but it will get better. So what  do we have? Let's comment out this filter. The filter will migrate a bit later, okay?  So what are the, so look, everything seems to be compiling except the route attribute.  So actually, there's no more route prefix in MVC6. It's just called route, okay? And  then the route from the class and the route from the action, they just get concatenated  with each other. It's an easy fix, no problem. Now, somehow the API controller is actually  also recognized even though it's a web API controller rather than MVC controller, okay?  So what is happening here? So if I go to the definition of this thing, you will see that  this is actually part of this web API compatibility scheme, okay? So we can actually use it to  ease our migration process. This is especially relevant if we use stuff like methods that  were defined on the base controller and things like that. Now, what you notice here is that  actually this API controller doesn't inherit from an MVC6 controller. It's a so-called  POCO controller. So POCO controllers, they are allowed in MVC6 and you can use them.  And in fact, this base controller is actually a POCO controller. So we can inherit from  it or we don't have to. So for now, I'm going to choose to not inherit from it and just  use straightaway POCO controller, okay? So if I run this, it will not work, but I will  just show you that we can get the message that can help us solve the problem. So the  problem is obviously the eye contact repository is not recognized, okay? So this is the web  API again. How was it solved in web API? So in web API, there we are, we had autofuck  plugged in, right? So we were using autofuck IOC container and it was set to the web API  dependency resolver, right? So that's how it was done in web API. We could do a similar  thing in MVC6 because autofuck is actually supported. There is autofuck for DNx, but  we don't have to do it because at this point, I think the majority of people would know  that MVC6 or actually ASP.net5 comes with built-in dependency injection, right? So we  can actually just use that, right? So we can register contact repository as in-memory contact  repository and there we are, okay? And now I'm getting some output, okay? So this is  promising. It's not identical to the stuff that we have in web API because in web API,  this is web API, we actually are getting XML, right? And here we are getting JSON. So what's  the story here? Well, it turns out that in web API, XML formatters and support for XML-based  media types was actually built-in. In MVC6, you actually have to opt into it, right? So  rather than being opt out, it's opt-in. So the way we can do it, we can go to this add  MVC and there is a MVC configuration object that we can act upon. This is the MVC options.  And in here we can say add XML data contract serializer formatter, okay? This will add  both an output formatter and an input formatter for XML. In MVC6, the formatters have been  split into input and output formatters. In web API, you only dealt with a single type  of formatter. So it was bidirectional. In MVC6, the formatters are unidirectional. Anyway,  this extension method will register both. So if I now run our solution, well, it's not  working, right? What's happened? Well, it turns out that it's actually working. So I'm going  to go to Postman, which is a bit more advanced tool, okay? And I'm going to... Oh, this is  bizarre. Okay. I'm going to say accept text XML. And I'm getting XML now, which is a  bit awkward because why doesn't it work from the browser, from the address bar of the browser?  This is another default behavior of MVC6. So MVC6 will actually try to sniff out whether  the request is coming from the browser address bar and it's going to just respond with JSON  for your own convenience. So anyway, there is a setting that says respect browser accept  header. And I would like to respect accept header, right? And now... Yes. Okay. Very good.  So we have reached a certain milestone. We have made one action work. Okay. So let's  go further. And let's migrate the rest of it. Okay? So I will... Okay, I don't need to  use them here. So I will just copy all of those. Oh, what is happening? Let's just copy  all of those. There's like get by ID and post and put and what have you, right? Now, the  one thing that I would like to highlight in this Web API controller is that there are  different ways to create an HTTP response to the client, right? So first we have actually...  We have a POCO, right? So we are actually returning a POCO. And then Web API pipeline is taking  over. Here we are actually returning HTTP response message. And this response message  is being created by an extension method here. This extension method internally in Web API  will do content negotiation and will produce an instance of HTTP response message and will...  This response message will then be sent to the client. And then finally here we actually  have IHTP action result which was introduced in Web API 2. And what we are using, we are  using actually a method on the base class created at the route and this will create a relevant  action result and that will be used to produce an HTTP response message. So there are three  ways... There were three ways in Web API. So let's just move over this code. Maybe I zoom  here as well. So at this point we have some issues. So some things were recognized like  HTTP response message. Even though it's an HTTP abstraction that doesn't really exist  in MVC6, it was brought in by this compatibility shim. So the compatibility shim will actually  drag the system that HTTP abstraction over. So we have it. At least the code will compile,  right? Now we have to fix those methods that were called on the base class and also the  properties coming from the base class. So at this point we can actually use the compatibility  shim and we can actually inherit from the API controller, right? And now somehow everything  seems to be okay. The only thing that needs to be fixed is this HTTP action result which  doesn't exist anymore because it's called IActionResult. So this is very exciting because the code  is compiling, right? Now I can run it and everything is broken. And the error is really  bizarre because it says that it cannot distinguish between a get and a post. And I'm saying,  you know, get slash contacts. So whoa. What's happening? So it turns out that the way that  actions are matched in MVC6 is different than how actions are matched in Web API, right?  In Web API you could actually indicate the HTTP verb by the name of the action, right?  That's why our actions are named like this, like get and, you know, it could be get by  ID but it would still be get and then post and what have you, right? So this is no longer  true in MVC6. So that's why this get and this post, they are being confused with each other,  okay? So there are two ways to fix it. So first way to fix it is actually to go into  these options and go into something that's called conventions. Maybe I zoom in so that  you can actually see IntelliSense. And you could add Web API action conventions here,  right? And this will look for all the API controllers and it will apply the typical action  resolution that you might know from Web API to your MVC6 application, right? Now we don't  want to do it because this type of fix we can apply manually as well. So the manual  way to fix it is to actually stop using the route attribute and just use the verb specific  versions of the attribute, okay? So this would become HTTP get, this would become HTTP post  and so on, right? So this is just an alternative way of fixing it and we can do it. HTTP delete.  There we are, okay? Whoops, I zoomed in somehow. So let's run and at least we are back where  we were, right? So contact is working. But this we already had working before. Now what  if I try to get a contact by ID? Ooh, something is not registered, right? And this something  is I content negotiator. So this is the service that in Web API, oops, sorry, in Web API was  responsible for a performing content negotiation, right? And if you look at our get by ID action,  we are actually calling an extension method on the request that is supposed to produce  HTTP response message, right? This extension method internally will use a service called  I content negotiator. So what we can do in order to fix it, what are two ways, right?  A, we could ditch HTTP response message completely and we can change it to I action result and  then use something like return okay, for example. Or we can keep HTTP response message because  we like it and we just have to register this I content negotiator, okay? Again, if we uncommented  the Web API conventions here, all of this would be done for us but also a lot more and  we don't want this, right? So we want to consciously apply what we want to be included in our application.  So we can say add instance of I content negotiator and this would be new default content negotiator.  And at this point, we also have to tell the content negotiator that it has a specific  set of formatters to use, right? So there is another configuration option that's called  the Web API compatibility and configuration options and here we can just say just use  the default set of formatters. Okay, at this point, it will get us a step further, it will  still not work though. So the reason I'm saying this is so that you don't think that my demos  are not working, okay? So this is the problem. Now it's a bit more bizarre because it says,  well, object content cannot be serialized, please mark it with data contract attribute.  But how can I mark it with data contract attribute if it's a type in system HTTP which I don't  control? Well, let's go back to our controller which is here and let's think for a moment  what is happening here. Compare this with this get all action. So when this get all,  we are returning a Poco or for that matter, a collection of Pocos and effectively the  MVC6 pipeline is grabbing this Poco, serializing it to XML or JSON or whatever it's needed  and then it's stuffing it into HTTP response. Whereas in this case, in the get by ID, we  already have an HTTP response message but the MVC6 pipeline doesn't know it so it just  tries to take this object and serialize it to JSON and stuff it into HTTP response which  is incorrect behavior because it should just map it, right? So the way we can solve this  is we can register a new output formatter that's called HTTP response message output  formatter. This will special case situations where an HTTP response message is being returned.  And there we are. So contact number four is working and in fact contact number three would  also work and all of the other ones as well. So this has taken us a step further and it's  actually quite nice. Now let's have a look at the other action. We don't have to look  at all of them but I will show you just post because that's particularly interesting. So  what I'm going to do, I actually had it already somewhere. There we are. Okay? So I'm going  to post just a contact. The repository doesn't generate an ID so I'm passing an ID and the  name is required. Okay? So I'm going to post this to create a new object on the server  site. And what I'm getting from MVC6 is I'm getting information that it's an error 400  and that the name is required. Okay, there's a name here, right? So what is happening here?  Whoops. Let's have a look at this post action. There's our post action. It turns out that  out of the box, again, this is sort of compatibility with MVC rather than with Web API. Out of the  box, the model binding would only work with query string and with form requests. So out  of the box, you cannot bind the JSON to an object on an action. Okay? So there are several  ways to fix it. Again, there is a special convention that you can apply. So if we go  to our conventions, there is Web API parameter conventions, right? This is something that  will kind of adjust or morph the MVC6 pipeline into something that is compatible with MVC6.  Another way to do it is to actually annotate this with from body, right? And this is why  when you generate a new project with Visual Studio Project Wizard and you choose the option  Web API in MVC6, the samples controller will actually have those from body already enabled.  So it's up to you how you want to choose it. And now if we run it, you can see that it's  actually 201 created. And in fact, in the headers, we get a location from where I can  actually retrieve this resource. Okay. So we have fixed this. Now, the next thing that  we probably want to do is we want to deal with this filter. Okay? If we look at the  response from the Web API, there is a self link. Okay? This is a poor man's hyper media.  Okay? There is a self link. So in MVC6, this is actually empty right now because those  links are populated by this filter. Okay? How it happens in Web API. Let me zoom in.  There we are. So this is a contact link filter and it will be invoked after the action has  executed. What happens is it grabs an instance of an HTTP response message. It will try to  extract either an instance of a contact or a collection of contacts. And then it will  annotate those objects or hydrate their self property with a link. Right? And this link  is generated from a service called link provider, which is a custom service that I wrote for  the purpose of this Web API. And what this service does, it just grabs a URL helper and  it generates a link. Okay? Now, there is another interesting thing here. And there is this  beautiful service locator call that we have here. Okay? So in Web API, doing dependency  injection into filters was a bit tricky because filters were cached. So if you had a transient  dependency or a per request dependency, it wasn't really that smart to inject it. So  you had to inject a factory or some sort of a delegate or maybe you would have to rewrite  the whole filter pipeline to make it not cached or cached based on different conditions.  That's why a lot of people would use this type of service locator approach where they  would just grab the dependency resolver, the current dependency resolver and just resolve  an instance. Okay? So anyway, we can copy this whole filter if I managed to highlight  it. And in my MVC6, I have a filters folder. And let's paste it. Okay? So some things are  resolved already. What I'm going to do, I'm also going to copy this link provider because  it will be required. So I'm just going to paste it here at the bottom. So what are the  things that we want to fix? Actually, let's do it like this. What are the things that  we want to fix? Well, first of all, there are action filter attributes in MVC6 as well.  So this is actually very encouraging because it gives us a head start, right? Now, whoops.  Okay. There we are. So the signature is obviously different. So I need to change it to action  executed context. Okay? This will be action executed context as well. Now, on this context,  there's no more response because there's no more HTTP response message, but there's something  called a result. Okay? I don't know if you can see it here, but there's something called  a result. Okay? And this is an I action result. Okay? Now, in this case, we know that we are  dealing with contact or collection of contact. So we can actually safely cast it to object  result. This will give us a little help because now we can extract either a contact or a collection  of contact. And at this point, maybe, let's fix this link provider. So the link provider  used to grab a URL helper of a request message, right? There's no more request message here,  but there is a URL helper. So we can still use a URL helper. It's just that we will not  be passing around HTTP request message. We'll be passing around HTTP context. So here is  my context. And from this context, we can actually grab an instance of URL helper. It's  request services, get required service, and it's called I URL helper. Okay? So this is  not needed. And the method is called link. And it should probably be absolute. Actually,  it should have been absolute all along. But anyway. So our link provider is fixed. Now,  let's go back to the filter. So in this filter, we can get rid of this service locator call.  And what we'll do for now is we'll just new up this link provider directly. We'll take  care of this later. We just want to make this work. And now, link provider, it requires  HTTP context, right? So this HTTP context should be here. Actually, it is. Yes. And  the final thing that needs to be fixed is this casting. Actually, it's not needed at  all because our response is this object result. Okay? So we can just reset it directly. Okay?  So everything is correct here. And now at this point, the code is going to compile. What  I need to fix is we just need to bring back this filter. And we can run. And you can see  that now the self link is being populated correctly. And the same should happen for individual  contacts. And that is correct. Now, the one last thing that probably requires fixing is  the fact that we are not injecting it, right? So in MVC6, we can actually construct or inject  it. So I could say link provider, link provider. And, whoa, this should become link provider.  And then this is not needed. And this whole line is not needed. And in fact, this shouldn't  be static anymore because it's using a field. And maybe this null check should go somewhere  here. Okay? Now the problem is this is not going to compile because now we have injection  into the constructor of a filter, right? And if I go back to our controller, now how do  we annotate this, right? Because we have to pass in this instance somehow. Well, to do  this, we can use one of the new things that arrived in MVC6 and that is the type filter.  And I misspelled it, but it's called a type filter. And to this type filter, we should  pass, guess what, a type. And this type filter will take care of instantiating the type for  us. And in fact, how will the dependencies of this type get resolved? They will be resolved  from the built-in IOC container, or DI container rather. And this means that this link provider  should be registered somewhere here, right? And if we run this, you will see that it is  indeed still working. And if we get by ID, we are still getting the sales link here.  So this was another piece of functionality that we somehow managed to migrate. Now, there  is another thing in our web API that we have. And that is, this is web API. I'm going to  show you this. You have probably not seen it yet because it's just a header network.  I'm going to zoom in here. Wow, I can't do it like this. So if I get a list of contacts,  we get an extra header here. It's actually showing zero right now. But it's the execution  time. Right now, it's so fast that it actually happens under one millisecond. That's why.  But we have a message handler plugged into web API. That's called a timer handler. And  this is a message handler that will measure how long the pipeline took to execute. And  the way it works is just based on this Russian model of architecture that web API was built  around where you have a handler that drops another handler, another handler, and so on.  So as the request flows into the system, this is our outer handler. So we are intercepting  the request. We are starting the stopwatch. Then we are dispatching the request into the  inner pipeline. Everything else in web API executes. So all the filters, the controller  action, everything else, the formatter. And then as the response goes out of the system,  we intercept it again, we stop the stopwatch, and we populate the header. So we want to  have the same type of functionality in MVC6. How can we do it? One way to do it is to maybe  use a global filter. There are no more message handlers in MVC6. We could use a global filter  that wraps everything, but it would only wrap the API pipeline. If we want to wrap really  the entire ASP. We should use middleware, probably. So I can add a new class and let's  create a new middleware. So let's call it a timer middleware. And there are different  ways to write middleware components. This is just one of those. So the middleware is  all convention-based, right? So it doesn't have to implement any interface. Everything  that matters is just this delegate or this method here that could easily be a delegate  as well. So it's just a method called invoke that takes in HTTP context and produces a  task. So we can copy our code in here. And then let's just fix the response. So the response  will be hanging off the HTTP context, right? So we will have headers here. Now this is  actually an array in MVC6. So we should change it. And now the only thing remaining is to  actually how to tell it to dispatch into the inner pipeline, right? Well, we could create  a constructor here that takes in a request delegate. This will represent the next component  in the pipeline. I can't type anymore. And in here we can just say, you know what? Just  go to the next. And this should become async. Now the only thing that we need to do at this  point is to plug in our middleware. And we already have this buffering middleware because  I mentioned this before. Timer middleware. Okay. And now if I try to get something like  maybe a list of contacts sxxml, we get execution time of one millisecond. So we have the same  type of functionality, right? We needed just a little bit of coding to make this happen.  All right. So next thing that I wanted to show you is another piece of interesting functionality  that we had in Web API. Okay? So let me go to our Web API, which is 1176, I think. And  let's say that our client accepts a media type that is not supported on the server side,  like application PDF. So Web API is telling me 406, right? It's not acceptable. And this  is a very valuable information for the client, right? Because the client knows that this  particular media type should not be used to interact with the server anymore or this particular  resource rather. So what happens if I do the same thing in MVC6? So I say application PDF  and I'm getting JSON. So the server is very happy to tell me it's 200 and here is your  data, right? So this is a bit bizarre and we want to fix it. And in fact, this is not  a breaking change. In fact, this behavior was default in Web API as well. It's just that  a lot of people would override it and would want to get 406 if the media type is not supported.  And this fallback to JSON was a bit awkward. So how was it done in Web API? I enlarged this.  In Web API, we had this default content negotiator, which is something that we have already briefly  touched on. And this default content negotiator, it could be replaced with an instance that  was created using its alternative constructor. And it had this constructor that allowed you  to exclude matching on type only, which effectively meant exclude this fallback to JSON, okay?  So because we deal with default content negotiator in MVC6 here as well, we could actually use  the same constructor here. Now, this is only solving part of the problem. Why? Because  if we go back to our controller, you should remember that this default content negotiator,  it was only kicking in through this extension method. In the case of this, for example,  or as a matter of fact here, just the regular content negotiation process that is part of  MVC6 was taking over, okay? So how can we handle this? Well, there is an extra output  formatter that should be registered. That's called not acceptable output formatter. And  once we have this in place, we can ask for application PDF. And indeed, MVC6 is telling  us 406. And the same is true actually to getting contact by its specific ID, okay? Okay. One  more piece of functionality from Web API. So if I say I want a list of contacts, I'm  getting a list of contacts based on the accept header. But I can say format JSON. And the  accept header is being overwritten explicitly by me, okay? So this was an interesting piece  of functionality. In fact, it could be done slightly different. It could be done also  as.json, for example. This way you could pretend that you have a file, physical file, on your  system that you don't. So how was it done in Web API? In Web API, there was something  called media type mappings, okay? And you can see that we have here a JSON formatter,  and we are actually adding a media type mapping, and we are telling this JSON formatter, listen,  if there is a query string called format with a value called JSON, then the response will  always be application JSON, which means that you always have to take over. Can this be  done in MVC6? Yes, it can. There are also formatter mappings here. So you can see that  we have formatter mappings where we can do stuff. But it turns out that actually the  formatter mappings, they are enabled by default. So for example, for JSON, it's already enabled  for us. It just happens to be that the controller has to opt into using this formatting. So  there is a filter that's called format filter. And by annotating a controller, we are saying,  please respect also the media type mappings. Okay, this could be registered globally if  you just want to globally enable it for your entire application. And now if I say format  JSON, I'm indeed getting format JSON. Okay? All right. So next thing that exists in our  web API is a custom message hand, custom exception handler. This is our exception handler. So  how would you handle exceptions in web API? Right? There are a few ways to do it, some  better, some worse. But in web API one especially, the only way really to do it was to use an  exception filter, right? So you'd have an exception filter. Similarly, how you might  do it in the old MVC, you'd have an exception filter and this exception filter will catch  some exceptions. The problem is it would not catch all of the exceptions. Okay? It would  only catch those exceptions that originate within the action. So if the exception originates  somewhere in the pipeline, maybe in the message handler, maybe in the formatter, it would be  uncode. That's why if you're running on IIS, a lot of people would have another handler  in the global ASAX catching all application errors because some of the exceptions were  actually spilling. So in web API two, they introduce exception handler, an interface I  exception handler and the base implementation exception handler. And you could use this to  kind of wrap the entire web API pipeline with an exception handler. And this is exactly  what is happening here. So we are catching the exception globally. We are creating a  sort of a metadata object. And then if the request is local, we are adding an exception  information to this metadata object. And then we are just returning a response to the client.  So here I have a controller that doesn't do anything except it throws an exception. Okay?  So let's see this in action. And you can see that, you know, the exception was coded by  the exception handler. And because it's local, we actually get the exception information,  the stack trace and what have you. Can we have the same type of functionality in VC6?  Yes. But it's slightly different. Okay? So I'm going to show you. I'm going to copy  this code. And here in our startup, what we can do, we can say, I don't know if you can  see it. So let's do it like this. Actually, I'm going to make it 100% and I'm going to  zoom. I think this would be better. So we can say use error handler. Use error handler.  And what we have here is actually we are defining really a sub application in our application  that will be used to handle errors. And this is really a sub application because what we  have here is actually I application builder. So what we can have here, we could do something  like use MVC. So we could have an MVC application to handle exceptions in our MVC application.  Okay? It probably doesn't make sense. So we can just handle all of the exceptions in  the same way. Okay? So I have pasted our code here. And this should be context. So let's  get rid of those nasty underlines by just saying return. So what are the things that  need fixing here? First of all, we have to determine somehow whether the request is local.  Right? So in MVC or actually in ASP.net 5, everything about the request is actually split  into the so-called features. Okay? So there is something called connection feature. So  from the HTTP context, we can get I HTTP connection feature. And this connection feature will  have a property is local. And it's a property not a method. Okay? So we have fixed this.  Now another thing is how do we get the instance of the exception? Well, there is something  called error feature. Okay? Let's just copy this. And it's I error handler feature. Okay?  And on this error feature, there is a property called error, which just happens to be an  exception. Okay? So this is exactly what we are looking for. I didn't come up with those  names. So what else? Well, we have to write the client somehow. Right? So how can we write  to the client? Well, on the HTTP context, we have response. Right? So it's actually  really easy to write because we can just write to the response. And we can copy this JSON.net  code and just put it here and then get rid of all this. And what we need to add is we  need to add content type as well. Right? So probably what you would need to do, you would  need to do some sort of content negotiation here rather than just implying JSON, but it's  okay for now. And this should be a sync now because this method should be awaitable. Okay?  Now the final thing to fix is actually the request URI. And this is a bit bizarre because  I don't think there is a way to get a request URI from the HTTP context right now other  than manually compose it. Because we have properties like path, we have properties like host, and  we have properties like query string, but there is no entire request. So we should do  something like this. To string plus context request path plus context request query string.  Okay? And now, well, actually, we also need to copy this breaking controller, okay, just  so that we can throw an exception. Let's move it to a separate file. Okay. At this point,  we can run. And the result should be pretty much the same. And you can see that it is  the same. We have the metadata object with the request URI with the exception. Also,  the stack traces here is just dragged along because of the Watson bucket. But ultimately,  the output is the same, right? So we have managed to kind of achieve the same type of  functionality. All right. There is one more thing that I wanted to have a look at, I think,  looking at my notes. See, I recently read a blog post by Troy Hunt. And he had those  tips for the speakers, right? And he said, like, keep notes and time slots and everything.  So I have everything here. So, well done, Troy. You have educated the speaker. All right.  So according to my notes, I actually have one more type of functionality that we should  be looking at. Okay? And that is a custom formatter. Okay? So in our web API, let's just close all  these windows because they are confusing. So in our web API, which was at port 1174, we  actually have CSV as well in play. Okay? Maybe not. I think I shut down the...  Ah! Well done. So, by the way, well, we will do it later, actually. Don't worry. But this  reminded me of something. Six. Very well. You get an award. So here is my CSV output.  Okay? So if the client says it's handling CSV, web API can return CSV. And in fact, this  will only work for the collections because if I say, please give me contact number one  as CSV, it's not a collection, so it is not acceptable. Okay? How is it done in web API?  In web API, we have something called CSV media type formatter. Okay? And this is taking care  of serializing a collection into a CSV format. Okay? I actually copied this from web API  project. And what is happening here? Well, as we mentioned before, formatters in web  API, they were bi-directional, so they were used for reading and writing. So in this case,  this formatter is declaring that I cannot read anything. I can only write stuff. And  I can only write stuff if the type in question is inumerable. Okay? And then we have a method  write to stream a thing, which does a bunch of things. Doesn't matter. Some reflection,  blah, blah, blah. And then at the end of the day, it just writes to the response stream.  Okay? So can we have the same type of functionality in MVC6? Yes, we can. And I'm going to add  a new class. And this will be called CSV output formatter. And it should inherit from output  formatter. There's one method to implement. And that is write response body async. This  is kind of similar to what we had before, because before we had write to stream async.  The difference is we had a bunch of different parameters here. Here we just have a context,  but this context is pretty much the same thing. So what we can do, we can actually highlight  this, just copy the verbatim and paste it here. And let's just fix the errors. So there  is context. Actually, it would be better to do it like this. So there is context declared  type, context declared type. There will be context object here as well. And then here,  we don't have a stream, but there will be context. HCP context, whoops, HCP context,  response. And then the body on the response is actually a stream. Okay? So we can write  to it. And now the only thing missing is async. Okay? So let's migrate the remaining pieces  like is the type inumerable? I think at this point. We can enlarge it. Is it inumerable?  And then we should also say what are the media types that are supported? Well, the encoding  support is actually also necessary. So we should say that it's UTF-8. And then supported  media types in this case will be media type header value, text CSV. Right? So that is  the same. And now we should also tell this formatter to only kick in if we are dealing  with inumerable, which is exactly the same condition as we had before. Right? And this  will be the runtime type. Okay? So I think we have everything. And this should build.  Oh! Oh, see, actually, this is interesting. So it doesn't build. And you can see why it  doesn't build. It's because the DNX core is actually failing. Because we are targeting  this project for both DNX451 and DNX core as well. Because that's the reason why you  want to use ASP.NET 5 to run on Core CLR. And in Core CLR, there are actually situations  where you have to pull extra packages that are relevant for Core CLR. So in our case,  actually, in our project JSON, we actually have some extra dependencies for DNX core  that actually did it up front because I was afraid that the network might not be working  here. There is actually a system reflections type extensions package. Okay? So what this  means is that even though this code looks like it's compiling for DNX451, actually for  DNX core, we actually need to include here system reflection. Okay? And now it's building.  So what we need to do, we just need to register our new formator, new CSV, output formator,  and we can get going. And now if I ask for contact SCSV, we actually get a list of contacts  SCSV. And for a single contact SCSV, we get 406, which is exactly what was expected. So  we have just one last thing to show, and we still have maybe three minutes, so I can do  it really, really quickly. There was one interesting thing in Web API, and that is you could have  a controller that would only work with a specific media type. Okay? So in this example, it was  quite common actually for people to use an HTML controller, for example. Right? So I  have here a controller that no matter what will always return CSV. And the way this was  achieved in Web API was through this CSV-only attribute, and you could actually configure  your controller to work only with a specific set of formators. Right? So in this case, we  are saying that, you know what, this controller would only use CSV media type formator, no  matter what. And we can have the same thing in MVC6, and it's actually much easier because  we don't have to create a custom attribute. The attribute is actually already there. It's  called produces. And this produces attribute allows us to specify the media type which  are produced by this specific controller or the specific action can be used on the action  as well. It could actually be multiple here as well, so we could have application whatever  here. Right? And in this case, now that we have it in place, all I need to do is I just  run this, and if I go to this specific route, look, I can remove the accept header, and  I can say CSV contact, and it's actually giving me CSV no matter what. So it's actually an  interesting way to solve this problem, and actually this is one of the things that I  really like. I think this is everything. We have actually gone through a bunch of different  Web API components, a bunch of different things that you might have used in your Web API,  also a bunch of conventions. We have consciously applied them to MVC6. We haven't used the Web  API compatibility in blindly. We just make sure that we want the pieces that we really  need. And all this code will be on GitHub. I will put it up there, and I think that's  about it. So if there are any questions, I think we still have maybe a couple minutes.  We can do some questions, and if not, thank you very much. Yes, Brok has a question.  There are things, for example, like parameter overloading rules that not necessarily you  want there. In general, I think what you want is some of the pieces there, they are very  useful conventions, but for example, the compatibility layer will only kick in if you inherit from  API controller. Now, for example, I would like to use some of the conventions that they  have there, but I would like to apply them to an MVC6 controller. And if you just use  the compatibility layer, just use the extension method, that would not happen. So it's just  a matter of cherry peaking stuff. I don't know if that answers your question. Yes?  It was your buffering middle web, by the way. Credit where credit is due.  The timing wouldn't. No. No. It was just for that. Good catch.  Yes. Yes. Exactly. All right. So one more thing is that I actually have my last two copies  of my book that I just wanted to give away. So if somebody wants a Web API book, I have  two copies to give away. Just don't be shy. Just come here, say that you want a book and  there's no strings attached. Nothing. All right. Thank you.
Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_0_12683.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_12684_15419.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_15420_16655.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_16656_17207.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_17208_17999.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_18000_20027.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_20028_20207.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_20208_21467.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_21468_22163.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_22164_22211.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_22212_23195.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_23196_23339.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_23340_23423.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_23424_23495.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_23496_23543.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_23544_23915.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_23916_24551.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_24552_24791.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_24792_28043.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_28044_28475.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_28476_30983.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_30984_31463.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_31464_33611.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_33612_34247.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_34248_35663.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_35664_36203.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_36204_36311.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_36312_37127.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_37128_37187.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_37188_39143.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_39144_39215.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_39216_39335.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_39336_39527.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_39528_39779.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_39780_40271.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_40272_42539.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_42540_47087.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_47088_47375.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_47376_49691.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_49692_50039.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_50040_50399.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_50400_50603.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_50604_50723.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_50724_50771.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_50772_51131.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_51132_55307.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_55308_55367.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_55368_55415.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_55416_55751.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_55752_56075.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_56076_56831.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_56832_56903.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_56904_57359.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_57360_59615.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_59616_59663.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_59664_59951.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_59952_60659.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_60660_62111.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_62112_62387.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_62388_64547.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_64548_64967.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_64968_71051.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_71052_72587.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_72588_72695.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_72696_72899.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_72900_73343.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_73344_73463.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_73464_73535.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_73536_73679.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_73680_73991.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_73992_74219.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_74220_79391.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_79392_80999.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_81000_81155.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_81156_83219.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_83220_83279.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_83280_83483.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_83484_87599.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_87600_87647.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_87648_87695.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_87696_87767.png Migrating_your_API_from_Web_API_2_to_MVC_6_-_Filip_W_87768_88865.png