I am trying to access the Shopify backend to update a customer's email, phone, and name via Ajax request.
I'm receiving a 404 error. while in postman and in google chrome the values are returned correctly and in postman, I am able to modify the values with PUT type.
my code:
let custFN = document.getElementById("customerFirstName").getAttribute("value") ;
let custLN = document.getElementById("customerLarstName").getAttribute("value") ;
let custEM = document.getElementById("customerEmail").getAttribute("value") ;
let custPH = document.getElementById("customerPhone").getAttribute("value") ;
let custID = document.getElementById("customerId").getAttribute("value") ;
let customerdata = {
"customer": {
"id": custID,
"first_name": custFN ,
"last_name": custLN,
"email": custEM,
"phone": custPH,
}
};
var customer_data_json = JSON.stringify(customerdata);
jQuery.cookie("session", null);
jQuery.ajax({
url:'https://{api key}:{api password}#{mysotre}.myshopify.com/admin/api/2022-04/customers/{customer_id}}.json',
type: 'PUT',
cache: false,
data: customer_data_json,
crossDomain: true,
dataType: "JSONP",
contentType: "application/json; charset=utf-8",
success: function(response) {
console.log(response);
},
error: function(response) {
console.log("-------------------------------- <ERROR> --------------------------------");
console.log(response);
console.log("-------------------------------- </ERROR> --------------------------------");
}
});
I placed the values noted with {} above.
I have changed to JSONP from JSON to avoid and fix the CORS policy error.
keep in mind that the response readyState is 4.
P.S: As mentioned by Shopify documentation, I created a private app, and did all the steps required to CRUD using API in Shopify are done.
I'm using axios to send http requests ( i used fetch also but it gives the same result ).
axios.post("http://localhost:3000/login",
{
answer: 42
},
{
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
In my go file I'm logging the response
func post(req *http.Request, res http.ResponseWriter) {
req.ParseForm()
fmt.Println(req.Form)
}
The log is as follows :
map[{"answer":42}:[]]
However i want it to be as follows :
map["answer":[42]]
(I get such when i use postman)
What is the issue with this.
Outgoing data for reference
UPDATE
I used request ( built-in with nodejs) and also with jQuery ajax. Both of them work well.
Its just with axios and fetch which is not working
Here is the code :
request
The following code using nodejs request
var request = require("request");
var options = { method: 'POST',
url: 'http://localhost:3000/login',
headers:
{
'cache-control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded' },
form: { answer: '42' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
jQuery ajax
The following is my jQuery code
var settings = {
"async": true,
"crossDomain": true,
"url": "http://localhost:3000/login",
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"cache-control": "no-cache",
},
"data": {
"answer": "42"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
However, I am still unable to get axios and fetch to work. If someone finds it please update the answer
You need something like this:
var querystring = require('querystring');
axios.post('http://localhost:3000/login', querystring.stringify({'answer': 42},headers: {
'Content-Type': 'application/x-www-form-urlencoded'
});
You can set query string parameters using the params config option,
It will definitely works:
axios.post("http://localhost:3000/login", "", {
params: {answer: 42},
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
To find out more please read this https://github.com/axios/axios/issues/350#issuecomment-227270046
I'm developing an netcore MVC application which uses Ajax requests to POST data to the server. I am using IdentityServer4 as my auth middleware. The flow is when the application is launched with a URL of http://localhost:6002 it redirect to IdentityServer (localhost:6000). The user logs in and is redirected to the main application which then works fine.
Ajax GET requests also work correctly. I can observe a list of claims on the Get action in the controller (User.Identity.Claims). However when I try a POST data from the server the request returns a 200 but from the Identity Server with Redirect=true
My call from the Javascript
applyUpdate(modelData) {
let that = this;
fetch("http://localhost:6002/Client/Update/", {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(modelData)
}
).then(function (response) {
return response;
}).then(function (outData) {
alert("saved");
});
}
The response I receive is
{type: "cors",
url: "http://localhost:6000/account/login?returnUrl=%2Fc…%26x-client..,
redirected: true,
status: 200,
ok: true}
I have enabled CORS on the applications as previously I was getting 405 issues. What appears to be happening is when I call my controller action from Javascript a redirect is being performed to IdentityServer which is then returning to the client without ever actually executing my action.
My controller action looks like
[Authorize]
[HttpPost]
public async Task<JsonResult> Update([FromBody] MyVM myVM)
{
}
If I remove the [Authorize] attribute the method is reached however the value of User.Identity.Claims is always empty where in a HTTP Get it contains a list of all my claims.
Below is the relevant section for configuring IdentityServer from the Startup.cs file
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
};
options.ClientId = "my client";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.UseTokenLifetime = false;
});
I am absolutely stumped at this behavior, any help would be greatly appreciated
UPDATE
Bizarrely I am using the Javascript Fetch API to do the POST, when I swap it out of use Jquery Ajax it works perfectly so many it's the Fetch API that isn't managing the Redirect. This call works fine
$.ajax({
type: "POST",
contentType: "application/json",
dataType: "json",
url: 'http://localhost:6002/Client/Update/',
data: JSON.stringify(modelData),
success: function success(msg) {
alert("saved");
},
error: function error(xhr, data, err) {
console.log(xhr);
}
});
I didn't want to have to include a dependency Jquery
I'd like to make a http request from my cloudcode that gets called on my clientside.
I found this a bit confusing at first so hopefully this helps.
In your Cloud Code main.js
Parse.Cloud.define("POSTfromCloud", function(request, response) {
//runs when Parse.Cloud.run("POSTfromCloud") on the client side is called
Parse.Cloud.httpRequest({
method: "POST",
headers: {
"X-Parse-Application-Id": "[PARSE_APP_ID]",
"X-Parse-REST-API-Key": "[PARSE_REST_ID]",
"Content-Type": "application/json"
},
//adds a new class to my parse data
url: "https://api.parse.com/1/classes/newPOSTfromCloudClass/",
body: {
"newPOSTfromCloudClass": {"key1":"value1","key2":"value2"}
},
success: function (httpResponse) {
console.log(httpResponse.text);
response.success(httpResponse);
},
error:function (httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
response.error(httpResponse.status);
}
}); //end of Parse.Cloud.httpRequest()
});
On your client side. This can be placed anywhere in any language, just use the Parse.Cloud.run to call the matching Parse.Cloud.define you placed in the cloud. You use the
Parse.Cloud.run('POSTfromCloud', {}, {
success: function(result) {
console.log("Posted a new Parse Class from Cloud Code Successfully! :"+ JSON.stringify(result))
},
error: function(error) {
console.log("Oops! Couldn't POST from Cloud Code successfully.. :"+ error)
}
});
}
Your Result: Assuming your POSTing
(here if you want to delete this new object your url would append the object id like so /newPOSTfromCloudClass/60j1uyaBt )
Know it doesnt have to be a httpRequst cloud function. You can do "anything" in the define and run functions.
NOTE: Also seen my other related question on passing params in this here
I've been working through a simple API example, a modified version of the ServiceStack Hello World example with authentication. The goal of the proof of concept is to create an a RESTful API that contains services requiring authentication accessible entirely through Ajax from several different web projects.
I've read the wiki for, and implemented, Authentication and authorization and implementing CORS (many, results [sorry, not enough cred to point to the relevant link]). At this point, my Hello service can authenticate using a custom authentication mechanism which is over-riding CredentialsAuthProvider and a custom user session object. I've created, or borrowed, rather, a simple test application (an entirely separate project to simulate our needs) and can authenticate and then call into the Hello service, passing a name, and receive a 'Hello Fred' response through a single browser session. That is, I can call the /auth/credentials path in the url, passing the username and id, and receive a proper response. I can then update the url to /hello/fred and receive a valid response.
My breakdown in understanding is how to implement the authentication for all ajax calls. My initial login, below, works fine. No matter what I do, my attempt to call the authenticated service via ajax, I either receive a OPTIONS 404 error or Not Found error, or Origin http // localhost:12345 (pseudo-link) is not allowed by Access-Control-Allow-Origin, etc.
Do I need to go this route?
Sorry if this is confusing. I can provide greater details if required, but think this might be sufficient help the knowledgeable to help my lack of understanding.
function InvokeLogin() {
var Basic = new Object();
Basic.UserName = "MyUser";
Basic.password = "MyPass";
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify(Basic),
url: "http://localhost:58795/auth/credentials",
success: function (data, textStatus, jqXHR) {
alert('Authenticated! Now you can run Hello Service.');
},
error: function(xhr, textStatus, errorThrown) {
var data = $.parseJSON(xhr.responseText);
if (data === null)
alert(textStatus + " HttpCode:" + xhr.status);
else
alert("ERROR: " + data.ResponseStatus.Message + (data.ResponseStatus.StackTrace ? " \r\n Stack:" + data.ResponseStatus.StackTrace : ""));
}
});
}
EDIT:
Based on the responses and the link provided by Stefan, I've made a couple of changes:
My Config (Note: I'm using custom authentication and session object and that is all working correctly.)
public override void Configure(Funq.Container container)
{
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(),
}));
base.SetConfig(new EndpointHostConfig
{
GlobalResponseHeaders = {
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" },
{ "Access-Control-Allow-Headers", "Content-Type, Authorization" },
},
DefaultContentType = "application/json"
});
Plugins.Add(new CorsFeature());
this.RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
//Handles Request and closes Responses after emitting global HTTP Headers
if (httpReq.HttpMethod == "OPTIONS")
httpRes.EndRequest(); // extension method
});
Routes
.Add<Hello>("/Hello", "GET, OPTIONS");
container.Register<ICacheClient>(new MemoryCacheClient());
var userRep = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRep);
}
My Simple Hello Service
[EnableCors]
public class HelloService : IService
{
[Authenticate]
public object GET(Hello request)
{
Looks strange when the name is null so we replace with a generic name.
var name = request.Name ?? "John Doe";
return new HelloResponse { Result = "Hello, " + name };
}
}
After making the login call, above, my subsequent call the Hello service is now yielding a 401 error, which is progress, though not where I need to be. (The Jquery.support.cors= true is set in my script file.)
function helloService() {
$.ajax({
type: "GET",
contentType: "application/json",
dataType: "json",
url: "http://localhost:58795/hello",
success: function (data, textStatus, jqXHR) {
alert(data.Result);
},
error: function (xhr, textStatus, errorThrown) {
var data = $.parseJSON(xhr.responseText);
if (data === null)
alert(textStatus + " HttpCode:" + xhr.status);
else
alert("ERROR: " + data.ResponseStatus.Message +
(data.ResponseStatus.StackTrace ? " \r\n Stack:" + data.ResponseStatus.StackTrace : ""));
}
});
}
Again, this works in the RESTConsole if I first make the call to /auth/credentials properly and then follow that up with a call to /hello.
FINAL EDIT
Following Stefan's advise, below, including many other links, I was finally able to get this working. In addition to Stefan's code, I had to make one additional modification:
Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization"));
On to the next challenge: Updating Jonas Eriksson's CustomAuthenticateAttibute code (which appears to be using an older version of ServiceStack as a couple of functions are no longer available.
THANKS AGAIN STEFAN!!
this code works for me, based on the Wiki documentation Custom authentication and authorization
Code is based also in the blog post from Community Resources
CORS BasicAuth on ServiceStack with custom authentication
For Basic Authentication, a custom provider
public class myAuthProvider : BasicAuthProvider
{
public myAuthProvider() : base() { }
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
//Add here your custom auth logic (database calls etc)
//Return true if credentials are valid, otherwise false
if (userName == "admin" && password == "test")
return true;
else
return false;
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
//Fill the IAuthSession with data which you want to retrieve in the app
// the base AuthUserSession properties e.g
session.FirstName = "It's me";
//...
// derived CustomUserSession properties e.g
if(session is CustomUserSession)
((CustomUserSession) session).MyData = "It's me";
//...
//Important: You need to save the session!
authService.SaveSession(session, SessionExpiry);
}
}
public class CustomUserSession : AuthUserSession
{
public string MyData { get; set; }
}
In AppHost
using System.Web;
using ServiceStack; // v.3.9.60 httpExtensions methods, before in ServiceStack.WebHost.Endpoints.Extensions;
using ....
AppHost.Configure
public override void Configure(Container container)
{
SetConfig(new ServiceStack.WebHost.Endpoints.EndpointHostConfig
{
DefaultContentType = ContentType.Json
..
// remove GlobalResponseHeaders because CordFeature adds the CORS headers to Config.GlobalResponseHeaders
});
Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization")); //Registers global CORS Headers
this.RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
if (httpReq.HttpMethod == "OPTIONS")
httpRes.EndRequestWithNoContent(); // v 3.9.60 httpExtensions method before httpRes.EndServiceStackRequest();
});
//Register all Authentication methods you want to enable for this web app.
Plugins.Add(new AuthFeature(() => new CustomUserSession(), // OR the AuthUserSession
new IAuthProvider[] {
new myAuthProvider(),
}) { HtmlRedirect = null }); // Redirect on fail
HtmlRedirect answer
Routes.Add<TestRequest>("/TestAPI/{Id}", "POST,GET, OPTIONS");
....
}
In Service
[Authenticate]
public class TestAPI : Service
{
...
}
in javascript
jQuery.support.cors = true;
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
Login first
function Authenticate() {
$.ajax({
type: 'Post',
contentType: 'application/json',
url: serverIP + 'Auth',
cache: false,
async: false,
data: {},
dataType: "json",
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", make_base_auth(username, password));
},
success: function (response, status, xhr) {
localStorage.sessionId = data.SessionId;
var UserName = response.userName;
},
error: function (xhr, err) {
alert(err);
}
});
}
and request
function DoTest() {
var TestRequest = new Object();
TestRequest.name = "Harry Potter";
TestRequest.Id = 33;
var username = "admin";
var password = "test";
$.ajax({
type: 'Post',
contentType: 'application/json',
cache: false,
async: false,
url: serverIP + '/TestAPI/'+ TestRequest.Id,
data: JSON.stringify(TestRequest),
dataType: "json",
beforeSend: function (xhr) {
xhr.setRequestHeader("Session-Id", localStorage.sessionId);
},
success: function (response, status, xhr) {
var s= response.message;
},
error: function (xhr, err) {
alert(xhr.statusText);
}
});
}
these questions here and here are helpful.
Also this answer for CredentialsAuthProvider, in case we can use cookies and sessions.