简体   繁体   中英

Error 415 when posting to ASP.Net Core WebAPI using XMLHttpRequest

Working on a new project using a WebAPI backend server and I am having trouble posting to the controller from an actual website, despite Postman having no issues posting to the controller. I get the an error 415, the browser console logging:

HTTP415: UNSUPPORTED MEDIA TYPE - The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method.
(XHR)OPTIONS - http://localhost:5000/api/Users

Whilst the log from Kestrel is

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 OPTIONS http://localhost:5000/api/Users  0
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 OPTIONS http://localhost:5000/api/Users  0
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 415
Microsoft.AspNetCore.Mvc.StatusCodeResult:Information: Executing HttpStatusCodeResult, setting HTTP status code 415
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action SchoolsChat.Controllers.UserContoller.Post (SchoolsChat) in 2.5323ms
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action SchoolsChat.Controllers.UserContoller.Post (SchoolsChat) in 2.5323ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 6.6615ms 415 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 6.6615ms 415 

I am trying to post the following JSON:

{
    UserDOB: "2016-12-18",
    UserFirstName: "asdf",
    UserLastName: "asasdf",
    UserName: "asdf",
    UserSecret: "asdf"
}

using this TypeScript class

/**
 * JsonPost
 */
class JsonPost {
    private _response: number;
    public get Reponse(): number {
        return this._response;
    }
    constructor(link: string, data: Object) {
        let request = new XMLHttpRequest();
        request.withCredentials = true;

        request.open("POST", APIUri + link, true);
        request.setRequestHeader("content-type", "application/json");
        request.setRequestHeader("cache-control", "no-cache");
        request.onreadystatechange = () => this._response = request.status;
        console.log(request);
        request.send(JSON.stringify(data));
    }
}

The model of User is

public class User
    {
        [KeyAttribute]
        public int UserId { get; set; }
        [RequiredAttribute]
        public int SchoolId { get; set; }
        [RequiredAttribute]
        public string UserName { get; set; }
        [RequiredAttribute]
        [DataTypeAttribute(DataType.Password)]
        public string UserSecret { get; set; } // Unnecessary due to school linking?
        [RequiredAttribute]
        public virtual School UserSchool { get; set; }
    }

Whilst the controller for posting is simple, just outputting the first name

[HttpPost]
    public IActionResult Post([FromBody]User user)
    {
        Console.WriteLine(user.UserName);
        return StatusCode(200);
    }

Edit Whilst the answer from nemec was helpeful in resolving the problem, I found that for the WebAPI specifically the best resolution was to only configure cors using app.UseCors as services.AddCors in many cases didn't actually include the necessary headers in the response.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            app.UseMvc();
        }

In my case the problem was that I put a FromBody attribute before my action parameter.

From:

[HttpPost("Contact")]
public async Task<IActionResult> NewContact([FromBody]Contact contact)

To:

[HttpPost("Contact")]
public async Task<IActionResult> NewContact(Contact contact)

As Evan mentioned in his comment, your POST is turning into an OPTIONS when you make a cross-origin ajax request. Due to browsers' cross-origin security policies, your web api needs to tell the browser/js that your website is allowed to make ajax requests against it.

https://docs.microsoft.com/en-us/aspnet/core/security/cors

To setup CORS for your application add the Microsoft.AspNetCore.Cors package to your project.

Add the CORS services in Startup.cs:

 public void ConfigureServices(IServiceCollection services) { services.AddCors(); }

If you follow the linked instructions, you can even use IApplicationBuilder.UseCors to further customize which sites are allowed.

For example:

app.UseCors(builder =>
    builder.WithOrigins("http://example.com")
           .AllowAnyHeader()
);

Postman is an app and therefore has the ability to exempt itself from cross-origin rules.

Don't know why yet, I'm still pretty new to .Net Core Web API's. I removed the controller attribute [ApiController] and everything fell into place.

In my situation, I have a MVC interface & WebApi on the same project. Hope this helps someone.

We upgraded to .NET core 3.0 and this required to use [FromQuery] when you're building an object from query parameters, eg with a GET request.

Example:

[HttpGet("Hierarchy")]
public virtual async Task<IActionResult> GetHierarchy([FromServices] IDeviceHierarchyService service, [FromQuery] HierarchyStructureRequest structureLoad)

I agree with @mohas. For me, common cause of 415 errors with ASP.Net Core is a missing binding attribute or a mismatch between the request and the declared binding ([FromBody], [FromForm], [FromQuery] etc).

Older versions of MVC could often work OK without binding attributes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM