Why Does My Web API Fail On Put Only? - c#

I have written a Web API in ASP.NET and I have written a test method in a desktop application that calls the Web API. The test method inserts a new record, updates the record, then deletes the record. The insert is successful and I can see the new record in the database table. The delete method also works and the record is removed from the table. The update method fails with this error message:
Response status code does not indicate success: 405 (Method Not Allowed).
I created the Web API using Visual Studio's normal process and I did not modify the framework code that it built. I have created a utility function called WebSave (shown below) to handle both the insert and update functions. I can't see why the update is any different in substance. Hopefully someone here can tell me.
private void SaveData_Test()
{
string sRequestURI = "http://localhost:49625/api/tipshare_def";
int iStoreNo = 40004;
tipshare_def oTipShareDef = new tipshare_def() { storeno = iStoreNo, tipshare_type = 1, total_bar_share = (decimal)1.0, food_bar_share = (decimal)0.6, alc_bar_share = (decimal)0.4, job_exclude1 = 1, job_exclude2 = 1, job_exclude3 = 0 };
Utilities.WebSave(sRequestURI, oTipShareDef, typeof(tipshare_def), true);
oTipShareDef.food_bar_share = (decimal)0.7;
oTipShareDef.alc_bar_share = (decimal)0.3;
Utilities.WebSave(sRequestURI, oTipShareDef, typeof(tipshare_def), false);
string sRequestURI2 = "http://localhost:49625/api/tipshare_def/" + iStoreNo.ToString("0");
Utilities.WebDelete(sRequestURI2);
}
public static async Task WebSave(string sRequestURI, object oRecord, Type tRecordType, bool bNewRecord)
{
try
{
HttpClient oHttpClient = new HttpClient();
if (bNewRecord == true)
{
HttpResponseMessage oResponse = await oHttpClient.PostAsJsonAsync(sRequestURI, oRecord);
oResponse.EnsureSuccessStatusCode();
HttpStatusCode oStatus = oResponse.StatusCode;
string sStatus = oStatus.ToString();
}
else
{
HttpResponseMessage oResponse = await oHttpClient.PutAsJsonAsync(sRequestURI, oRecord);
oResponse.EnsureSuccessStatusCode();
HttpStatusCode oStatus = oResponse.StatusCode;
string sStatus = oStatus.ToString();
}
}
catch (Exception oException)
{
HandleError("WebSave", oException);
}
}
// Here is the related Web API code:
// PUT: api/tipshare_def/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Puttipshare_def(int id, tipshare_def tipshare_def)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != tipshare_def.storeno)
{
return BadRequest();
}
db.Entry(tipshare_def).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!tipshare_defExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/tipshare_def
[ResponseType(typeof(tipshare_def))]
public async Task<IHttpActionResult> Posttipshare_def(tipshare_def tipshare_def)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.tipshare_def.Add(tipshare_def);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (tipshare_defExists(tipshare_def.storeno))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = tipshare_def.storeno }, tipshare_def);
}

In this case, your parameters do not match. You have an Id in your PUT request but not in your POST.
As a general rule, if you receive a 405 response on only a few verbs (usually PUT and DELETE), you need to update your web.config with one of the following changes:
<system.webServer>
<handlers>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*."
verb="GET,HEAD,POST,DEBUG,PUT,DELETE"
type="System.Web.Handlers.TransferRequestHandler"
preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
It is also possible that WEBDAV will interfere with your requests. The following will remove it.
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule"/>
</modules>

I've gotten this error from one of two different reasons:
The HTTP Verb is not allowed.
The fix for this is posted above by David L.
Cross-origin resource sharing is not enabled for the service.
The solution to this can be a bit more complex, as it depends on which version of the Web API that you are using and which version of ASP.NET you are using. Microsoft has provided an article on enabling Cross-Origin Requests in ASP.NET Web API 2, which is a good place to start:
http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Related

Calling a Web API hosted on the same server from a controller, the AD name for auth is the IIS app pool name not the user name

