自定义 Web api 上的 POST 405(不允许的方法)

Posted

技术标签:

【中文标题】自定义 Web api 上的 POST 405(不允许的方法)【英文标题】:POST 405 (Method Not Allowed) on custom web api 【发布时间】:2019-07-04 02:39:21 【问题描述】:

我有以下反应代码来调用 post 端点,webapi 确实有一个帖子,但我仍然不知道为什么会出现此错误

import React,  Component  from 'react';
import  Row, Col, Tabs, Menu, Dropdown, Button, Icon, message, Input  from 'antd';

import Form from '../../components/uielements/form';
import PageHeader from '../../components/utility/pageHeader';
import Box from '../../components/utility/box';
import LayoutWrapper from '../../components/utility/layoutWrapper';
import ContentHolder from '../../components/utility/contentHolder';
import basicStyle from '../../settings/basicStyle';
import IntlMessages from '../../components/utility/intlMessages';
import  Cascader  from 'antd';
import  adalApiFetch  from '../../adalConfig';

import Notification from '../../components/notification';

const FormItem = Form.Item;
 class ExtractPageTemplate extends Component 
    constructor(props) 
        super(props);
        this.state = options:[], loading:false, selectedOptions:[], description:'';
        this.loadData = this.loadData.bind(this);
        this.enterLoading = this.enterLoading.bind(this);
        this.onChange = this.onChange.bind(this);
        this.handleChangeDescription= this.handleChangeDescription.bind(this);

    

    handleChangeDescription(event)
        this.setState(description : event.target.value);
    

    enterLoading ()
        this.setState( loading: true );

        const options = 
            method: 'post',
            body: JSON.stringify(
              
                  "SiteCollectionUrl": this.state.selectedOptions[0].value,
                  "PageName": this.state.selectedOptions[1].label, 
                  "Description": this.state.Description
              ),
              headers: 
                  'Content-Type': 'application/json; charset=utf-8'
               
          ;

          adalApiFetch(fetch, "/Page/CreatePageTemplate", options)
            .then(response =>
              if(response.status === 204)
                  Notification(
                      'success',
                      'Page tempate created',
                      ''
                      );
               else
                  throw "error";
               
            )
            .catch(error => 
              Notification(
                  'error',
                  'Page template not created',
                  error
                  );
              console.error(error);
          );

    

    componentDidMount() 
        adalApiFetch(fetch, "/SiteCollection", )
          .then(response => response.json())
          .then(json => 
            console.log(json);
            const firstLevelOptions = json.map(post => (
                value: post.Url,
                label: post.Title,
                isLeaf: false    
            ));

            this.setState(
                options: firstLevelOptions
            );
          );
    

    onChange(value, selectedOptions) 
        console.log("value:", value, "selectedOptions", selectedOptions);
        this.setState(
            selectedOptions: selectedOptions
        );
    

    loadData(selectedOptions)
        console.log("loaddata", selectedOptions);

        const targetOption = selectedOptions[selectedOptions.length - 1];
        targetOption.loading = true;

        const options = 
            method: 'get',
              headers: 
                      'Content-Type': 'application/json; charset=utf-8'
                                  
          ;

        adalApiFetch(fetch, "/Page/"+encodeURIComponent(targetOption.value.replace("https://","")), options)
          .then(response => response.json())
          .then(json => 
            targetOption.loading = false;
            console.log(json);
            const secondLevelOptions = json.map(page => (
                value: page.Id,
                label: page.Name,
                isLeaf: true    
            ));
            targetOption.children = secondLevelOptions;
            this.setState(
                options: [...this.state.options],
            );
            
        );

    

    render()

        console.log("uepa" , this.props);
        const  rowStyle, colStyle, gutter  = basicStyle;
        const TabPane = Tabs.TabPane;
        const  getFieldDecorator  = this.props.form;
        const formItemLayout = 
        labelCol: 
            xs:  span: 24 ,
            sm:  span: 6 ,
        ,
        wrapperCol: 
            xs:  span: 24 ,
            sm:  span: 14 ,
        ,
        ;
        const tailFormItemLayout = 
        wrapperCol: 
            xs: 
            span: 24,
            offset: 0,
            ,
            sm: 
            span: 14,
            offset: 6,
            ,
        ,
        ;

        return (
        <div>
            <LayoutWrapper>
            <PageHeader><IntlMessages id="pageTitles.PageAdministration" /></PageHeader>
            <Row style=rowStyle gutter=gutter justify="start">
            <Col md=12 sm=12 xs=24 style=colStyle>
                <Box
                title=<IntlMessages id="pageTitles.siteCollectionsTitle" />
                subtitle=<IntlMessages id="pageTitles.siteCollectionsTitle" />
                >
                <ContentHolder>
                    <Cascader
                                options=this.state.options
                                loadData=this.loadData
                                onChange=this.onChange
                                changeOnSelect
                    />

                <FormItem ...formItemLayout label="Description " hasFeedback>
                getFieldDecorator('Description', 
                    rules: [
                        
                            required: true,
                            message: 'Please input the page template description',
                        
                    ]
                )(<Input name="Description" id="Description" onChange=this.handleChangeDescription />)
                </FormItem>


                    <Button type="primary" loading=this.state.loading onClick=this.enterLoading>
                            Click me!
                            </Button>


                </ContentHolder>
                </Box>
            </Col>
            </Row>
        </LayoutWrapper>
        </div>
        );
  


const WrappedExtractPageTemplate = Form.create()(ExtractPageTemplate );
export default WrappedExtractPageTemplate;

还有我的 webapi 代码

