ASP.NET Routing in rest service not working in IIS - rest

I created a simple rest service with routing enabled. The routing is properly working when i run it locally i.e using asp.net development server. But when I deploy the application in IIS (IIS 7.5) then it and try to access the method in the service i get the error HTTP 404.0 Not found. Here is my code :
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class HelloWorldService
{
[WebGet(UriTemplate = "Date")]
public DateTime Date()
{
return System.DateTime.Now;
}
}
Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("ServiceData", new WebServiceHostFactory(), typeof(HelloWorldService)));
}
Web.config:
<configuration>
<system.web>
<compilation debug="true" targetFramework="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>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
</configuration>
I also Enabled HTTP Redirection Feature under
Windows Features -> Internet Information Services -> Word Wide Web services -> Common HTTP Features
I also tried Adding handlers like
<handlers>
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</handlers>
Also i have tried all the other solutions that were suggested on the web but nothing works. Thanks in advance for any help.

Something is wrong with your RegisterRoutes(). It should be:
private void RegisterRoutes()
{
// Edit the base address of Service1 by replacing the "Service1" string below
RouteTable.Routes.Add(new ServiceRoute("HelloWorldService", new WebServiceHostFactory(), typeof(HelloWorldService)));
}

Related

.NetCore code with sustainsys-saml2 does nothing

I have an .Net MVC project with SSO working. The SSO config looks like this:
<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
<caches>
<sessionSecurityTokenCache type="Vixion.IdentityModel.Cache.SharedSessionSecurityTokenCache, Vixion.IdentityModel.Cache">
<cacheServiceAddress url="http://tvwapps35434d.kpnis.nl:1008/SessionSecurityTokenCacheService.svc" />
</sessionSecurityTokenCache>
</caches>
<audienceUris>
<add value="http://localhost:24442/" />
</audienceUris>
<securityTokenHandlers>
<remove type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add type="System.IdentityModel.Tokens.Saml2SecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
<certificateValidation certificateValidationMode="None" />
<issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
<authority name="http://vix-make-o:8080">
<keys>
<add thumbprint="5137c779a1e77a0f4a78abd356b0238912637469" />
</keys>
<validIssuers>
<add name="http://vix-make-o:8080" />
</validIssuers>
</authority>
</issuerNameRegistry>
</identityConfiguration>
</system.identityModel>
<system.identityModel.services>
<federationConfiguration>
<cookieHandler requireSsl="false" path="/" />
<wsFederation passiveRedirectEnabled="true" issuer="http://vix-make-o:8080" realm="http://localhost:24442/" requireHttps="false" reply="http://localhost:24442/" />
</federationConfiguration>
</system.identityModel.services>
According to the example here:
https://github.com/Sustainsys/Saml2/tree/master/Sustainsys.Saml2.AspNetCore2
I write this code in a new .NetCore app:
services.AddIdentity<IdentityUser, IdentityRole>()
.AddDefaultTokenProviders();
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.Cookie.HttpOnly = true;
});
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
services.AddAuthentication()
.AddSaml2(options =>
{
options.SPOptions.EntityId = new EntityId("http://vix-make-o:8080");
options.IdentityProviders.Add(
new IdentityProvider(
new EntityId("http://vix-make-o:8080/ws_saml_metadata"), options.SPOptions)
{
//LoadMetadata = true
});
});
app.UseAuthentication();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
When I start the app, the browser goes straight to the index page. No error, no nothing. When I check with SAML Chrome Panel it shows no traffic at all.
I do not expect my code working, but it should at least do something to give me a hint to how to go further.
Any suggestions are welcome.
Thank you.
S.
Remove the web.config content. It is not used with the ASP.NET Core module.
Set back the LoadMetadata flag, or add the necessary config manually.
Set Saml2 as the default challenge protocol for authentication.
Finally, add an [Authorize] attribute to your controllers that should require authentication to initiate the authentication process.

Cannot access identity claims in custom authorize attribute in MVC application

