We help IT Professionals succeed at work.

Receiving Error 405 when POST or PUT from Angular 7 to ASP.NET WebAPI

Simon
Simon asked
on
209 Views
Last Modified: 2019-01-24
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.

While I click "save" button, it display error 405 (Method Not Allowed)
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}`);
  }
}

Open in new window


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();
        }
    }
}

Open in new window


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=\&quot;Web\&quot; /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>

Open in new window

Comment
Watch Question

David FavorFractional CTO
CERTIFIED EXPERT
Distinguished Expert 2019

Commented:
You're going to have a difficult time getting this to work locally + then you'll have to move it to some public site + start your debugging all over again.

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/understanding/CORS provides a good starting point + you'll likely require a good bit of research + thought + design to create an exact setup which will work for your situation.
David FavorFractional CTO
CERTIFIED EXPERT
Distinguished Expert 2019

Commented:
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.
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION

Gain unlimited access to on-demand training courses with an Experts Exchange subscription.

Get Access
Why Experts Exchange?

Experts Exchange always has the answer, or at the least points me in the correct direction! It is like having another employee that is extremely experienced.

Jim Murphy
Programmer at Smart IT Solutions

When asked, what has been your best career decision?

Deciding to stick with EE.

Mohamed Asif
Technical Department Head

Being involved with EE helped me to grow personally and professionally.

Carl Webster
CTP, Sr Infrastructure Consultant
Empower Your Career
Did You Know?

We've partnered with two important charities to provide clean water and computer science education to those who need it most. READ MORE

Ask ANY Question

Connect with Certified Experts to gain insight and support on specific technology challenges including:

  • Troubleshooting
  • Research
  • Professional Opinions
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.