简体   繁体   English

415(不支持的媒体类型)与REST Post请求

[英]415 (Unsupported Media Type) with REST Post request

I have a react component that when a checkbox is pressed, it calls a rest api, post request with a single parameter. 我有一个react组件,当按下一个复选框时,它会调用一个rest api,使用一个参数发出请求。

I put a breakpoint in the webapi and its never hit, still I get a 415 unsopported media type on the component 我在webapi中设置了一个断点并且它从未命中过,我仍然在组件上获得了415个不支持的媒体类型

react js component (see onchange event)

import React, { Component } from 'react';
import {  Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';

class ListTenants extends Component {

    constructor(props) {
        super(props);
        this.state = {
            data: []
        };
    }



    fetchData = () => {
        adalApiFetch(fetch, "/Tenant", {})
          .then(response => response.json())
          .then(responseJson => {
            if (!this.isCancelled) {
                const results= responseJson.map(row => ({
                    key: row.ClientId,
                    ClientId: row.ClientId,
                    ClientSecret: row.ClientSecret,
                    Id: row.Id,
                    SiteCollectionTestUrl: row.SiteCollectionTestUrl,
                    TenantDomainUrl: row.TenantDomainUrl
                  }))
              this.setState({ data: results });
            }
          })
          .catch(error => {
            console.error(error);
          });
      };


    componentDidMount(){
        this.fetchData();
    }

    render() {
        const columns = [
                {
                    title: 'Client Id',
                    dataIndex: 'ClientId',
                    key: 'ClientId'
                }, 
                {
                    title: 'Site Collection TestUrl',
                    dataIndex: 'SiteCollectionTestUrl',
                    key: 'SiteCollectionTestUrl',
                },
                {
                    title: 'Tenant DomainUrl',
                    dataIndex: 'TenantDomainUrl',
                    key: 'TenantDomainUrl',
                }
        ];

        // rowSelection object indicates the need for row selection
        const rowSelection = {
            onChange: (selectedRowKeys, selectedRows) => {
                if(selectedRows[0].key != undefined){
                    console.log(selectedRows[0].key);


                    const options = { 
                        method: 'post', 
                        body: JSON.stringify({ clientid : selectedRows[0].key.toString() }) ,
                        config: {
                            headers: {
                              'Content-Type': 'application/json'
                            }
                          }
                    };

                    adalApiFetch(fetch, "/Tenant/SetTenantActive", options)
                        .then(response =>{
                        if(response.status === 200){
                            Notification(
                                'success',
                                'Tenant set to active',
                                ''
                                );
                        }else{
                            throw "error";
                        }
                        })
                        .catch(error => {
                        Notification(
                            'error',
                            'Tenant not activated',
                            error
                            );
                        console.error(error);
                    });
                }
            },
            getCheckboxProps: record => ({
                type: Radio
            }),
        };

        return (
            <Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
        );
    }
}

export default ListTenants;

and the webapi method 和webapi方法

[HttpPost]
        [Route("api/Tenant/SetTenantActive")]
        public async Task<IHttpActionResult> SetTenantActive([FromBody]string clientid)
        {
            var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
            var allTenants = await tenantStore.Query().Where(x => x.TenantDomainUrl != null).ToListAsync();
            foreach(Tenant ten  in allTenants)
            {
                ten.Active = false;
                await tenantStore.UpdateAsync(ten);
            }

            var tenant = await tenantStore.Query().FirstOrDefaultAsync(x => x.clientid == clientid);
            if (tenant == null)
            {
                return NotFound();
            }

            tenant.Active = true;
            var result = await tenantStore.UpdateAsync(tenant);

            return Ok(result);
        }

Couple of things I noticed. 我注意到了几件事。

  1. You're trying to do a POST request with a JSON body. 您正在尝试使用JSON正文执行POST请求。 On the client, your request looks fine. 在客户端,您的请求看起来很好。

As I understand the POST body is 据我所知,POST机构是

{ clientid: 'some-client-id' }
  1. The interesting thing is in the web API you receive it as 有趣的是在您收到它的Web API中

public async Task<IHttpActionResult> SetTenantActive([FromBody]string clientid)

This is possibly the culprit. 这可能是罪魁祸首。 Your API is expecting a string as a POST body where it is a json object. 您的API期望一个字符串作为POST主体,它是一个json对象。 Have you tried changing the type to dynamic or JObject ? 您是否尝试将类型更改为dynamicJObject

So, essentially, 所以,基本上,

public async Task<IHttpActionResult> SetTenantActive([FromBody]dynamic clientRequest)

OR 要么

public async Task<IHttpActionResult> SetTenantActive([FromBody]JObject clientRequest)

Alternately, 交替,

If you want to continue using your API as is, then you can just change the request you're making from the client to 'some-client-id' instead of { clientid: 'some-client-id' } 如果您想继续按原样使用API​​,那么您只需将您从客户端发出的请求更改为'some-client-id'而不是{ clientid: 'some-client-id' }

Change 更改

const options = { 
    method: 'post', 
    body: JSON.stringify({ clientid : selectedRows[0].key.toString() }) ,
    config: {
        headers: {
            'Content-Type': 'application/json'
        }
    }
};

to

const options = { 
    method: 'post', 
    body: JSON.stringify({ clientid : selectedRows[0].key.toString() }) ,
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    }
};

