I have created a backend service using Azure Mobile App. Offline sync works fine with mobile clients using provided SDK. All the controllers that I have used are TableController. Now I want to add a simple Web API 2 controller ApiController, that will not be used by mobile clients.
Here is a simple ApiController that I have added to Controllers folder:
public class SimpleController : ApiController
{
public string Get()
{
return "Hello";
}
}
But the controller is never hit. If I add [MobileAppController] attrebute to the controller, it works but now it asks for additional headers in the request (I guess this headers are sent by client SDK):
{"message":"No API version was specified in the request, this request needs to specify a ZUMO-API-VERSION of '2.0.0'. For more information and supported clients see: http://go.microsoft.com/fwlink/?LinkId=690568#2.0.0"}
But I do not need this additional functionality here - I just want my service to respond to simple GET requests.
Although the guide states that it is not necessary to decorate the class:
Any controller that does not have MobileAppControllerAttribute applied can still be accessed by clients, but it may not be correctly consumed by clients using any Mobile App client SDK.
I can not achieve this. Am I missing something?
Thanks
I have figured out how to use both types of controllers.
Just add a call to config.MapHttpAttributeRoutes(); in your StartUp.ConfigureMobileApp method like this:
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
//For more information on Web API tracing, see http://go.microsoft.com/fwlink/?LinkId=620686
config.EnableSystemDiagnosticsTracing();
config.MapHttpAttributeRoutes(); //Add this line
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
... //rest of the code
}
And then decorate your controller with custom route:
[Route("api/Simple")] //add this
public class SimpleController : ApiController
{
public string Get()
{
return "Hello";
}
}
Related
I have this ASP.NET Web API and I want to restrict access so it only works when called by specific host. I cannot, for what I know until now, secure it by token, because the WEB API url will be a postback url for a system that will call it automatically when a certain action is made. I have checked out CORS, but what CORS seems to do is to allow a specific domain to access the API. So, does this mean that my WEB API is already restricted for other domains? Then why I can access it by Postman locally, even if it is hosted in Azure?
I just want my service to allow calls from localhost and another specific domain only.
How do I achieve this?
Thanks!
One possibility is to use a custom authorization filter that creates an HTTP response with a failure status code like 400 bad request or 404 not found if the requests has a host that is not allowed. We could define an authorization filter named RestrictDomain that looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class RestrictDomainAttribute : Attribute, IAuthorizationFilter
{
public IEnumerable<string> AllowedHosts { get; }
public RestrictDomainAttribute(params string[] allowedHosts) => AllowedHosts = allowedHosts;
public void OnAuthorization(AuthorizationFilterContext context)
{
// Get host from the request and check if it's in the enumeration of allowed hosts
string host = context.HttpContext.Request.Host.Host;
if (!AllowedHosts.Contains(host, StringComparer.OrdinalIgnoreCase))
{
// Request came from an authorized host, return bad request
context.Result = new BadRequestObjectResult("Host is not allowed");
}
}
}
If you want to apply the RestrictDomain filter globally, then you can add the filter in the Startup.cs file like this:
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
// Add the restrict domain filter globally
// You could read the allowed hosts from a config file, here we hard code them
options.Filters.Add(new RestrictDomainAttribute("localhost", "example.com"));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
}
With this setting, if I remove "localhost" from the constructor and only allow "example.com", I get a 400 request when I use Postman since the host will be localhost.
Another option is to use the filter in a controller or controller action directly since we configure it to work as an attribute. However, the allowed hosts will have to be constant values instead of values that can be calculated at runtime. Here's an example:
using Microsoft.AspNetCore.Mvc;
[Route("")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public ContentResult Index() => Content("Home");
[HttpGet("greeting")]
[RestrictDomain("localhost", "example.com")] // values must be constants
public ContentResult Greeting() => Content("Hello, World!");
}
If you don't want constant values and don't want to apply the filter globally, then you could inject the IConfiguration into the filter to read the allowed hosts that can access the resource.
CORS on its own only tells the browser / client apps to stop sending requests, in many ASP.Net Web API implementation it doesn't actually block the request pipeline. This is why Postman works, Postman doesn't execute the same pre-flight OPTIONS check first, it just send the request.
You should absolutely look into adding authentication to your API, Bearer token based authentication works well enough with and between APIs. Have a read over Secure a Web API with Individual Accounts and Local Login in ASP.NET Web API 2.2, but that is out of scope for this question.
At a conceptual level, you just need to intercept the incoming request in the OWIN pipeline before the API middleware and request the request if it doesn't match your rules. This should be using in conjunction with CORS so that browsers respond in a standard way.
For background, have a read over Block or limit unwanted traffic to Asp.Net Web Application. Usually we manage domain or IP level security filtering in the hosting architecture or routing. IIS or Azure hosts have many built in policies to help manage this.
You could implement this at a global level by adding the following OWIN request processor:
public void ConfigureOAuth(IAppBuilder app)
{
app.Use((context, next) =>
{
string[] AllowedDomains = new string[] { "::1", "localhost", "mybusinessdomain.com" };
if (!AllowedDomains.Contains(context.Request.Host.Value.ToLower()))
{
context.Response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
return System.Threading.Tasks.Task.FromResult<object>(null);
}
return next();
});
// TODO: add in your other OWIN configuration AFTER the request filter.
}
If you are NOT using the OWIN hosting pipeline and you want to manage this globally then you could put a check into the global.asax.cs toi handle the Application_BeginRequest:
private static readonly string[] AllowedDomains = new string[] { "::1", "localhost", "mybusinessdomain.com" };
protected void Application_BeginRequest(Object sender, EventArgs e)
{
if >(!AllowedDomains.Contains(HttpContext.Current.Request.UserHostName.ToLower()))
{
HttpContext.Current.Response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
HttpContext.Current.Response.End();
// or transfer to a specific page
// Server.Transfer("~/banned.aspx");
}
}
I'm creating an ASP.NET Core API app, and currently, and when one creates a new project there is a controller named Values, and by default the API opens it when you run. So, I deleted that controller and added a new controller named Intro, and inside it an action named Get. In the Startup.cs file, I have the following lines of code:
app.UseMvc(opt =>
{
opt.MapRoute("Default",
"{controller=Intro}/{action=Get}/{id?}");
});
And my Intro controller looks like this:
[Produces("application/json")]
[Route("api/[controller]")]
[EnableCors("MyCorsPolicy")]
public class IntroController : Controller
{
private readonly ILogger<IntroController> _logger;
public IntroController(ILogger<IntroController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
// Partially removed for brevity
}
}
But, again when I run the API, it by default tries to navigate to /api/values, but since I deleted the Values controller, now I get a 404 not found error. If I manually then navigate to /api/intro, I get the result that is provided from my Get action inside the Intro controller. How can I make sure that when the API run (for example through Debug->Start Without Debugging) that it by default gets the Get action from the Intro controller?
You can change it in launchSettings.json file in Properties node. There should be field launchUrl which contains default launching url
With later version of ASP .Net Core, MVC routing is less prominent than it once was, there is general routing now in place which can handle routing of Web APIs, MVC and SignalR amongst other kinds of routes.
If you are using later versions, and not specifically calling app.UseMvc you can do this with the generalised Endpoints configuration:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=Account}/{action=login}/{id?}");
// Create routes for Web API and SignalR here too...
});
Where Account and login are your new default controller and actions. These can be MVC or Web API controller methods.
Assuming you forgot to tick the Web API checkbox (add it to the project) when making a new MVC (5) project, what do you need to do add Web API and get it working?
There are a bunch of migration questions, but none seemed to have the complete and up-to-date steps for adding Web API to an MVC 5 project and it seems to have changed from some of the old answers.
Add Web API to MVC 4
Adding GlobalConfiguration.Configure(WebApiConfig.Register) MVC 4
Update the MVC project
Use Nuget to get the newest Web API.
Project - Right click - Manage Nuget Packages - Search for Web API (Microsoft ASP.NET Web API ...) and install it to your MVC project.
Then you still need to get Web API routing to work.
From Microsoft's Configuring ASP.NET Web API 2
Add WebApiConfig.cs to the App_Start/ folder
using System.Web.Http;
namespace WebApplication1
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// TODO: Add any additional configuration code.
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// WebAPI when dealing with JSON & JavaScript!
// Setup json serialization to serialize classes to camel (std. Json format)
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
}
}
If you have an MVC Project it will have Global.asax.cs, add the new routes. Order of the Global.asax.cs routes is critical. Note there are outdated examples which use
WebApiConfig.Register
Add this line to Global.asax.cs:
GlobalConfiguration.Configure(WebApiConfig.Register);
protected void Application_Start()
{
// Default stuff
AreaRegistration.RegisterAllAreas();
// Manually installed WebAPI 2.2 after making an MVC project.
GlobalConfiguration.Configure(WebApiConfig.Register); // NEW way
//WebApiConfig.Register(GlobalConfiguration.Configuration); // DEPRECATED
// Default stuff
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
WebAPI Help
To get the (very) helpful WebAPI help pages, install WebAPI.HelpPage.
See http://channel9.msdn.com/Events/Build/2014/3-644 (~42 minutes in) for what it does. It looks very helpful!
Nuget Console: Install-Package Microsoft.AspNet.WebApi.HelpPage
To verify WebAPI is working:
To the controllers folder -> Add new item -> Web API Controller Class.
public class TestController : ApiController
{
//public TestController() { }
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
//...
}
Now you can test in IE/FF/Chrome as usual, or in the JavaScript consoles for non-get testing.
(With just the controller in the URL it will call the GET() action in the new Web API Controller, it's automatically mapped to methods/actions depending on the REST e.g. PUT/POST/GET/DELETE. You don't need to call them by action like in MVC)
The URL directly:
http://localhost:PORT/api/CONTROLLERNAME/
Alternatively use jQuery to query the controller.
Run the project, Open the console (F12 in IE) and try run an Ajax query. (Check your PORT & CONTROLLERNAME)
$.get( "http://localhost:PORT/api/CONTROLLERNAME/", function( data ) {
//$( ".result" ).html( data );
alert( "Get data received:" + data);
});
Side note: There are some pros/cons to consider when combining MVC and Web API in a project
WebAPI Help verification:
http://localhost:PORT/help
I have an existing .NET 4 console application which I want to start exposing a REST API. I'm using the Microsoft self host Web API library to have it expose that API, but I'm having trouble understanding how the routing path gets developed.
Here is an example of a controller, which should expose some database objects my console application already handles:
public class UserController : ApiController
{
UserInformation[] users;
public IEnumerable<UserInformation> GetAllUsers()
{
// snip.
}
public UserInformation GetUserById(int id)
{
// snip.
}
}
And I'm exposing my API in Program.cs like so:
var config = new HttpSelfHostConfiguration("http://localhost:8800");
config.Routes.MapHttpRoute(
"API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
using (var server = new HttpSelfHostServer(config)) { // code }
Given the above code, I'd expect that I could get resources by making http requests like http://localhost:8800/api/users and http://localhost:8800/api/users/1, but those don't seem to work. How does the controller part of the GET request get created? It doesn't seem like users is the correct routing path for the API, but I'm not sure what goes there.
Thanks for any help
That's because your controller is called UserController and not UsersController. Either rename your controller to UsersController or modify your request to go to http://localhost:8800/api/user.
This should solve the problem.
I have been playing with Webapi 2.0 and OData 2.0 for past 2 weeks. I would like to know as to when to use OData routing and Webapi Attribute routing.
Thanks,
Pavan
Web API is a general framework for creating HTTP endpoints (web APIs/REST APIs). You can also use it to create OData endpoints, which is a more specific application of ASP.NET Web API.
So the first question is whether you want to create an OData endpoint or some other type of REST API.
If you are creating an OData endpoint, use OData routing. Otherwise use the "regular" Web API routing
Web API Routing
you can use default routing but if you want to defined your route templates then you can used AtrributeRouting which is using convention-based routing
In this type of routing, you define one or more route templates, which are basically parameterized strings.
When the framework receives a request, it matches the URI against the route template
install following nuget package :
"Install-Package Microsoft.AspNet.WebApi.WebHost"
Add following line in your "WebApiConfig.cs" file
config.MapHttpAttributeRoutes();
create your webapicontroller like following
[Route("api/users")]
public HttpResponseMessage GetUsers(int userId)
{
you can access your web api like this URL:
http://localhost:8080/api/users/
Web API Odata Routing
when you want to use IQueryable instead of IEnumerable then you can use odata
install nuget package for odata
"Microsoft.AspNet.WebApi.OData"
Add following code in your "WebApiConfig.cs" file
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<ViewTest>("TestOdata");
var model = modelBuilder.GetEdmModel();
config.Routes.MapODataRoute(routeName: "OData", routePrefix: "odata", model: model);
Add following code in your "UsersOdataController.cs" file
public class TestOdataController : OdataController
{
//GET odata/TestOdata
public IQueryable<ViewTest> Get()
{
try
{
return context.View_TestRepository.GetQueryable();
}
catch (Exception ex)
{
throw ex;
}
}
}
ViewTest : view model class with your required columns
View_TestRepository : Repository class
you can access your odata web api by following URL:
"http://localhost:8080/odata/TestOdata"