Web Api Odata not working - asp.net-web-api

I created a Web Api OData controller and try to view it directly but it's not working.
WebApiConfig config
public static void Register(HttpConfiguration config)
{
config.Routes.MapODataRoute("odata", "odata", GetEdmModel());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
public static IEdmModel GetEdmModel()
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers");
builder.Namespace = "PackageManager.Models";
return builder.GetEdmModel();
}
in App start calling registering:
WebApiConfig.Register(GlobalConfiguration.Configuration);
Api controller:
public class CustomersController : EntitySetController<Customer, string>
{
NorthwindDbContext _Context = new NorthwindDbContext();
[Queryable]
public override IQueryable<Customer> Get()
{
return _Context.Customers;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_Context.Dispose();
}
}
Try to access :
localhost:2375/odata/Customers
then always getting error:
The resource cannot be found.

Just add follow to web.config on system.webServer\handler:
<add name="ApiURIs-ISAPI-Integrated-4.0" path="*" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
or replace
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
to
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*" verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
(Different - one dot in path-attribute)

#Parwej - Try writing the below code in your webapi.config instead of your code. Also make sure that proper case is followed in your URL as Odata url is case sensitive. And dont forget to use using System.Web.Http.OData instead of System.Web.Odata in controller. This should work.
public static void Register(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers");
config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
}

Related

How to log every web api request in Insights with .NET 4.6.1

I have a .NET Framework 4.6.1 solution which has this global.asax.cs:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{ Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.InstrumentationKey = EnvironmentHelper.InstrumentationKey;
HttpConfiguration config = GlobalConfiguration.Configuration;
GlobalConfiguration.Configure(WebApiConfig.Register);
}
protected void Application_Error(Object sender, EventArgs e)
{
_telemetry.TrackException(Server.GetLastError());
}
}
This WebApiConfig.cs:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Services.Add(typeof(IExceptionLogger), new InsightsExceptionLogger());
}
}
This logger class:
public class InsightsExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
if (context != null && context.Exception != null)
{
var ai = new TelemetryClient();
ai.TrackException(context.Exception);
}
base.Log(context);
}
}
And this is an example of a controller and method:
public class SomeController : ApiController
{
[HttpPost, Route("api/v1/Something")]
public async Task<IHttpActionResult> Something()
{
The problem is I'm not getting any requests logged in Insights at all.
What do I need to do to get these API calls logged in Insights? (Assuming that simply adding .TrackRequest() is not necessary.)
Create a Request Telemetry initializer class and add the below code. On request Telemetry property you can select which property u want to add in a application insights.
After the Request Telemetry initializer class created add
<Add Type="webapptostoreloginappinsights.ReqTelemetryInitializer,webapptostoreloginappinsights"/>
in a applicationinsights.config file under telemetry processors
Make sure to call the request Telemetry initializer class in a global.asax.cs
Try to run the code now able to view the api request in a Application Insights
What I ended up doing was this....
Add packages:
<package id="Microsoft.ApplicationInsights" version="2.18.0" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.18.0" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.18.0" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.Web" version="2.18.0" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.WindowsServer" version="2.18.0" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.18.0" targetFramework="net472" />
<package id="Microsoft.AspNet.TelemetryCorrelation" version="1.0.8" targetFramework="net472" />
Ensure web.config <system.web> contains this:
<httpModules>
<add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
During the process the ApplicationInsights.config file was created and once deployed the application started logging Reuqest entries in Insights.

ASP.NET Web API2 - suddenly won't display page

All of a sudden my app won't display a page and I now get "You do not have permission to view this directory or page." I looked at this but the suggestions there didn't help.
for my code:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
// NOTE: If this class is renamed, remember to update the global.asax.cs file
public class Service
{
[WebGet(UriTemplate = "")]
public string GetTest()
{
return "Windward update REST service running.";
}
}
When I request an aspx page, it works.
Global.asx.cs:
public class Global : HttpApplication
{
private static readonly ILog log = LogManager.GetLogger(typeof(Global));
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
log4net.Config.XmlConfigurator.Configure();
log.Info(string.Format("Update REST server started, running under user {0}", WindowsIdentity.GetCurrent().Name));
}
private void RegisterRoutes()
{
// The class providing the REST service
RouteTable.Routes.Add(new ServiceRoute("Service", new WebServiceHostFactory(), typeof(Service)));
}
web.config (relevant parts):
<system.web>
<compilation debug="true" targetFramework="4.5"/>
<pages controlRenderingCompatibilityVersion="4.0"/>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</modules>
</system.webServer>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<standardEndpoints>
<webHttpEndpoint>
<!--
Configure the WCF REST service base address via the global.asax.cs file and the default endpoint
via the attributes on the <standardEndpoint> element below
-->
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
Nothing substantive has changed. So why do the requests no longer call the service?
thanks - dave

Registering HttpHandler in ASP.NET MVC 3 and getting 404 Not Found Error

I made a custom handler for test purposes, which looks like:
namespace MVCHttpHandlerProject
{
public class SomeHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.Write("SomeHandler test");
}
}
}
Then I added to my web.config next lines:
<httpHandlers>
<add path="SomeHandler.axd" verb="*" type="MVCHttpHandlerProject.SomeHandler, MVCHttpHandlerProject" /></httpHandlers>
and in <system.webServer>
<handlers>
<add path="SomeHandler.axd" verb="*" type="MVCHttpHandlerProject.SomeHandler, MVCHttpHandlerProject" name="SomeHandler.axd"/>
</handlers>
My Global.asax.cs was not modified and looks exactly as when it was generated with routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); in RegisterRoutes method.
Still, when I try to get to "http://localhost/Home/SomeHandler.axd", "The resource cannot be found" error occurs. Why? Did I miss something? How should I fix this?
You should be requesting http://localhost/SomeHandler.axd instead of http://localhost/Home/SomeHandler.axd. There's no Home.