Check your server settings. 检查您的服务器设置。 By default it should support json but its better to verify it. 默认情况下,它应该支持json但更好地验证它。 Also try to clear Accept header in yor api code and set to * which means all types. 还尝试在yor api代码中清除Accept标头并设置为* ,这意味着所有类型。

Moreover check adalApiFetch method. 另外检查adalApiFetch方法。 What headers it send? 它发送了什么标头? Is the format of Content-Type used & set correctly? Content-Type的格式是否正确使用和设置?

For a simple RESTFul call like that you could follow suggestion naming conventions along with HTTP verbs that better clarifies the intention and simplify the call itself. 对于像这样的简单RESTFul调用,您可以遵循建议命名约定以及HTTP动词,以更好地阐明意图并简化调用本身。 No need to over complicate the API model for such a simple call. 对于这样一个简单的调用,无需过度复杂化API模型。

Something like 就像是

[HttpPut] // Or HttpPost. PUT is usually used to update the resourcce
[Route("api/Tenant/{clientid}/Active")]
public async Task<IHttpActionResult> SetTenantActive(string clientid) {
    var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
    var allTenants = await tenantStore.Query().Where(x => x.TenantDomainUrl != null).ToListAsync();
    var updates = new List<Task>();
    foreach(Tenant ten  in allTenants) {
        ten.Active = false;
        updates.Add(tenantStore.UpdateAsync(ten));
    }

    await Task.WhenAll(updates);

    var tenant = await tenantStore.Query().FirstOrDefaultAsync(x => x.clientid == clientid);
    if (tenant == null)
    {
        return NotFound();
    }

    tenant.Active = true;
    var result = await tenantStore.UpdateAsync(tenant);

    return Ok(result);
}

And on the client 并在客户端

const rowSelection = {
    onChange: (selectedRowKeys, selectedRows) => {
        if(selectedRows[0].key != undefined){
            var clientid = selectedRows[0].key;
            console.log(clientid);

            var url = "/Tenant/" + clientid + "/Active"

            const options = { 
                method: 'put'
            };

            adalApiFetch(fetch, url, options)
                .then(response => {
                if(response.status === 200){
                    Notification(
                        'success',
                        'Tenant set to active',
                        ''
                        );
                }else{
                    throw "error";
                }
                })
                .catch(error => {
                Notification(
                    'error',
                    'Tenant not activated',
                    error
                    );
                console.error(error);
            });
        }
    },
    getCheckboxProps: record => ({
        type: Radio
    }),
};

Why are you using post ? 你为什么用post From a 'REST`y point of view, it is used to create an entity (a tenant in your case). 从“REST”的角度来看,它用于创建实体(在您的案例中为租户)。

The simple request intended can be solved via GET with the clientid as part of the route: 预期的简单请求可以通过GET解决, clientid作为路线的一部分:

[HttpGet]
[Route("api/Tenant/SetTenantActive/{clientid}")]
public async Task<IHttpActionResult> SetTenantActive(string clientid)
{
   // ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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