APPLICATION DESCRIPTION
The application consist of two functions. Within the controller is a call to the web API as follows:
private async Task<string> CallMciPostsApi()
{
using (var handler = new HttpClientHandler { UseDefaultCredentials = true })
using (var client = new HttpClient(handler))
{
var response = await
client.GetAsync("https://medapp10.med.state.sbu/emed2webapi/api/mcipost");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
else
{
EventLogger.Info(response.ReasonPhrase);
}
}
return null;
}
The controller has [Authorize(Roles = ("MED Supervisors Overseas"))]
Within the razor page an Ajax call:
function getCountries() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://medapp10.med.state.sbu/Emed2webapi/api/mcipost');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function () {
loadCountrySelect(JSON.parse(xhr.responseText), true);
}
xhr.send();
}
web.config
<authorization>
<allow users="*" />
<deny users="?" />
</authorization>
<identity impersonate="false" />
Web API
[Authorize(Roles = ("MED Supervisors Overseas"))]
[RoutePrefix("api/mcipost")]
public class MciPostController : BaseController
{
/// <summary>
/// Status Return a list of MciPost objects
/// </summary>
/// <returns></returns>
[Route()]
[HttpGet]
public virtual IHttpActionResult Get()
{
try
{
var entityList = MCIPostRepository.Get();
if (entityList == null)
{
return InternalServerError(); // 500 Internal Server Error
}
if (entityList.Count == 0)
{
return NotFound();
}
return Ok(entityList); // 200 OK
}
catch (Exception ex)
{
EventLogger.Error(ex);
}
}
}
PROBLEM
With the web API installed and running the application under Visual
Studio - IIS Express, the AJAX call fails but the controller call
succeeds. Visual Studio is running as local administrator Based on an issue with running a post-build script and being unable to see network resources, the fact that VS is running as admin may be significant.
However, with the application installed on the web API host server,
the opposite occurs and the AJAX call succeeds and the controller
call fails (response.ReasonPhrase="UnAuthorized").
After many days of research and consulting with colleagues, it remains a mystery why this is so. This is my first time dealing with Active Directory and I suspect I am missing something obvious for someone who has worked with it more extensively.
Regarding problem 2, created a custom authorized attribute to log authorization errors. The active directory name is the 'IIS APPPOOL' name.
Tried setting impersonate="true" in the web.config
**
From:
Web api authorization by capturing client's service account name
Changed HttpClient to WebClient (UseDefaultCredentials=true;) and set impersonate=true on web.config.

Identity's Register Method Not working on IIS

I have Implemented ASP.NET Identity management and its working fine when i run my application on Local. Then i Deployed it to IIS , Now the 'Register' method of Account Controller in Identity is throwing me Unauthorized response.
I have updated my Connection string too. Even I hit the same URL from Postman , I am getting the result '200 OK'. That is the user is getting inserted in 'AspNetUsers' Table
Please Help What is the Problem.
Is Something Went wrong deploying the application. i have deployed it like 5 times freshly.
My Register Method :-
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public HttpResponseMessage Register(RegisterBindingModel model)
{
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
if (!ModelState.IsValid)
{
return Request.CreateResponse(HttpStatusCode.OK, modelError.CreateModelError(ModelState));
}
try
{
var user = new ApplicationUser() { UserName = model.UserName, Email = model.Email };
IdentityResult result = UserManager.Create(user, model.UserName);
if (!result.Succeeded)
{
httpResponseMessage.Content.Headers.Add("Identity_Insert", "Failed");
httpResponseMessage.StatusCode = HttpStatusCode.InternalServerError;
}
}
catch (Exception ex)
{
employeeMaster.LogError<EmployeeMaster>(ex, Global.Class.Common.ErrorType.APIError, 0);
httpResponseMessage.StatusCode = HttpStatusCode.InternalServerError;
}
return httpResponseMessage;
}

WebException on HTTP request while debugging