Global API security checks with ASP.Net MVC3

We are planning on using ASP.Net MVC3 to create a JSON API. For handling security we will have something like an API key, or possibly the Username/Password and maybe a timestamp.
I haven't done any MVC before but I'm wondering if there isn't some simple way to add code to Global.asax that ensures that ALL requests have these variables in them somehow. That way, no request could even get through unless it included the API key.
That way we don't have to add API key handling to each section of the site.
Create a global authorization filter -
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class MyAuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// do Authorization
}
}
then register it in Global.asax -
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyAuthorizationFilterAttribute());
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Although you could create a bespoke Authentication module -
public class CustomAuthentication : IHttpModule
{
public void Init(HttpApplication application)
{
application.AuthenticateRequest += new EventHandler(this.Authenticate);
}
public void Authenticate(object source, EventArgs eventArgs)
{
HttpApplication _application = (HttpApplication)source;
HttpContext _context = _application.Context;
// do authentication
// if authenticated set pricipal
// _context.User = new GenericPrincipal(new GenericIdentity("user"), new string[]);
}
public void Dispose() { }
}
Then you just need to register the module in web.config
<modules runAllManagedModulesForAllRequests="true">
<add name="CustomAuthentication" type="AuthenticationNamespace.CustomAuthentication"/>
</modules>
and set asp.net authentication to none -
<authentication mode="None">
</authentication>
Then you can check to see it the user is authenticated in your AuthorizationFilter.
if(HttpContext.Current.Request.User.Identity.IsAuthenticated)

ASP.NET Membership for one website but multiple and potentially unknown sql server instance

This my problem. I have 1 and only 1 website for multiple customer. To access to their data, customers use urls like this :
http://customer1.mysite.com
http://customer2.mysite.com
etc
Each customer have a SqlServer instance with their data.
Depends to the URL the website connect to the right sql server instance. This is good.
My issue is about Membership, each instance have is own "membership database". In my webconfig I configure a dummy section like this :
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
type="MapApp.MyMembershipProvider, MyApp"
enablePasswordRetrieval="true"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
applicationName="/"
requiresUniqueEmail="false"
passwordFormat="Encrypted"
minRequiredPasswordLength="5"
minRequiredNonalphanumericCharacters="0"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""
connectionStringName="A_DATABASE" />
</providers>
</membership>
Also I have a custom Membershipprovider with code like this :
public class MyMembershipProvider : SqlMembershipProvider
{
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
// Update the private connection string field in the base class.
string connectionString = "my new connection string depdend of the customer"
// Set private property of Membership provider.
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
}
but is not enough a have some issues like "already instanciate" when I call my custom membership provider.
Someone can help me ? Please...
Sorry for my bad english in this long post
This is an interesting problem. Seems like you are attempting to use a different connection string for the database, based on the URL. This might be a bit less elegant, but how about getting the code for the sql membership provider and modifying it so that the connection string which it uses is based on the users URL. I think this could be a good work around if you are truly only using one asp.net application.
I think I have the solution but it's a bit strange.
In my customMemberShipProvider class I must have an empty constructor even if I never call it. And I need another constructor (with args even if I never use it).
So this is my code :
public class myMembershipProvider : SqlMembershipProvider
{
public myMembershipProvider()
{
//never use
}
public myMembershipProvider(string dummyArg)
{
string configPath = "~/web.config";
Configuration config = WebConfigurationManager.OpenWebConfiguration(configPath);
MembershipSection section = (MembershipSection)config.GetSection("system.web/membership");
ProviderSettingsCollection settings = section.Providers;
NameValueCollection membershipParams = settings[section.DefaultProvider].Parameters;
if ((HttpContext.Current.Session != null) && (HttpContext.Current.Session["SubDomainInstanceName"] != null) && (!string.IsNullOrEmpty(HttpContext.Current.Session["SubDomainInstanceName"].ToString())))
{
membershipParams.Add("Instance", HttpContext.Current.Session["SubDomainInstanceName"].ToString());
}
else
{
membershipParams.Add("Instance", "default");
}
Initialize(section.DefaultProvider, membershipParams);
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
//need to remove the key before call Initialize method
string instance = config["Instance"];
config.Remove("Instance");
base.Initialize(name, config);
// Update the private connection string field in the base class.
string connectionString = //my specific connectionstring;
// Set private property of Membership provider.
FieldInfo connectionStringField = GetType().BaseType.GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
connectionStringField.SetValue(this, connectionString);
}
Regards

Resources