Simon
asked on
Receiving Error 405 when POST or PUT from Angular 7 to ASP.NET WebAPI
Hi,
I am studying Angular 7 with ASP.NET WebAPI.
I host ASP.NET Web API REST Service in IIS 10.
In Angular 7, I can successfully GET from ASP.NET WebAPI, but I receive Error 405 when POST or PUT to ASP.NET WebAPI.
(I can successfully GET / POST / PUT to the same ASP.NET WebAPI using Postman.)
I cannot figure it out, thanks for your help.
Client code:
WebAPI code:
WebAPI Web.config:
I am studying Angular 7 with ASP.NET WebAPI.
I host ASP.NET Web API REST Service in IIS 10.
In Angular 7, I can successfully GET from ASP.NET WebAPI, but I receive Error 405 when POST or PUT to ASP.NET WebAPI.
(I can successfully GET / POST / PUT to the same ASP.NET WebAPI using Postman.)
I cannot figure it out, thanks for your help.
Client code:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { MDLSyst } from '../_Model/MDLSyst';
import { MessageService } from '../_Service/message.service';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
@Injectable({
providedIn: 'root'
})
export class SystService {
private systUrl = 'http://localhost/fars-api/api/syst'; // URL to web api
constructor(private http: HttpClient,
private messageService: MessageService) { }
getSystList(): Observable<MDLSyst[]> {
return this.http.get<MDLSyst[]>(this.systUrl)
.pipe(
tap(_ => this.log('fetched SystList')),
catchError(this.handleError('getSystList', []))
);
}
/** GET syst by id. Will 404 if id not found */
getSyst(id: number): Observable<MDLSyst> {
const url = `${this.systUrl}/${id}`;
return this.http.get<MDLSyst>(url).pipe(
tap(_ => this.log(`fetched syst id=${id}`)),
catchError(this.handleError<MDLSyst>(`getSyst id=${id}`))
);
}
/** POST: add a new syst to the server */
addSyst(syst: MDLSyst): Observable<MDLSyst> {
return this.http.post<MDLSyst>(this.systUrl, syst, httpOptions).pipe(
tap((syst: MDLSyst) => this.log(`added syst w/ id=${syst.Id}`)),
catchError(this.handleError<MDLSyst>('addSyst'))
);
}
/** PUT: update the syst on the server */
updateSyst(syst: MDLSyst): Observable<any> {
const id = syst.Id;
const url = `${this.systUrl}/${id}`;
return this.http.put(url, syst, httpOptions).pipe(
tap(_ => this.log(`updated syst id=${syst.Id}`)),
catchError(this.handleError<any>('updateSyst'))
);
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
//console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
this.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
/** Log a SystService message with the MessageService */
private log(message: string) {
this.messageService.add(`SystService: ${message}`);
}
}
WebAPI code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Data;
using System.Data.SqlClient;
using FARS.MDL;
namespace FarsApi.Controller
{
public class SystController : ApiController
{
// GET: api/Syst
public IHttpActionResult Get()
{
ICollection<MDLSyst> SystList = new List<MDLSyst>();
// get Syst from database
using (SqlDataReader rdr = SQLHelper.ExecuteReader(SQLHelper.CONN_STRING, CommandType.StoredProcedure, "SS_SYST_L", null))
{
while (rdr.Read())
{
MDLSyst itmDept = new MDLSyst { Id = rdr.GetInt16(0), Status = rdr.GetString(1), Type = rdr.GetString(2), Brief = rdr.GetString(3), Name = rdr.GetString(4), Frequency = rdr.GetString(5), ContactName = rdr.GetString(6), ContactEmail = rdr.GetString(7), ContactTel = rdr.GetString(8), DisplayOrder = rdr.GetInt16(9), UpdateTime = rdr.GetDateTime(10), UpdateUser = rdr.GetString(11) };
SystList.Add(itmDept);
}
}
//
if (SystList.Count == 0)
{
return NotFound();
}
return Ok(SystList);
}
// GET: api/Syst/5
public IHttpActionResult Get(Int16 Id)
{
// Set up a return value
MDLSyst Syst = null;
// Create a parameter
SqlParameter parm = new SqlParameter("@ID", SqlDbType.SmallInt);
// Bind the parameter
parm.Value = Id;
// Execute the query
using (SqlDataReader rdr = SQLHelper.ExecuteReader(SQLHelper.CONN_STRING, CommandType.StoredProcedure, "SS_SYST_S", parm))
{
while (rdr.Read())
{
Syst = new MDLSyst { Id = rdr.GetInt16(0), Status = rdr.GetString(1), Type = rdr.GetString(2), Brief = rdr.GetString(3), Name = rdr.GetString(4), Frequency = rdr.GetString(5), ContactName = rdr.GetString(6), ContactEmail = rdr.GetString(7), ContactTel = rdr.GetString(8), DisplayOrder = rdr.GetInt16(9), UpdateTime = rdr.GetDateTime(10), UpdateUser = rdr.GetString(11) };
}
}
if (Syst == null)
{
return NotFound();
}
return Ok(Syst);
}
// POST: api/Syst
public IHttpActionResult Post(MDLSyst Syst)
{
if (!ModelState.IsValid)
return BadRequest("Not a valid model");
// Create the parameters
SqlParameter[] parms = new SqlParameter[] {
//new SqlParameter("@ID", SqlDbType.SmallInt),
new SqlParameter("@STATUS", SqlDbType.Char, 1),
new SqlParameter("@TYPE", SqlDbType.VarChar, 10),
new SqlParameter("@BRIEF", SqlDbType.NVarChar, 20),
new SqlParameter("@NAME", SqlDbType.NVarChar, 50),
new SqlParameter("@FREQUENCY", SqlDbType.VarChar, 20),
new SqlParameter("@CONTACT_NAME", SqlDbType.NVarChar, 50),
new SqlParameter("@CONTACT_EMAIL", SqlDbType.VarChar, 255),
new SqlParameter("@CONTACT_TEL", SqlDbType.VarChar, 20),
new SqlParameter("@DISPLAY_ORDER", SqlDbType.SmallInt),
new SqlParameter("@UPDATE_USER", SqlDbType.VarChar, 20)
};
// Bind the parameters
//parms[0].Value = Syst.Id
parms[0].Value = Syst.Status;
parms[1].Value = Syst.Type;
parms[2].Value = Syst.Brief;
parms[3].Value = Syst.Name;
parms[4].Value = Syst.Frequency;
parms[5].Value = Syst.ContactName;
parms[6].Value = Syst.ContactEmail;
parms[7].Value = Syst.ContactTel;
parms[8].Value = Syst.DisplayOrder;
parms[9].Value = Syst.UpdateUser;
// Execute the query
SQLHelper.ExecuteNonQuery(SQLHelper.CONN_STRING, CommandType.StoredProcedure, "SS_SYST_I", parms);
return Ok();
}
// PUT: api/Syst/5
public IHttpActionResult Put(Int16 Id, MDLSyst Syst)
{
if (!ModelState.IsValid)
return BadRequest("Not a valid model");
// Create the parameters
SqlParameter[] parms = new SqlParameter[] {
new SqlParameter("@ID", SqlDbType.SmallInt),
new SqlParameter("@STATUS", SqlDbType.Char, 1),
new SqlParameter("@TYPE", SqlDbType.VarChar, 10),
new SqlParameter("@BRIEF", SqlDbType.NVarChar, 20),
new SqlParameter("@NAME", SqlDbType.NVarChar, 50),
new SqlParameter("@FREQUENCY", SqlDbType.VarChar, 20),
new SqlParameter("@CONTACT_NAME", SqlDbType.NVarChar, 50),
new SqlParameter("@CONTACT_EMAIL", SqlDbType.VarChar, 255),
new SqlParameter("@CONTACT_TEL", SqlDbType.VarChar, 20),
new SqlParameter("@DISPLAY_ORDER", SqlDbType.SmallInt),
new SqlParameter("@UPDATE_USER", SqlDbType.VarChar, 20)
};
// Bind the parameters
parms[0].Value = Syst.Id; // Id
parms[1].Value = Syst.Status;
parms[2].Value = Syst.Type;
parms[3].Value = Syst.Brief;
parms[4].Value = Syst.Name;
parms[5].Value = Syst.Frequency;
parms[6].Value = Syst.ContactName;
parms[7].Value = Syst.ContactEmail;
parms[8].Value = Syst.ContactTel;
parms[9].Value = Syst.DisplayOrder;
parms[10].Value = Syst.UpdateUser;
// Execute the query
SQLHelper.ExecuteNonQuery(SQLHelper.CONN_STRING, CommandType.StoredProcedure, "SS_SYST_U", parms);
return Ok();
}
}
}
WebAPI Web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add name="FARS_Conn" connectionString="Data Source=localhost; Initial Catalog=FARS; user id=FARS_User; password=xxx;" />
</connectionStrings>
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" />
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" />
</compilers>
</system.codedom>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
<handlers>
<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>
</configuration>
Generally 405s mean...
1) You've requested an existing/valid resource, else you'd have gotten a 404.
2) How you accessed the resource is invalid.
3) Best way to debug this is using curl as your client + tracking exact logged errors on your API side.
Note: This presupposes you have highly granular logging running for your API side, so every minor error/warning is logged.
Tip: Writing APIs in some languages is difficult + time consuming (ASP) while other languages (PHP) you'll be able to write API code in a few minutes.
You might consider starting with PHP to build a prototype + then move to ASP, if you really must.
1) You've requested an existing/valid resource, else you'd have gotten a 404.
2) How you accessed the resource is invalid.
3) Best way to debug this is using curl as your client + tracking exact logged errors on your API side.
Note: This presupposes you have highly granular logging running for your API side, so every minor error/warning is logged.
Tip: Writing APIs in some languages is difficult + time consuming (ASP) while other languages (PHP) you'll be able to write API code in a few minutes.
You might consider starting with PHP to build a prototype + then move to ASP, if you really must.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
1) Setup all your dev code on the public IPs where your API code will be running.
Tip: Use OVH for cheap + fast hardware.
Tip: Run your API services in an LXD container, so you can easily clone your container to multiple other machines to scale up your API throughput.
2) Go ahead + wrap your API code in HTTPS now.
3) Research CORS + setup your CORS ACLs now.
https://spring.io/understa