I am trying to do POC of switching from forms authentication in existing MVC 4 application to claims-based one, but cannot get custom authorize attribute working - have infinite redirect loop ending with known 'bad request' issue because of total cookie size...
And the reason - there is no authenticated identity (and correct Claims collection) in attribute context as I see it in debug despite I see successfully created identity with expected claims collection in SecurityTokenValidated handler.
So, it seems like it is somehow lost after OIDC middleware process it, and not unavailable in attribute context, therefore I cannot further check for 'role' claim.
Maybe I am missing something?
My OWIN Startup:
AntiForgeryConfig.UniqueClaimTypeIdentifier = "sub";
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
//AuthenticationMode = AuthenticationMode.Active, // ? https://github.com/IdentityServer/IdentityServer3/issues/1963
Authority = OAuthServerUrl,
ClientId = "my_app",
RedirectUri = MyAppUrl,
ResponseType = "id_token token", //notice, if response type 'token' is requested, IdentityServer stops including the claims in the identity token, though we could set alwaysInclude=true for some scope claims (ex. for role)
Scope = "openid profile roles",
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
PostLogoutRedirectUri = MyAppUrl,
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
var id = n.AuthenticationTicket.Identity;
//n.OwinContext.Authentication.SignIn(id);
//tried SignIn() explicitly, and even setting HttpContext.Current.User and Thread.CurrentPrincipal to n.AuthenticationTicket.Identity, but without luck
return Task.FromResult(0);
},
RedirectToIdentityProvider = async n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var result =
await n.OwinContext.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType);
var idToken = result?.Identity.Claims.GetValue("id_token");
if (idToken != null)
{
n.ProtocolMessage.IdTokenHint = idToken;
}
}
await Task.FromResult(0);
}
}
});
My custom attribute inherited from System.Web.Mvc.AuthorizeAttribute:
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//var owinContext = HttpContext.Current.GetOwinContext();
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// 403 we know who you are, but you haven't been granted access
filterContext.Result = new HttpStatusCodeResult(System.Net.HttpStatusCode.Forbidden);
}
else
{
// 401 who are you? go login and then try again
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
where filterContext.HttpContext.User.Identity (also tried HttpContext.Current.User and Thread.CurrentPrincipal) has Claims collection with single 'name' claim from LOCAL AUTHORITY issuer (default one?).
I also modified Web.config to disable forms auth:
<authentication mode="None">
...
<membership>
<providers>
<clear />
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="999" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="3" passwordStrengthRegularExpression="" applicationName="Audit" />
<!--<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="999" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="3" passwordStrengthRegularExpression="" applicationName="Audit" />-->
</providers>
</membership>
<profile>
<providers>
<clear />
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" applicationName="Audit" />
<!--<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" applicationName="Audit" />-->
</providers>
</profile>
<roleManager enabled="true">
<roleManager enabled="false">
<providers>
<clear />
<add connectionStringName="ApplicationServices" applicationName="Audit" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<add applicationName="Audit" name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<!--<add connectionStringName="ApplicationServices" applicationName="Audit" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />-->
<!--<add applicationName="MyApp" name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />-->
</providers>
</roleManager>
...
<handlers>
<remove name="FormsAuthentication" />
<remove name="RoleManager" />
UPDATE 1: I've migrated app from MVC 4 to the latest MVC 5 hoping it is MVC 4 related issue, but, unfortunately, no difference. Apparently, something wrong with my client application/configuration.

Enable Migrations Not working

I have an application which has three layers.I am using EntityFramework 6.1 and code first migrations using existing database.The three layers are given below:
1.Presententation Layer:ConceptuCodeFirstMigrationDemo
Simply a Console Application for simplicity
Data Layer Consists of Context and Initializer.The codes are given below:
namespace ConceptuCodeFirstMigrationDemo.Data.DataContext
{
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using ConceptuCodeFirstMigrationDemo.Domain;
public partial class ConceptuContext : DbContext,IDisposable
{
public ConceptuContext()
: base("name=ConceptuContext")
{
}
public virtual DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.WithRequired(e => e.User)
.HasForeignKey(e => e.CreatedBy)
.WillCascadeOnDelete(false);
}
}
}
ConceptuInitializer.Cs
namespace ConceptuCodeFirstMigrationDemo.Data.DataContext
{
public class ConceptuInitializer: System.Data.Entity.CreateDatabaseIfNotExists<ConceptuContext>
{
}
}
Appconfig:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<entityFramework>
<contexts>
<context type="ConceptuCodeFirstMigrationDemo.Data.DataContext.ConceptuContext, ConceptuCodeFirstMigrationDemo.Data">
<databaseInitializer type="ConceptuCodeFirstMigrationDemo.Data.DataContext.ConceptuInitializer, ConceptuCodeFirstMigrationDemo.Data" />
</context>
</contexts>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<connectionStrings>
<add name="ConceptuContext" connectionString="data source=AMITABHA-PC;initial catalog=Conceptu;user id=sa;password=lovediya;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Domain Layer: Consists of entity class for example user
After writing a new class when I try to enable migrations using Enable-Migrations
showing
No context type was found in the assembly 'ConceptuCodeFirstMigrationDemo'.
After I tried the following command:
Enable-Migrations -ContextTypeName ConceptuCodeFirstMigrationDemo.Data.DataContext.ConceptuContext
this shows following error
The context type 'ConceptuCodeFirstMigrationDemo.Data.DataContext.ConceptuContext' was not found in the assembly 'ConceptuCodeFirstMigrationDemo'.
After I tried the following command
Enable-Migrations ConceptuCodeFirstMigrationDemo.Data.DataContext.ConceptuContext
It shows the following error
Enable-Migrations : A positional parameter cannot be found that accepts argument 'ConceptuCodeFirstMigrationDemo.Data.DataContext.Concept
uContext'.
At line:1 char:18
+ Enable-Migrations <<<< ConceptuCodeFirstMigrationDemo.Data.DataContext.ConceptuContext
+ CategoryInfo : InvalidArgument: (:) [Enable-Migrations], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Enable-Migrations
Try this.
Enable-Migrations -ContextTypeName ConceptuCodeFirstMigrationDemo.Data.DataContext.ConceptuContext -ProjectName ConceptuCodeFirstMigrationDemo

