POST 405(方法不允许)在自定义网站上 api
POST 405 (Method Not Allowed) on custom web api
我有以下反应代码来调用 post 端点,webapi 确实有一个 post 但我仍然不知道为什么我会收到此错误
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 OfficeDevPnP.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 OfficeDevPnP.Core.AuthenticationManager().GetSharePointOnlineAuthenticatedContextTenant(model.SiteCollectionUrl, tenant.Email, keyVaultHelper.SecretValue))
{
try
{
var clientsidepageTemplateStore = CosmosStoreFactory.CreateForEntity<OfficeDevPnP.Core.Pages.ClientSidePage>();
var page = OfficeDevPnP.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 = OfficeDevPnP.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 端点工作完美
如果您想访问 CreatePageTemplate
操作,您必须请求 url 以下格式: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)
但如果您打算使用原始 url,您应该只更新您的操作
[HttpPost]
[Route("CreatePageTemplate")] //remove api/Page as it is already in RoutePrefix
public async Task<IHttpActionResult> CreatePageTemplate([FromBody]PageTemplateCreationModel model)
我有以下反应代码来调用 post 端点,webapi 确实有一个 post 但我仍然不知道为什么我会收到此错误
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 OfficeDevPnP.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 OfficeDevPnP.Core.AuthenticationManager().GetSharePointOnlineAuthenticatedContextTenant(model.SiteCollectionUrl, tenant.Email, keyVaultHelper.SecretValue))
{
try
{
var clientsidepageTemplateStore = CosmosStoreFactory.CreateForEntity<OfficeDevPnP.Core.Pages.ClientSidePage>();
var page = OfficeDevPnP.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 = OfficeDevPnP.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 端点工作完美
如果您想访问 CreatePageTemplate
操作,您必须请求 url 以下格式: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)
但如果您打算使用原始 url,您应该只更新您的操作
[HttpPost]
[Route("CreatePageTemplate")] //remove api/Page as it is already in RoutePrefix
public async Task<IHttpActionResult> CreatePageTemplate([FromBody]PageTemplateCreationModel model)