I have a ASP.NET project which involves sending HTTP requests via the Web-API Framework. The following exception is only raised when debugging:
The server committed a protocol violation. Section=ResponseStatusLine
The project runs perfectly if I "Start Without Debugging".
How should I resolve this exception?
Any help is appreciated!
Update
The problem seems related to the ASP.NET MVC Identity Framework.
To access other Web-API methods, the client application has to first POST a login request (The login request does not need to be secure yet, and so I am sending the username and password strings directly to the Web-API POST method). If I comment out the login request, no more exception is raised.
Below are the relevant code snippets:
The Post method:
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
AccountAccess ac = new AccountAccess();
public async Task<HttpResponseMessage> Post()
{
string result = await Request.Content.ReadAsStringAsync();
LoginMessage msg = JsonConvert.DeserializeObject<LoginMessage>(result);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
var user = UserManager.Find(msg.username, msg.password);
if (user == null)
return response;
if (user.Roles == null)
return response;
var role = from r in user.Roles where (r.RoleId == "1" || r.RoleId == "2") select r;
if (role.Count() == 0)
{
return response;
}
bool task = await ac.LoginAsync(msg.username, msg.password);
response.Content = new StringContent(task.ToString());
return response;
}
The Account Access class (simulating the default AccountController in MVC template):
public class AccountAccess
{
public static bool success = false;
public AccountAccess()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountAccess(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
public async Task<bool> LoginAsync(string username, string password)
{
var user = await UserManager.FindAsync(username, password);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
return true;
}
else
{
return false;
}
}
~AccountAccess()
{
if (UserManager != null)
{
UserManager.Dispose();
UserManager = null;
}
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.Current.GetOwinContext().Authentication;
}
}
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
}
Below are the relevant code snippets:
In client application:
public static async Task<List<T>> getItemAsync<T>(string urlAction)
{
message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(urlBase + urlAction);
HttpResponseMessage response = await client.SendAsync(message);
string result = await response.Content.ReadAsStringAsync();
List<T> msgs = JsonConvert.DeserializeObject<List<T>>(result);
return msgs;
}
In Web-API controller:
public HttpResponseMessage Get(string id)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
if (id == "ItemA")
{
List<ItemAMessage> msgs = new List<ItemAMessage>();
// some code...
response.Content = new StringContent(JsonConvert.SerializeObject(msgs));
}
else if (id == "ItemB")
{
List<ItemBMessage> msgs = new List<ItemBMessage>();
// some code...
response.Content = new StringContent(JsonConvert.SerializeObject(msgs));
}
return response;
}
Some observations I have:
I thought that I may need to send the request asynchronously (with the async-await syntax), but the exception still persists that way.
If I step through the code, the request does enter the HTTP method, but the code breaks at random line (Why?!) before returning the response, so I assume no response is being sent back.
I have tried the following solutions, as suggested in answers to similar questions, none of which works for me:
Setting useUnsafeHeaderParsing to true
Adding the header Keep-Alive: false
Changing the port setting of Skype (I don't have Skype, and port 80 and 443 are not occupied)
Additional information, in case they matter:
Mac OS running Windows 8.1 with VMware Fusion
Visual Studio 2013
.NET Framework 4.5
IIS Express Server
Update 2
The exception is resolved, but I am unsure of which modification did the trick. AFAIK, either one or both of the following fixed it:
I have a checkConnection() method, which basically sends a GET request and return true on success. I added await to the HttpClient.SendAsync() method and enforced async all the way up.
I retracted all code in the MainWindow constructor, except for the InitializeComponent() method, into the Window Initialized event handler.
Any idea?
Below are relevant code to the modifications illustrated above:
the checkConnectionAsync method:
public static async Task<bool> checkConnectionAsync()
{
message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(urlBase);
try
{
HttpResponseMessage response = await client.SendAsync(message);
return (response.IsSuccessStatusCode);
}
catch (AggregateException)
{
return false;
}
}
Window Initialized event handler (retracted from the MainWindow constructor):
private async void Window_Initialized(object sender, EventArgs e)
{
if (await checkConnectionAsync())
{
await loggingIn();
getItemA();
getItemB();
}
else
{
logMsg.Content = "Connection Lost. Restart GUI and try again.";
}
}
Update 3
Although this may be a little off-topic, I'd like to add a side note in case anyone else falls into this – I have been using the wrong authentication approach for Web-API to start with. The Web-API project template already has a built-in Identity framework, and I somehow "replaced" it with a rather simple yet broken approach...
This video is a nice tutorial to start with.
This article provides a more comprehensive explanation.
In the Client Application you are not awaiting task. Accessing Result without awaiting may cause unpredictable errors. If it only fails during Debug mode, I can't say for sure, but it certainly isn't the same program (extra checks added, optimizations generally not enabled). Regardless of when Debugging is active, if you have a code error, you should fix that and it should work in either modes.
So either make that function async and call the task with the await modifier, or call task.WaitAndUnwrapException() on the task so it will block synchronously until the result is returned from the server.
Make sure URL has ID query string with value either as Item A or Item B. Otherwise, you will be returning no content with Http status code 200 which could lead to protocol violation.
When you use SendAsync, you are required to provide all relevant message headers yourself, including message.Headers.Authorization = new AuthenticationHeaderValue("Basic", token); for example.
You might want to use GetAsync instead (and call a specific get method on the server).
Also, are you sure the exception is resolved? If you have some high level async method that returns a Task and not void, that exception might be silently ignored.

WEB API PUT method is not working and getting HTTP/1.1 405 Method Not Allowed

Hi I am not understanding how to fix this can any one please let me know why my put method is not getting executed. I get error
HTTP/1.1 405 Method Not Allowed
in my PUT method. Is there any way to fix using ROUTE attribute to debug my PUT method.
public class UserProfessionController : ApiController
{
//[Route("")]
public IHttpActionResult Get()
{
return Ok();
}
[HttpPut]
public HttpResponseMessage UpdateUserProfession([FromUri]string categoryCode)
{
try
{
int userId = 1;
if (String.IsNullOrWhiteSpace(categoryCode))
{
var specialitiesRepository = new UserRepository();
if(specialitiesRepository.UpdateUserProfession(userId, categoryCode) > 0)
{
return Request.CreateResponse(HttpStatusCode.Created);
}
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Invalid Input Data");
}
else
{
throw new Exception("Category Code-" + categoryCode + "For the User " + userId + " is not valid");
}
}
catch (Exception ex)
{
var msg = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(ex.Message),
ReasonPhrase = "Invalid Input Data"
};
throw new HttpResponseException(msg);
}
}
}
in web.config system.webServer:
<modules runAllManagedModulesForAllRequests="true">
<remove name="WebDAVModule"/>
</modules>
After struggling with this issue - tried the changes to config and IIS regarding removing WebDAV with no success, I tried the following and the issue was resolved.
Remove WebDAV publishing from the IIS install from control panel/program and features - select "turn windows features on/off"
Found this solution at the link below.
https://ignas.me/tech/405-method-not-allowed-iis/
This fixed the issue on both Windows 10 and 7.

