Web API 404 error Delete request - asp.net-web-api

I have a Web API that worked perfectly on development with all kind of HTTP requests (on the same controller), once I moved it to production (shared server, I don't even have access to it) the DELETE requests stopped working (the others are working fine), I get a 404 error:
Requested
URL https://www.example.com:443/Rejected-By-UrlScan~/API/Users/DeleteUser/1
Physical Path d:\xx\yy\example.com\Rejected-By-UrlScan
Logon Method Anonymous
Logon User Anonymous
This is (a part of) the web.config:
<system.web>
<customErrors mode="Off"/>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
The Delete Action:
[Authorize]
[RoutePrefix("Users")]
public class UsersController : ApiController
{
[HttpDelete]
[Route("DeleteUser/{id:int}")]
public void Delete(int id)
{
_UsersRepository.Delete(id);
}
And the AJAX call:
deleteUser = function (id, callback) {
$.ajax({
url: "../API/Users/DeleteUser/" + id,
type: "DELETE",
success: function () {
callback;
}
});
}
WebApiConfig.cs:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
//create json formatter and remove all others
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
jsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.Indented;
config.Formatters.Remove(config.Formatters.FormUrlEncodedFormatter);
config.Formatters.Remove(config.Formatters.XmlFormatter);
}
An example of a working call on the same Controller:
getUsers = function (callback) {
$.get("../API/Users/GetUsers/", callback);
}
And the Action:
[Route("GetUsers")]
public IEnumerable<User> GetUsers()
{
return _UsersRepository.GetUsers();
}

In your IIS do you have the URLScan extension configured ?
https://www.iis.net/downloads/microsoft/urlscan
UrlScan is a security tool that restricts the types of HTTP requests that IIS will process.
The "Rejected-By-UrlScan" in your URL suggests that the extension may be configured to reject "Delete" requests.
You can ask your Administrator of the Server hosting the IIS about whether Delete requests are configured to be allowed in the IIS.

The URL is wrong in the JS snippet. It should be
deleteUser = function (id, callback) {
$.ajax({
url: "[Application_Path]/Users/DeleteUser/" + id,
type: "DELETE",
success: function () {
callback;
}
});
}
[RoutePrefix("Users")] overrides the default routing, so there should be no "API" in the URL.
You should remove the [Application_Path] and put your virtual directory name or use the #Url.Action

I had to got it to work so I changed the type of the request from DELETE to POST and it works perfectly:
[Authorize]
[RoutePrefix("Users")]
public class UsersController : ApiController
{
[HttpPost]
[Route("DeleteUser/{id:int}")]
public void Delete(int id)
{
_UsersRepository.Delete(id);
}
deleteUser = function (id, callback) {
$.ajax({
url: "../API/Users/DeleteUser/" + id,
type: "POST",
success: function () {
callback;
}
});
}

Related

Calling ASP.NET Web Service with XMLHttpRequest

I have a web service defined as:
[ScriptService]
public class LoginController : ApiController
{
[HttpGet]
[WebMethod]
public IEnumerable<string> getUsers()
{
try
{
using (var context = new questionanswerEntities())
{
var users = context.users.ToList();
string[] result = new string[users.Count];
for (int i = 0; i < users.Count; ++i)
{
user u = users[i];
result[i] = u.id + " " + u.name + " " + u.email;
}
return result;
}
}
catch (MySqlException ex)
{
throw (ex);
}
catch (EntityException ex)
{
throw (ex);
}
}
}
I have published this service to my localhost and I am able to call and test it with a browser and the result is:
<ArrayOfstring>
<string>1 Olcay aaa#aaa.com</string>
<string>2 Mukaddes aaa#aaa.com</string>
<string>3 Saduman sadumanertas#gmail.com</string>
<string>4 Bernam bernella#bb.com</string>
<string>8 Bernella bernella#gmail.com</string>
<string>9 Bernella bernella#gmail.com</string>
<string>10 Bernella bernella#gmail.com</string>
<string>11 Bernella bernella#gmail.com</string>
<string>12 Bernella bernella#gmail.com</string>
<string>13 Bernella bernella#gmail.com</string>
<string>14 lala loorrrrr#gmail.com</string>
<string>15 lala loorrrrr#gmail.com</string>
</ArrayOfstring>
What I need is to call this service from JavaScript. I am trying this:
function callService() {
var url = "http://localhost:1903/QATest/login/getUsers";
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader("Content-type", "application/json");
xhr.onload = function () {
var response = xhr.responseText;
console.log("XHR - onload - Result of getUsers: " + response);
document.getElementById('response').innerHTML = "Response: " + response;
};
xhr.onerror = function () {
console.log("XHR - onerror - Web servis cagirilirken hata olustu!");
};
xhr.send();
};
But getting this error:
XMLHttpRequest cannot load http://localhost:1903/QATest/login/getUsers. Invalid HTTP status code 405
My ASP.NET service is hosted on my localhost and client side codes are hosted at my remote host at 'www.olcayertas.com'.
What am I doing wrong?
UPDATE:
I have started this project as ASP.NET Web application and then selected Web API option. So there is no any asmx files as web service projects. But Exposing Web Services to Client Script suggests adding following to client side for ASP.NET web pages:
<asp:ScriptManager runat="server" ID="scriptManager">
<Services>
<asp:ServiceReference
path="~/WebServices/SimpleWebService.asmx" />
</Services>
</asp:ScriptManager>
But I have to call my services using JavaScript from a PHP page.
How can I do that?
You are being blocked because you are attempting a Cross-Origin XMLHttpRequest which fails because of the Same-origin policy
You will need to configure CORS on you local host to allow requests from 'www.olcayertas.com'. With IIS you might just need to add this to the web.config;
<add name="Access-Control-Allow-Origin" value="*" />
Be careful though as this opens up your service to requests from any domain. You should probably only allow requests from 'www.olcayertas.com' if you know this is the only consumer of your web service.

Elmah.axd on WebAPI 2.2 - No HTTP Resource was found

I'm trying to access /elmah.axd in my broswer, but it returns:
{"message":"No HTTP resource was found that matches the request URI 'http://services.domain.com/elmah.axd'."}
The server is local (127.0.0.1) and even on that I have my web.config Elmah settings to secure it this way:
<elmah>
<security allowRemoteAccess="true" />
</elmah>
My WebApiConfig looks like:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Locally only you will be able to see the exception errors
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
// Web API routes
config.Routes.IgnoreRoute("elmah", "elmah.axd");
config.Routes.IgnoreRoute("allemah", "elmah.axd/{*pathInfo}");
config.Routes.IgnoreRoute("elmahgeneric", "{resource}.axd/{*everything}");
config.MapHttpAttributeRoutes();
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
I even tried ignoring only one route at the time from any of the 3 combinations and no luck.
finally my global.asax looks like this:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
Any hint or idea of what I could be missing, would be good.
Thanks in advance, really appreciate your time looking into this.
I finally made it.
By adding to the WebApiConfig.cs file
config.Routes.MapHttpRoute("AXD", "{resource}.axd/{*pathInfo}", null, null, new StopRoutingHandler());
The entire code of the WebApiConfig.cs file looks like this:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Locally only you will be able to see the exception errors
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
// Web API routes
config.MapHttpAttributeRoutes();
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Routes.MapHttpRoute("AXD", "{resource}.axd/{*pathInfo}", null, null, new StopRoutingHandler());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
And the final change is add this to the global.asax under application_start method
RouteTable.Routes.Ignore("{resource}.axd/{*pathInfo}");
Special thanks all who helped me with this issue.
When I added the following in in the global.asax under the application_start method:
RouteTable.Routes.Ignore("{resource}.axd/{*pathInfo}");
It did not work until I put it above ...
GlobalConfiguration.Configure(WebApiConfig.Register);
The order of the routes that you add to the Routes property is significant, because the application uses the first route in the collection that matches the URL.
https://msdn.microsoft.com/en-us/library/system.web.routing.routetable.routes(v=vs.110).aspx
The most likely problem is missing handlers to generate the error page.
Ensure the following handlers are configured your web.config file:
<system.web>
...
<httpHandlers>
...
<add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
<system.webServer>
...
<handlers>
...
<add name="Elmah" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</handlers>
With those settings in place you should be able to access elmah.axd and secure it as expected.
You can grab a sample config file that was updated from a fresh project as an example here: https://brutaldev.com/download/30987439.zip
For me adding
config.Routes.IgnoreRoute("axd", "{resource}.axd/{*pathInfo}");
to WebApiConfig was enough.

WebAPI : 403 Forbidden after publish website

Alright, I'm having a tough time locating the problem since it works locally but after doing a publish the results are simply:
Error Code: 403 Forbidden. The server denied the specified Uniform Resource Locator (URL). Contact the server administrator. (12202)
The code:
[RoutePrefix("api/v1/project")]
public class ProjectController : BaseApiController
{
[HttpGet]
public HttpResponseMessage GetProjects()
{
HttpResponseMessage resp = new HttpResponseMessage(HttpStatusCode.OK);
if(User.Identity.IsAuthenticated)
{
var model = new ModelFactory().CreateProjects();
resp = Request.CreateResponse(HttpStatusCode.OK, model);
}
return resp;
}
}
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// all actions under /project routes require authentication
config.Routes.MapHttpRoute(
name: "ProjectApi",
routeTemplate: "api/v1/{controller}/{action}/{apikey}",
defaults: new { apikey = RouteParameter.Optional },
constraints: new { controller = "project" },
handler: new BasicAuthHandler(config));
// all routes requires an api key
config.MessageHandlers.Add(new ApiKeyHandler());
config.MapHttpAttributeRoutes();
}
}
I've tried several "solutions" from the net yet none of them seems to fix this. I've added the:
// Stop IIS/Asp.Net breaking our routes
RouteTable.Routes.RouteExistingFiles = true;
from: http://www.grumpydev.com/2013/09/17/403-14-error-when-trying-to-access-a-webapi-route/
And also made sure that:
<modules runAllManagedModulesForAllRequests="true">
Having the code above, using the following link gives a successful connection where it checks (in the correct order) the APIkey (ApiKeyHandler), checks if the user needs to log in(BasicAuthHandler) and then goes to method in the controller ({controller}/{action}).
// THIS WORKS!
http://localhost:51077/api/v1/project/getprojects?apikey=123456
then we do a publish and tries the same thing
// This is haunted with number 403
http://website.com/api/v1/project/getprojects?apikey=123456
gives the Error Code: 403 Forbidden.
I am clueless. I've even tried changing the whole publish folder's security settings for "NETWORK SERVICE" to full access.. no change.
Let me know if you need any more intel.
Called the web server machine fellas and they had a firewall blocking incoming webapi calls with authenticating. It now works as it should :)

