Attribute routing is enabled, according to the screenshot posted in comments, as the WebApiConfig
has the default configuration
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Attribute routing.
config.MapHttpAttributeRoutes();
// Convention-based routing.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Note the api
prefix on the convention based route.
The request from client is being made to /SiteCollections/CreateModernSite
which wont match the Web API as the API controller appears to not be using the attribute routing and the requested URL does not match the Web API convention based route.
Also on the client side a JSON body is constructed in options while the content type is set to 'multipart/form-data'
If the intention is to POST content in the body, then on the server side you would need to make a few changes to make the API accessible.
[Authorize]
[RoutePrefix("api/SiteCollections")]
public class SiteCollectionsController : ApiController {
// GET api/SiteCollections
[HttpGet]
[Route("")]
public async Task<IHttpActionResult> Get() {
var tenant = await TenantHelper.GetTenantAsync();
using (var cc = new OfficeDevPnP.Core.AuthenticationManager().GetAppOnlyAuthenticatedContext(tenant.TenantAdminUrl, tenant.ClientId, tenant.ClientSecret)) {
var tenantOnline = new Tenant(cc);
SPOSitePropertiesEnumerable siteProps = tenantOnline.GetSitePropertiesFromSharePoint("0", true);
cc.Load(siteProps);
cc.ExecuteQuery();
var sites = siteProps.Select(site =>
new TenantManagementWebApi.Entities.SiteCollection() {
Url = site.Url,
Owner = site.Owner,
Template = site.Template,
Title = site.Title
})
.ToList();
return Ok(sites);
}
}
// POST api/SiteCollections
[HttpPost]
[Route("")]
public async Task<IHttpActionResult> CreateModernSite([FromBody]NewSiteInformation model) {
if(ModelState.IsValid) {
var tenant = await TenantHelper.GetTenantAsync();
using (var context = new OfficeDevPnP.Core.AuthenticationManager().GetAppOnlyAuthenticatedContext(tenant.TenantAdminUrl, tenant.ClientId, tenant.ClientSecret)) {
var teamContext = await context.CreateSiteAsync(
new TeamSiteCollectionCreationInformation {
Alias = model.Alias, // Mandatory
DisplayName = model.DisplayName, // Mandatory
Description = model.Description, // Optional
//Classification = Classification, // Optional
//IsPublic = IsPublic, // Optional, default true
}
);
teamContext.Load(teamContext.Web, _ => _.Url);
teamContext.ExecuteQueryRetry();
//204 with location and content set to created URL
return Created(teamContext.Web.Url, teamContext.Web.Url);
}
}
return BadRequest(ModelState);
}
public class NewSiteInformation {
[Required]
public string Alias { get; set; }
[Required]
public string DisplayName { get; set; }
public string Description { get; set; }
//...
}
}
Note the inclusion of a proper strongly typed object model for the POST action, model validation and the returning of the proper HTTP status code as expected by the client. (204)
On the client side, update the URL being called to match the API controller’s route and update the options to send the correct content type.
//...
const options = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(
{
Alias: this.state.Alias,
DisplayName: this.state.DisplayName,
Description: this.state.Description
})
};
adalApiFetch(fetch, "api/SiteCollections", options)
.then(response =>{
if(response.status === 204){
Notification(
'success',
'Site collection created',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Site collection not created',
error
);
console.error(error);
});
//...
Note how the headers
are directly in the fetch options as apposed to config.headers
in the original code.
I have trouble with an error. Localhost works fine.But when it is in live server and makes a post request its show me 405
status code.
Laravel api route is :
Route::post('/add_mase/', 'Abc@add_mase');
And Laravel Method is
public function add_mase(Request $r){
$w = $this->d();
if($data = DB::table('mess')->where('code', $r->code)->first()){
$boder = $this->boder();
DB::table('add_requ')->insert(['mId' => $data->id, 'bId' => $boder->id, 'dateTime' => date('d-m-Y h:i:s a') ]);
return response( ['msg' => 'You Request Success. Your Mase Name is '.$data->name , 'status' => false]);
}
return response( ['msg' => 'You Request not Success. Try exact Code', 'status' => true ]);
}
Now React Code are
JS6 react Form
import React from 'react';
import { connect } from 'react-redux';
import Header from "../Auth/Header";
import Footer from "../Auth/Footer";
import { withRouter } from 'react-router-dom';
import { checkMass, addMase } from '../../services/actions/BoderAction'
class AddMase extends React.Component {
state = {
code: '',
form: true
}
onChangemorning = (e) => {
this.setState({ code: e.target.value })
}
onSubmit = (e) => {
e.preventDefault();
const obj = {
code: this.state.code,
}
this.props.addMase(obj)
}
componentDidMount() {
this.props.checkMass(JSON.parse(localStorage.getItem('user')).id)
}
render() {
if (this.props.loader == true) {
return <div className="loader"></div>;
}
return (
<>
<Header />
<div className="card o-hidden border-0 shadow-lg my-5">
<div className="card-body p-0">
<div className="row">
<div className="col-lg-12">
<div className="p-5">
<div className="text-center">
{this.props.sucMsg}
{this.props.form == true &&
<form method="POST" onSubmit={this.onSubmit} >
<div className="form-group">
<label htmlFor="exampleFormControlSelect1">Mase Code</label>
<input
type='text'
className="form-control"
onChange={this.onChangemorning}
/>
</div>
<button type="submit" className="btn btn-primary">Request </button>
</form>
}
</div>
</div>
</div>
</div>
</div>
</div>
<Footer />
</>
);
}
}
const mapStateToProps = (state) => {
return {
loader: state.boder.loading,
errMsg: state.boder.errMsg,
sucMsg: state.boder.sucMsg,
form: state.boder.form,
}
}
export default withRouter(connect(mapStateToProps, { checkMass, addMase })(AddMase));
Border Action for redux
export const addMase = (id) => async dispatch => {
dispatch({type: LOADING})
await axios.post(`${URL}/add_mase/`, id, {
headers: {
'Authorization' : localStorage.getItem("token")
}
})
.then( res => {
console.log( res )
dispatch({
type: ADD_MASE,
payload: {
massage : res.data.msg,
status : res.data.status
}
})
})
.catch(err => {
dispatch({
type: FAILED,
payload: err.response.data.error
})
})
}
Boder Reducer
case ADD_MASE:
return { ...state, loading: false, sucMsg: payload.massage, form: payload.status }
those make a error status code 405
Browser console
GET https://example.com/admin/api/add_mase 405
N.B ssl not set to my domain
And Browser Network preview
The GET method is not supported for this route. Supported methods: POST
One more thing, localhost working fine in all routes. But in live server all post request creates 405 error. Get request works fine in live server too.
The following request is giving me a 405 Method not allowed when running from a simple create-react-app generated project from localhost:3000. I really need this app to run outside of the domain (decoupled). This feels very much like a cross origin issue but I’m sending the X-CSRF-TOKEN
header token and have apache configured to accept the headers as well.
No idea what I’m doing wrong…
Host: contenta.loc
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: GET
Origin: http://localhost:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Access-Control-Request-Headers: authorization,x-csrf-token
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
However, running the same code from within a custom module on the same domain executes as expected.
import React, { Component } from 'react';
import axios from 'axios';
import Querystring from 'query-string';
import SubRequests from 'd8-subrequests';
const URL = 'http://contenta.loc';
class App extends Component {
state = {
csrfToken: '',
oauthToken: {}
};
constructor(props){
super(props);
this.initializeCsrfToken();
this.initializeOauthToken();
}
initializeCsrfToken(){
axios.get(URL + '/session/token')
.then(response => {
this.setState({csrfToken: response.data});
})
.catch((error) => {
console.log('error ' + error);
});
}
initializeOauthToken(){
const data = {
grant_type: 'password',
client_id: '77e40506-4b2a-4317-b6c0-5ed5b27ce886',
client_secret: 'test1123',
username: 'test',
password: 'test'
};
axios.post(URL + '/oauth/token', Querystring.stringify(data))
.then(response => {
this.setState({oauthToken: response.data.access_token});
})
.catch((error) => {
console.log('error ' + error);
});
}
fetchSubrequests() {
const subrequests = new SubRequests(URL + '/subrequests?_format=json');
const AuthStr = 'Bearer '.concat(this.state.oauthToken);
subrequests.add({
uri: '/api/categories'
});
subrequests.add({
uri: '/api/tags'
});
subrequests.add({
uri: '/api/menus'
});
axios.get(subrequests.getUrl(), {
headers: {
Authorization: AuthStr,
'X-CSRF-Token': this.state.csrfToken,
}
}).then(dataresponse => {
console.log(dataresponse);
})
}
render = () => {
return (
<button >
<div onClick ={
() => {this.fetchSubrequests()}
} > Fetch Subrequests</div>
</button>
)
}
}
export default App;
UPDATE:
Here’s what my services.yml
cors.config looked like: (THIS IS WRONG???)
# Configure Cross-Site HTTP requests (CORS).
# Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
# for more information about the topic in general.
# Note: By default the configuration is disabled.
cors.config:
enabled: true
# Specify allowed headers, like 'x-allowed-header'.
allowedHeaders: ['x-csrf-token,authorization,content-type,accept,origin,x-requested-with']
# Specify allowed request methods, specify ['*'] to allow all possible ones.
allowedMethods: ['POST, GET, OPTIONS, DELETE, PUT']
# Configure requests allowed from specific origins.
allowedOrigins: ['*']
# Sets the Access-Control-Expose-Headers header.
exposedHeaders: true
# Sets the Access-Control-Max-Age header.
maxAge: false
# Sets the Access-Control-Allow-Credentials header.
supportsCredentials: false
This is what my cors.config needs to be for it to work!!
cors.config:
enabled: true
# Specify allowed headers, like 'x-allowed-header'.
# ['x-csrf-token,authorization,content-type,accept,origin,x-requested-with']
allowedHeaders: ['*']
# - '*'
# Specify allowed request methods, specify ['*'] to allow all possible ones.
# ['POST, GET, OPTIONS, DELETE, PUT']
allowedMethods: ['*']
# - '*'
# Configure requests allowed from specific origins.
allowedOrigins: ['*']
# Sets the Access-Control-Expose-Headers header.
exposedHeaders: true
# Sets the Access-Control-Max-Age header.
maxAge: false
# Sets the Access-Control-Allow-Credentials header.
supportsCredentials: false
Here’s what my apache.conf
looks like:
<VirtualHost *:80>
ServerName loc
DocumentRoot /Users/justinwinter/Sites
VirtualDocumentRoot /Users/justinwinter/Sites/%-2/docroot
UseCanonicalName Off
Header set Access-Control-Allow-Origin "http://localhost:3000"
Header set Access-Control-Allow-Credentials true
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, PATCH, DELETE"
Header always set Access-Control-Allow-Headers: "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization"
<Directory "/Users/justinwinter/Sites/*/docroot">
AllowOverride All
Order allow,deny
Allow from all
Require all granted
</Directory>
AddType application/x-httpd-php .php
</VirtualHost>
NOT WORKING FROM A SEPARATE JS REACT APP
The following screenshot shows the result of running the same JS from within the contenta.loc domain (same server as the endpoint)
WORKING FROM WITHIN A CUSTOM MODULE
The following screenshot shows the error when running the JS from within the create-react-app nodejs localhost server.
I am trying to connect my react-apollo client to my graphql backend and I keep getting a 405 error. My server is hosted on localhost port 3000, and my client is also on localhost (port 8100). I have enabled CORS in express to allow this conenction, but when my client tries to connect, I get the error:
OPTIONS http://localhost:3000/graphql 405 (Method Not Allowed)
Here are the request and response headers copied from my browser’s failed request:
General
Request URL:http://localhost:3000/graphql
Request Method:OPTIONS
Status Code:405 Method Not Allowed
Remote Address:[::1]:3000
Response Headers
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Access-Control-Request-Headers, Access-Control-Request-Method
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:*
Allow:POST
Connection:keep-alive
Date:Sun, 15 Jan 2017 20:21:41 GMT
Transfer-Encoding:chunked
X-Powered-By:Express
Request Headers
Accept:*/*
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:en-US,en;q=0.8,fr;q=0.6
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Connection:keep-alive
DNT:1
Host:localhost:3000
Origin:http://localhost:8100
Referer:http://localhost:8100/
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
And here is my server script file:
var express = require('express');
var bodyParser = require('body-parser');
var { graphqlExpress } = require('graphql-server-express');
var Schema = require('./server/schema.js');
const PORT = 3000;
var app = express();
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Accept-Encoding, Accept-Language, Access-Control-Request-Headers, Access-Control-Request-Method");
next();
});
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: Schema }));
app.listen(PORT);
console.log('GraphQL Sandbox started on port: 3000');
react — returned by the backend 405
the reason: Background interface is a PUT method
I wrote POST
solve:
export async function statusTeacher(params = {}) {
return request(`server/xx/xx/xx?${stringify(params)}`, {
method: 'PUT',
// body: params
});
}
Intelligent Recommendation
Nginx+React Ant Design 405 error
Why is it possible to configure a proxy locally, but not on nginx? Probably because there are proxies made by other servers locally, such as node, so nginx also needs to be configured The package is p…
More Recommendation
405
problem causes @PostMapping way problem solved Use postman conduct post test. Bug reproduce…
A way for React to interact with the backend
Last week, I needed to link a react front-end project with the spring back-end project. However, I have never done this kind of separation system before and after. Although I have learned the basics o…
React backend pagination
Under the api is the secondary package of axios index.js request.js Create a list component: Effect picture PS: Cross-domain issues mentioned The back end is node, port 4000, configuration proxy, fron…