How to receive auth failure from ASP.NET MVC5+Identity server on .NET client?

i'm using ASP.NET MVC5 with the latest Identity and Signalr on the server and have .NET client app. Currently i have working auth logic implemented but i don't get it how can i get auth failure in .NET desktop client?
Here is my .NET desktop client auth code:
private static async Task<bool> AuthenticateUser(string siteUrl, string email, string password)
{
try
{
var handler = new HttpClientHandler { CookieContainer = new CookieContainer() };
using (var httpClient = new HttpClient(handler))
{
var loginUrl = siteUrl + "Account/Login";
_writer.WriteLine("Sending http GET to {0}", loginUrl);
var response = await httpClient.GetAsync(loginUrl);
var content = await response.Content.ReadAsStringAsync();
_verificationToken = ParseRequestVerificationToken(content);
content = _verificationToken + "&UserName="+email+"&Password="+password+"&RememberMe=false";
_writer.WriteLine("Sending http POST to {0}", loginUrl);
response = await httpClient.PostAsync(loginUrl, new StringContent(content, Encoding.UTF8, "application/x-www-form-urlencoded"));
content = await response.Content.ReadAsStringAsync();
_verificationToken = ParseRequestVerificationToken(content);
_connection.CookieContainer = handler.CookieContainer;
return true;
}
}
catch (Exception ex)
{
Logger.Log(ex, "Auth");
return false;
}
}
where _connection is a hub connection which receives cookie needed for hub auth. The problem is that httpCLient.PostAsync() always return valid result and i don't get it how i can implement auth failure detection.
Here is server login code:
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
On failure it just adds error string on the page.
Please advice what is the better way to implement auth result.
This is strange that i got no single answer for this question. I've come to an intermediate solution:
Add unique hidden tags for login and index pages (on failure login page is displayed again, on success - index page)
<div style="display: none;" id="#SharedData.Constants.INDEX_PAGE_TAG"></div>
In .NET client check content string for the specific tag presence.
I don't think this the preformance-wise solution but at least it works...

Categories

Resources