SSRS 2008 Execution Service LoadReport Error

I'm using SSRS 2008 (NOT R2)
I have a report deployed to a dev server, I'm trying to render the report as a pdf by calling the execution service.
The error I am getting is
This operation is not supported on a report server that runs in native mode. ---> Microsoft.ReportingServices.Diagnostics.Utilities.OperationNotSupportedNativeModeException: This operation is not supported on a report server that runs in native mode.
Two things I notice: one is that web service wsdl shows LoadReport having two parameters - report path and history id, but when I generate a service reference for the ReportExecution2005.asmx, the LoadReport method has 5 parameters: trusteduserheader, reportPath, historyid, serviceinfoheader, and executionheader
I have tried adding the service reference with and without ?wsdl at the end of the url but the result is the same
Here's the code I'm using:
ReportExecutionServiceSoapClient rs = new ReportExecutionServiceSoapClient("ReportExecutionServiceSoap", "http://xxx:80/ReportServer/ReportExecution2005.asmx");
rs.ClientCredentials.Windows.ClientCredential = new NetworkCredential("aaa", "aaa", "aaa");
rs.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
// Render arguments
byte[] result = null;
string reportPath = "/Invoices/InvoiceStandard";
string format = "PDF";
string historyID = null;
string devInfo = "";
// Prepare report parameter.
ParameterValue[] parameters = new ParameterValue[3];
parameters[0] = new ParameterValue();
parameters[0].Name = "PartyID";
parameters[0].Value = "19758";
parameters[1] = new ParameterValue();
parameters[1].Name = "Contract";
parameters[1].Value = "17703"; // June
parameters[2] = new ParameterValue();
parameters[2].Name = "FinancialPeriod";
parameters[2].Value = "MAR-2012";
string encoding="";
string mimeType="";
string extension="";
Warning[] warnings = null;
string[] streamIDs = null;
ExecutionInfo execInfo = new ExecutionInfo();
TrustedUserHeader trusteduserHeader = new TrustedUserHeader();
ExecutionHeader execHeader = new ExecutionHeader();
ServerInfoHeader serviceInfo = new ServerInfoHeader();
execHeader = rs.LoadReport(trusteduserHeader, reportPath, historyID, out serviceInfo, out execInfo);
rs.SetExecutionParameters(execHeader, trusteduserHeader, parameters, "en-us", out execInfo);
try
{
rs.Render(execHeader,
trusteduserHeader,
format,
devInfo,
out result,
out extension,
out encoding,
out mimeType,
out warnings,
out streamIDs);
}
Here's my web.config
<?xml version="1.0"?>
<configuration>
<connectionStrings>
<add name="ApplicationServices"
connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<authentication mode="Forms">
<forms loginUrl="~/Account/Login.aspx" timeout="2880" />
</authentication>
<membership>
<providers>
<clear/>
<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices"
enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
applicationName="/" />
</providers>
</membership>
<profile>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/"/>
</providers>
</profile>
<roleManager enabled="false">
<providers>
<clear/>
<add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" />
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
</providers>
</roleManager>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="ReportExecutionServiceSoap" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows"/>
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://xxx:80/ReportServer/ReportExecution2005.asmx"
binding="basicHttpBinding" bindingConfiguration="ReportExecutionServiceSoap"
contract="SSRS.ReportExecutionServiceSoap" name="ReportExecutionServiceSoap" />
</client>
</system.serviceModel>
</configuration>
I had the same problem with SSRS 2008 R2, but I did not want to resort to calling the report viewer control.
Above, Rick Hodder was using the following statement:
TrustedUserHeader trusteduserHeader = new TrustedUserHeader();
This statement will cause the OperationNotSupportedNativeModeException error he encountered if the SSRS installation is not configured with a certificate for SSL connections. Check the SSRS Logs for an error entry that contains:
ERROR: TrustedHeader Not Supported in Native Mode.
If this is the case, you need to either configure the server to work with SSL, or use null for the trusted header.
TrustedUserHeader trusteduserHeader = null;
Is this report loaded from some front end or is it loaded from DLL? To me it looks like you are using ASP.net Application. In this case you could use reportViewer object to pull the report for you all the heavy lifting can be done by the ReportViewer and you can then just save the file as PDF
using something like this
reportViewer.ServerReport.ReportServerUrl = new Uri(Config.ReportServerURL);
reportViewer.ServerReport.ReportPath = String.Format("{0}/{1}", Config.ReportServerEnvironment, reportName);
reportViewer.ServerReport.ReportServerCredentials = new ReportsCredentials(Config.ReportServerUser, Config.ReportServerPassword, Config.ReportServerDomain);
reportViewer.GetDocumentStream(SSRSFormatType.Pdf, documentName);
Here reportViewer is the reference to ReportViewer object on the screen.