undefined 405 (Method Not Allowed) Angularjs + ajax

i've been trying my hand on Angularjs + rest and when trying to get the data , i am getting the error - undefined 405 (Method Not Allowed)
main.js --
url:'http://abc.org/angularDemo/rest/demo/studentJson',
type:'GET'}).success(function (sampleData) {
alert("here ... "+sampleData);
$scope.setPagingData(sampleData,page,pageSize);
}).error(function () {
alert("Error getting users.");
});
#Path("/demo")
#XmlRootElement
public class AngularDataObj {
#GET
#Path("/studentJson")
#Produces(MediaType.APPLICATION_JSON)
public List<StudentObj> studentJson(){
List<StudentObj> list = new ArrayList<StudentObj>();
StudentObj obj = new StudentObj();
//Map<String,StudentObj> map = new HashMap<String,StudentObj>();
for(int i=0 ; i< 50; i++){
obj.setName("name"+i);
obj.setAge(20+i);
obj.setId(i);
obj.setLocation("location"+i);
list.add(obj);
//map.put(""+i, obj);
}
return list;
}
}
i am able to see the json object when i directly access this url - http://abc.org/angularDemo/rest/demo/studentJson
Change your web.config, removing the following keys:
<system.webServer>
<modules>
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
</handlers>
</system.webServer>
From:
http://www.askamoeba.com/Answer/196/Http-405-Method-Not-Allowed-error-Web-API-Controller-PUT-Request-AngularJs
Regards.

