Linking to an MVC Route from Web API inside an in-memory server - asp.net-mvc-routing

I'm having problems testing the generation of MVC Routes from inside Web API. The code works when hit manually, but fails under test as the in-memory instance of Web API is unaware of the MVC routes and I can't figure out how to add them.
Here's an example project on github illustrating the problem, but I'll include some relevant code here.
I'm using an in-memory HTTP Server to host the Web API for integration testing:
private HttpConfiguration _config;
private HttpServer _server;
private HttpMessageInvoker _client;
[TestInitialize]
public void TestInitialize()
{
_config = new HttpConfiguration();
WebApiConfig.Register(_config);
_server = new HttpServer(_config);
_client = new HttpMessageInvoker(_server);
}
In my Web API Controller I'm trying to return links via the out-of-the-box default routes, both Web API and Mvc:
[HttpGet]
[Route("MvcRoute")]
public string MvcRoute()
{
return Url.Link("Default", new {Controller = "Other", Action = "Index"});
}
[HttpGet]
[Route("ApiRoute")]
public string ApiRoute()
{
return Url.Link("DefaultApi", new {Controller = "Example", Id = "MvcRoute"});
}
A test for the ApiRoute passes, but this test for the MvcRoute fails with the error message "A route named 'Default' could not be found in the route collection.":
[TestMethod]
public void ShouldReturnMvcRoute()
{
using (var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/api/example/mvcroute"))
{
using (var response = _client.SendAsync(request, CancellationToken.None).Result)
{
var responseContent = response.Content.ReadAsStringAsync().Result;
Assert.AreEqual("\"http://localhost/Other\"", responseContent);
}
}
}
So how can I make the in-memory server aware of MVC's routes? Or if that's the wrong question to ask, how can I run automated tests on a build server (i.e., no IIS) that hit Web API routes that generate links to MVC routes?

In-memory scenario is only supported in Web API. When hosted on a real server like IIS, Web API registers its routes onto the route table provided by System.Web.Routing.RouteCollection...Since MVC also registers its routes into this same route table, when generating links from Web API to MVC, the routes are indeed there...
Note that Web API came later than MVC and one of the design goals of it was to run outside IIS host (like it can be run on Selfhost too) and not have dependency on System.Web...In your about example, you are instantiating HttpConfiguration which means that you are having a different route collection where as in the real app WebApiConfig.Register would be passed in GlobalConfiguration instance...

Related

Azure AD Redirect URL Using Application Gateway and ASE

We have an ASP Core 2.0 App working nicely with Azure AD on a public network. Our Test environment is running in an Azure ASE. The user starts with a public address that passes through the Azure Application Gateway and gets routed to 1 of 2 App servers in the ASE. The application is registered in Azure AD with response URL's that specify the public address.
The problem is when the user redirects to login, the request address presented to Azure AD is an internal address from one of the 2 servers. Then the response URL's don't match and we get an error at login.
The question is how to present the public address to Azure AD so the response URL's match and the token is posted back to the app using the same? The app gateway, I'm told, is configured to populate x-forwarded-for header which has the original address. I don't see where in the web application this can be controlled.
startup.cs
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAd(options =>
{
Configuration.Bind("AzureAd", options);
AzureAdOptions.Settings = options;
})
.AddCookie();
AccountController.cs
public IActionResult SignIn()
{
var redirectUrl = _azureAdOptions.WebBaseUrl;
return Challenge(
new AuthenticationProperties { RedirectUri = redirectUrl },
OpenIdConnectDefaults.AuthenticationScheme);
}
I would think this is a common configuration - passing public to private servers with SSO integrated.
[Edit]
Based on the link provided in the comments, which was very helpful, we tried several things including explicitly setting UseforwardedHeaders in startup.cs even though this is supposed to be enabled by default. Nothing we did changed the URL bolded in the URL below.
https://login.microsoftonline.com/2ff13e34-f33f-498b-982a-7cb336e12bc6/oauth2/authorize?client_id=998c48ae-bbcf-4724-b6f4-6517e41d180a&redirect_uri=**http%3A%2F%2Flocalhost%3A2345%2Fsignin-oidc**&resource=https%3A%2F%2Fgraph.windows.net&response_type=id_token%20code&scope=openid%20profile&response_mode=form_post......
However, and maybe this is a clue, if we comment out the [Authorize] on the home controller and login after the user clicks a button to login, it works. Why?
Note: IDs/GUIDs above have been scrambled to protect the innocent
I came across this post explaining how Application Gateway did not implement the standard x-forwarded-host headers. I'm hoping this gets fixed so the code below would not be required. The solution that worked in our configuration was to force both the public domain and scheme (HTTPS) on every request because the app gateway wasn't (and apparently couldn't be) configured to pass SSL to the backend servers.
Added to startup.cs
app.Use((ctx, next) =>
{
ctx.Request.Host = new HostString(options.Value.CustomDomain;
ctx.Request.Scheme = "https";
return next();
});
Now when the application redirects for any secure resource -- [Authorize] controller methods, or code that explicitly calls Challenge(x, y, z) the public domain on HTTPS is used as the origin host and scheme. Thanks to #juunas for pointing in the right direction.

Autofac OWIN web api - load dependency based on request

How I can load a service dependency based on the route parameter?
My requirement is different, but I'll try to use a simple example.
A user can select the shipping provider (UPS, Fedex...) and the information is as part of the request model or route. Based on the route, I need to load the service class.
How it can be done in Autofac OWIN? Help on this will be appreciated
When you use the Autofac's OWIN integration, each request creates a new lifetime scope in which the current IOwinContext is registered, as you can see here.
You could then delegate the creation of your service to a factory that would take a dependency on IOwinContext.
public class MyServiceFactory
{
private readonly IOwinContext _context;
public MyServiceFactory(IOwinContext context)
{
_context = context;
}
public IService Create()
{
// inspect the context and determine which service you need
// you could return, dependending on, let's say, the URL
// - UpsService()
// - FedexService()
}
}
One thing you'll need to make sure is that you register your factory as InstancePerLifetimeScope since the IOwinContext will be different for each request.
Do you need to work at the OWIN layer, though? It will make things harder, and possibly a bit hacky, since OWIN is really just the HTTP layer, so there's no such thing as route data.
If you use ASP.NET Web API, you could base the factory on the current HttpRequestMessage if you use the RegisterHttpRequestMessage extension method.
You can then access route data through request.GetRequestContext().RouteData. Note that GetRequestContext is an extension method in the System.Net.Http namespace.
If you use ASP.NET MVC, you can register the AutofacWebTypesModule in the container, itself registering quite a few types in the container.
One of those is HttpRequestContext which has a RouteData property, so you can inject this one in the factory and apply your logic.

LightSwitch 2013: Calling the OData ApplicationData.svc service with forms authentication

I have written a simple LightSwitch 2013 application that manages "customers" by adding a customer entity and let LightSwitch handle the attached SQL Server file.
LightSwitch exposes the data with a restful service (ApplicationData.svc) that can be called like this:
https://somesite.azurewebsites.net/ApplicationData.svc/Customers
Now, I want to add another Windows 8 Universal App client application (Store and Phone), aside to the "included" HTML and Silverlight Desktop client. Therefore, I need to call the restful service programatically.
I struggle there with the forms authentication that I have enabled. So I try to log in programatically by code. I do not exactly know what is going on, I tried to analyze the traffic on the wire with fiddler. I see that there is a "LogIn.aspx" page called (GET), then a postback with the credentials filled out by the user is made (POST).
I always get an "401 - unauthorized" response.
My best guess looks like this:
var cookieContainer = new CookieContainer();
var clientHandler = new HttpClientHandler { CookieContainer = cookieContainer };
using (var client = new HttpClient(clientHandler))
{
client.BaseAddress = new Uri("https://somesite.azurewebsites.net");
// Get the login page
var loginGet = client.GetAsync("/LogIn.aspx").Result;
loginGet.EnsureSuccessStatusCode();
// Post-back to login page with credentials
var loginPost = client.PostAsync("/LogIn.aspx", new FormUrlEncodedContent(new Dictionary<string, string> {
{ "LoginUser$Username", "myname" },
{ "LoginUser$Password", "mypw"},
{ "LoginUser$LoginButton", "LOG+IN" },
})).Result;
loginPost.EnsureSuccessStatusCode();
// try to get the customers list via OData
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// Getting a "401 - unauthorized" here:
var response = client.GetAsync("ApplicationData.svc/Customers").Result;
response.EnsureSuccessStatusCode();
}
Could someone kick me into the right direction?
PS:
I know that if I wrote an .NET client, I could just use the "Lightswitch.ApplicationData" class to call the restful service seamlessly. This solution is suggested here:
authenticate Lightswitch Odata service that uses forms authentication
But in my case, I have a Windows 8 Universal App, so I cannot reference the "Server" assembly generated by LightSwitch, which is based on the .NET runtime.
So I finaly found it .... I was completely wrong by handling around with cookies ....
A lightswitch OData service with Forms authentication is exposed with Basic Authentication.
https://usernamme:password#somesite.azurewebsites.net/ApplicationData/Customers
Be sure that you are using SSL of course!
See this article: Exposing LightSwitch Application Data

Creating a Client using Spring MVC for a RESTful API

I'm new to Spring framework and REST concepts. I've been working on a project to grasp these concepts efficiently.
I'm building a quiz tool where a user can login using his credentials and take a quiz.
I've created a RESTful API for same using JAX-RS. And now I want to create a Client which will work over this API, using Spring MVC.
Is that possible and how to start with that ??
I mean, How do I use Spring MVC to create a Client for my RESTful API ??
some of my resources are -
GET /scorecard
GET /scorecard/{quizId}
GET /scorecard/{userId}
GET /quiz/{questionId}
POST /quiz/{questionId}
and so on..
I'm really confused about the design aspects about a client using Spring MVC. Do I include the logic of evaluating quiz,calculating & storing scores in the API or in the spring MVC client ??
Thanx in advance.
Here is an example of the first two endpoints implemented with Spring MVC:
#Controller
#RequestMapping(value = "/scorecard")
public class ScorecardController {
#Autowired
private ScorecardService scorecardService;
// GET /scorecard
#RequestMapping(method = RequestMethod.GET)
public List<Scorecard> getScorecards()
{
List<Scorecard> scorecards = scorecardService.getScorecards();
return scorecards;
}
// GET /scorecard/{quizId}
#RequestMapping(value = "/{quizId}", method = RequestMethod.GET)
public List<Scorecard> getScorecardsByQuizId(#PathVariable long quizId)
{
List<Scorecard> scorecards = scorecardService.getScorecardsByQuizId(quizId);
return scorecards;
}
}
I would suggest checkout spring mvc showcase project from Github and experimenting with source code.
spring-mvc-showcase
You can easily use your browser as client for quite a few REST calls.
Design for application depends on requirements. e.g.
Keeping the score at client will keep you free from session management otherwise you will need to handle at server.

REST help page and REST Test client in MVC4 WebApi

I developed several REST services using an early version of webapi (0.6.0), and for My services I enabled help page and Test client in as below in RegisterRoutes (called from application_start:
routes.Add(new ServiceRoute("auth",
new HttpServiceHostFactory()
{
Configuration = new HttpConfiguration()
{
EnableTestClient = true,
EnableHelpPage = true
}
},
typeof(Auth_Api)));
So I was able to access service at
http://<myserver>/auth
and access help page and test client at
http://<myserver>/auth/help
http://<myserver>/auth/test
Now I need to migrate them to MVC4 webapi, and I would like to accomplish the same behaviour, regarding test and help page, but I cannot find how to do it.
In RegisterRoutes I have this code which setup routes for API (REST) functionality
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Can I add System.ServiceModel.Activation and Microsoft.ApplicationServer.Http.Activation assemblies to MVC4 webapi app and set the routes as before?
Any drawbacks if I do it this way (in case it works)?
Thanks
Look at the API Explorer for the ability to generate Help pages. I believe the Test client has been dropped for the moment. I think there are plans to bring it back. I don't remember exactly.