Delete not Working in WCF rest service throwing (405) Method Not Allowed

I have created WCF RESTful service which uses simple database behind it and just trying to working on put, get,post and delete items. Right now post ,put and get is working. But the delete is n't working. Some forums telling that I need to disable the WebDAV module. I did that and then I got PUT to work. But I can not get DELETE to work. Whenever I call DELETE verb through my service I got the following error:
The remote server returned an unexpected response: (405) Method Not Allowed.
Can any one help me on this?
I have not yet found one comprehensive answer for PUT and DELETE being overridden and returning 405s for restful WCF services anywhere else, so I'm going to post it here.
This problem can be addressed easily by simply uninstalling WebDAV from the machine via either the role manager (on a server) or through Add/Remove windows features. If that is an acecceptible approach, you can stop reading now.
The commonly recommended fix here is to simply remove the WebDAV module from your site, appending something like the following to your web.config:
<system.webServer>
<modules>
<remove name="WebDAVModule" />
</modules>
</system.webServer>
The problem here is that now your WCF app has no way of knowing how to handle PUT and DELETE at all. So to solve this, some solutions suggest making the follwing modification:
<modules runAllManagedModulesForAllRequests="true">
<system.webServer>
<modules>
<remove name="WebDAVModule" />
</modules>
</system.webServer>
This may be satisfactory for most, but I did not like the idea of having our service uselessly loading everything up for every call when it wasn't necessary. So I refined the approach a bit by manually mapping the extensionless URL handler to all HTTP verbs (probably could be refined to just the necessary ones)
<system.webServer>
<modules>
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
I only tested this on R2 64 and 7 64, so ymmv. But hopefully this helps.
There seems to have some similar questions on StackOverflow already:
WCF Service 405 Method Not Allowed Exception
WCF The request failed with HTTP status 405: Method Not Allowed
WCF Service Returning "Method Not Allowed"
Hope one of them helps.
I've used the WebChannelFactory approach in the client side to consume the REST service. I've created the Service Reference using the normal "Add Service Reference" approach. This doesn't add [WebGet(UriTemplate = "/")].
After I added these for all Operation on Client side proxy class just like the Service Contract, it started working.
Adding Codes to the Web.config File
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.serviceModel>
<behaviors >
<serviceBehaviors>
<behavior name="http">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="www">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<services>
<service behaviorConfiguration="http" name="REST.CRUD.Alternativ">
<endpoint address=""
binding="webHttpBinding"
behaviorConfiguration="www"
contract="REST.CRUD.IAlternativ"
bindingConfiguration="rest">
</endpoint>
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange">
</endpoint>
</service>
</services>
<bindings>
<webHttpBinding>
<binding allowCookies="true" name="rest">
<security mode="None"></security>
</binding>
</webHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<directoryBrowse enabled="true" />
<security>
<requestFiltering>
<verbs>
<add verb="GET" allowed="true" />
<add verb="POST" allowed="true" />
<add verb="DELETE" allowed="true" />
<add verb="PUT" allowed="true" />
</verbs>
</requestFiltering>
</security>
</system.webServer>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<connectionStrings>
<add name="KendoDB" connectionString="Data Source=(localDB)\v11.0;Initial Catalog=Kendo;Integrated Security=True;Pooling=False" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
PersonDTO
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace REST.CRUD
{
public class PersonDTO
{
public int ID { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
public class KendoContext : DbContext
{
public KendoContext():base("KendoDB"){}
public DbSet<PersonDTO> PersonDTOs { get; set; }
}
}
IAlternativ.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Web.Services;
namespace REST.CRUD
{
[ServiceContract]
public interface IAlternativ
{
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
List<PersonDTO> GetAll();
[WebInvoke(Method = "GET",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
PersonDTO Get(int ID, string f, string l);
[WebInvoke(Method = "DELETE",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
String Delete(int ID, string f, string l);
[WebInvoke(Method = "PUT",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
String Update(int ID, string f, string l);
}
}
Alternativ.svc.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Web.Script.Services;
using System.Web.Services;
namespace REST.CRUD
{
public class Alternativ : IAlternativ
{
public List<PersonDTO> GetAll()
{
var db = new KendoContext();
return db.PersonDTOs.Select(c => c).ToList();
}
public PersonDTO Get(int ID, string f, string l)
{
var db = new KendoContext();
return db.PersonDTOs.Where(c => c.ID == ID).Select(c => c).First();
}
public String Delete(int ID, string f, string l)
{
//Delete Code
return "OK";
}
public String Update(int ID, string f, string l)
{
//Update Code
return "OK";
}
}
}
[Build and Run the WCF Project(F5)].
Add jquery to DOM for test(Paste to Console)
(function() {
if (! window.jQuery ) {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
(document.getElementsByTagName('head')[0] ||
document.getElementsByTagName('body')[0]).appendChild(s);
}
}());
Add functions to Console
function POST(){
$.ajax({
type: "POST", //GET,POST,PUT or DELETE verb
url: "/Alternativ.svc/GetAll", // Location of the service
data: '{"ID":"1"}', //Data to be sent to server
dataType: "json", // content type sent to server
contentType: "application/json; charset=utf-8", //Expected data format from server
processdata: false, //True or False
success: function (response) {
console.log(response);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.statusText);
}
});
}
function GET(){
$.ajax({
type: "GET", //GET,POST,PUT or DELETE verb
url: "/Alternativ.svc/Get", // Location of the service
data: {ID:1}, //Data to be sent to server
dataType: "json", // content type sent to server
contentType: "application/json; charset=utf-8", //Expected data format from server
processdata: true, //True or False
success: function (response) {
console.log(response);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.statusText);
}
});
}
function DELETE(){
$.ajax({
type: "DELETE", //GET,POST,PUT or DELETE verb
url: "/Alternativ.svc/Delete", // Location of the service
data: '{"ID":"1"}', //Data to be sent to server
dataType: "json", // content type sent to server
contentType: "application/json; charset=utf-8", //Expected data format from server
processdata: false, //True or False
success: function (response) {
console.log(response);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.statusText);
}
});
}
function PUT(){
$.ajax({
type: "PUT", //GET,POST,PUT or DELETE verb
url: "/Alternativ.svc/Update", // Location of the service
data: '{"ID":"1"}', //Data to be sent to server
dataType: "json", // content type sent to server
contentType: "application/json; charset=utf-8", //Expected data format from server
processdata: false, //True or False
success: function (response) {
console.log(response);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.statusText);
}
});
}
Call functions
DELETE();
PUT();
GET();
POST();