namespace TenantManagementWebApi.Controllers

    [Authorize]
    [RoutePrefix("api/Page")]
    public class PageController : ApiController
    
        [HttpGet]
        [Route("*sitecollectionUrl")]
        public async Task<List<TenantManagementWebApi.Entities.Page>> Get(string sitecollectionUrl)
        
            var tenant = await TenantHelper.GetActiveTenant();
            var siteCollectionStore = CosmosStoreFactory.CreateForEntity<TenantManagementWebApi.Entities.SiteCollection>();
            await siteCollectionStore.RemoveAsync(x => x.Title != string.Empty); // Removes all the entities that match the criteria
            string domainUrl = tenant.TestSiteCollectionUrl;
            string tenantName = domainUrl.Split('.')[0];
            string tenantAdminUrl = tenantName + "-admin.sharepoint.com";
            KeyVaultHelper keyVaultHelper = new KeyVaultHelper();
            await keyVaultHelper.OnGetAsync(tenant.SecretIdentifier);

            using (var context = new OfficeDe***P.Core.AuthenticationManager().GetSharePointOnlineAuthenticatedContextTenant("https://"+sitecollectionUrl, tenant.Email, keyVaultHelper.SecretValue))
            

                var pagesLibrary = context.Web.GetListByUrl("SitePages") ?? context.Web.GetListByTitle("SitePages");
                CamlQuery query = CamlQuery.CreateAllItemsQuery(100);
                var pages = pagesLibrary.GetItems(query);
                context.Load(pages);
                context.ExecuteQuery();

                List<TenantManagementWebApi.Entities.Page> listOfPages = new List<TenantManagementWebApi.Entities.Page>();
                foreach(ListItem item in pages)
                
                    listOfPages.Add(new TenantManagementWebApi.Entities.Page()  Id = item.Id, Name = item.FieldValues["Title"]+".aspx" );
                
                return listOfPages;

            ;
        


        [HttpPost]
        [Route("api/Page/CreatePageTemplate")]
        public async Task<IHttpActionResult> CreatePageTemplate([FromBody]PageTemplateCreationModel model)
        
            if (ModelState.IsValid)
            
                var tenant = await TenantHelper.GetActiveTenant();
                var siteCollectionStore = CosmosStoreFactory.CreateForEntity<TenantManagementWebApi.Entities.SiteCollection>();
                await siteCollectionStore.RemoveAsync(x => x.Title != string.Empty); // Removes all the entities that match the criteria
                string domainUrl = tenant.TestSiteCollectionUrl;
                string tenantName = domainUrl.Split('.')[0];
                string tenantAdminUrl = tenantName + "-admin.sharepoint.com";

                KeyVaultHelper keyVaultHelper = new KeyVaultHelper();
                await keyVaultHelper.OnGetAsync(tenant.SecretIdentifier);
                using (var context = new OfficeDe***P.Core.AuthenticationManager().GetSharePointOnlineAuthenticatedContextTenant(model.SiteCollectionUrl, tenant.Email, keyVaultHelper.SecretValue))
                
                    try
                    
                        var clientsidepageTemplateStore = CosmosStoreFactory.CreateForEntity<OfficeDe***P.Core.Pages.ClientSidePage>();
                        var page = OfficeDe***P.Core.Pages.ClientSidePage.Load(context, model.PageName);
                        if (!ModelState.IsValid)
                        
                            return BadRequest(ModelState);
                        

                        var added = await clientsidepageTemplateStore.AddAsync(page);
                        return StatusCode(HttpStatusCode.NoContent);


                        //PageTemplate pageTemplate = new PageTemplate();
                        //pageTemplate.Name = model.Name;
                        // var page = OfficeDe***P.Core.Pages.ClientSidePage.Load(context, "Home.aspx");
                        //int sectionOrder = 0;
                        //foreach (var section in page.Sections)
                        //
                        //    pageTemplate.Sections.Add(section);

                        //    foreach (var column in section.Columns)
                        //    
                        //        foreach(var webpart in column.Controls)
                        //        

                        //        
                        //    

                        //    sectionOrder++;
                        //
                    
                    catch (System.Exception ex)
                    
                        throw ex;
                    
                
            
            return BadRequest(ModelState);
        
    


get 端点完美运行

【问题讨论】:

'返回状态码(HttpStatusCode.NoContent);'可以写成'return NoContent();'。您是否尝试过使用 postman 之类的东西来访问 webapi?可能会告诉您这是 API 问题还是前端问题 每当你看到你的Route 属性复制RoutePrefix 属性(在api/Page 中),你必须知道你做错了。跨度> 我不懂卡米洛? 【参考方案1】:

如果您想访问CreatePageTemplate 操作,您必须请求以下格式的网址:RoutePrefix + Route。在您的情况下,结果 url 应该是 api/Page + api/Page/CreatePageTemplate = api/Page/api/Page/CreatePageTemplate。但显然这段代码

adalApiFetch(fetch, "/Page/CreatePageTemplate", options)

请求api/Page/CreatePageTemplate。所以只需将其更新为以下内容

adalApiFetch(fetch, "/Page/api/Page/CreatePageTemplate", options)

但如果您打算使用原始网址,您应该只更新您的操作

[HttpPost]
[Route("CreatePageTemplate")] //remove api/Page as it is already in RoutePrefix
public async Task<IHttpActionResult> CreatePageTemplate([FromBody]PageTemplateCreationModel model)

【讨论】:

以上是关于自定义 Web api 上的 POST 405(不允许的方法)的主要内容,如果未能解决你的问题,请参考以下文章

从 POSTMAN 到我的 java api 的 POST 上的 405 响应代码

ASP.NET Web API、405 状态码和异常处理

$http.post 的 Web API 405 错误

Web Api GET 动词 405 不允许

JQuery Ajax POST 到 Web API 返回 405 Method Not Allowed

Spring Cloud Netflix Feign - 不支持错误 405 请求方法“POST”