ASP.NET WEB API 2 OWIN Authentication unsuported grant_Type

Hi I am trying to set up OAuth bearrer token authentication in my ASP.NET Web API 2 project.
I have two project one will be the WEB API Project and the other a SPA project.
Here is what I have done so far:
I have created the OWIN Startup class:
[assembly: OwinStartup(typeof(CodeArt.WebApi.App_Start.Startup))]
namespace CodeArt.WebApi.App_Start
{
public class Startup
{
static Startup()
{
PublicClientId = "self";
UserManagerFactory = () => new UserManager<UserModel>(new UserStore<UserModel>());
OAuthOptions = new OAuthAuthorizationServerOptions {
TokenEndpointPath = new PathString("/Token"),
Provider = new OAuthAuthorizatonServer(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static Func<UserManager<UserModel>> UserManagerFactory { get; set; }
public static string PublicClientId { get; private set; }
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalBearer);
app.UseOAuthBearerTokens(OAuthOptions);
}
}
I have configured Web API to use only bearer token authentication:
private static void ConfigureBearerTokenAuthentication(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthOptions.AuthenticationType));
}
I have configured WEB API to support CORS:
private static void ConfigureCrossOriginResourseSharing(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
}
I have created the OAuthAuthorizationServerProvider class.From this class I only managed to make my code call this method:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
if(context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
The if condition inside of it always gets executed.
On my spa project I have the following:
This is my viewModel:
var vm = {
grant_type: "password",
userName: ko.observable(),
password: ko.observable()
};
When the login button gets clicked I call this function:
var http = {
post:function(url, data) {
return $.ajax({
url: url,
data: data,
type: 'POST',
contentType: 'application/json',
dataType: 'jsonp'
});
}
}
function loginClick() {
var model = ko.mapping.toJS(vm.loginModel);
var rez = $.param(model);
http.post("http://localhost:3439/Token", rez)
.done(function (data) {
console.log(data);
})
.fail(function(eror, stuff, otherstuff) {
console.log(eror);
console.log(stuff);
console.log(otherstuff);
});
}
My first attempt I have set the post calls dataType to json and I got this errors:
OPTIONS ...:3439/Token 400 (Bad Request) jquery.js:7845
OPTIONS ...:3439/Token No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin
'...:3304' is therefore not allowed access.
jquery.js:7845
XMLHttpRequest cannot load ...3439/Token. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin '...3304' is therefore not allowed
access.
The 3 dots represent http://localhost.
The second time arround I set it datatype to jsonp and I got back an error that stated unsupported "unsupported_grant_type".
Both calls make it to ValidateClientAuthentication that I mentioned above but they are both sent back as a failed request.
Now I am guessing that the problem is more related to how I am sending data instead of the grand_type because the SPA template in Visual Studion set's the grant type to grant_type: "password" like I did.
Also I have read that I have to serialize the data not send it in json in order for this to work here is the exact json serialized data that get's sent:
"grant_type=password&userName=aleczandru&password=happynewYear&moduleId=models%2FappPostModels%2FloginModel"
The model id property get's set to all my object in my SPA template by Durandal Framework.
Can anyone tell me what I am doing wrong I have been trying to figure this out for the last two days?
Add the following line of code to GrantResourceOwnerCredentials, which will add the header to the response.
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
for more information refer to:
web-api-2-0-cors-and-individual-account-identity
Like Robin Karlsson said, you should use:
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
in your Startup configuration.
And make sure it's the only cors statement (don't mix them) and the first statement in your Startup.

Resources