I want to update part of data through an API. I've successfully made a POST data. I would also like to do PATCH to update one field of the data.
Below is the code am trying to use for the changes:
else if (out_timing.worker_timeout_id === emp_id && out_timing.check_status === false) {
let checkOutTime = new TimeOutModel(out_timing.date, emp_id, out_timing.time_out, true);
//introduced two lines below to carry out update for last field
let updateCheckStatus = new TimeInModel("", "", "", false);
this.employeesService.patchTimeinStatus(updateCheckStatus)
this.employeesService.pushTimeOut(checkOutTime)
.subscribe(checkOutTime => {
},
error => this.errorMessage = <any>error);
console.log("Successfully checked-out!");
break;
}
else {
console.log("All conditions exhausted!");
break;
}
This is what I have in my service component.
patchTimeinStatus(timing: TimeInModel): Observable<TimeInModel> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.patch(this.empApiUrl + '/time-in/', timing, options)
.map(this.extractData)
.catch(this.handleErrorObservable);
}
I get 405 method not allowed from the server. Apart from that, I doubt whether what am doing is correct. What is the correct approach to do the update using patch or put methods?
Related
I have got the message body. Now I want to update it according to my needs. I am using this login/code. but it says 400 error. I think issue is in body parameter of the request. Would you please help me there?
var token = localStorage.getItem("accessToken");
var messageId = "18514426e2b99017";
async function updateMessageBody() {
var updatedBody = "Hello,\n\nThis is the UPDATED message body.\n\nBest regards,\nJohn";
const API_KEY = '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-CXIk';
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
};
const response = await fetch(`https://gmail.googleapis.com/gmail/v1/users/me/messages/18514426e2b99017/modify?key=['API_KEY']`, {
method: 'POST',
headers: headers,
body: JSON.stringify({
raw: window.btoa(unescape(encodeURIComponent(updatedBody)))
})
});
if (!response.ok) {
// throw new Error(`Request failed with status code ${response.status}`);
}
return await response.json();
}
updateMessageBody()
.then(response => {
console.log('Message body updated successfully:', response);
})
.catch(error => {
});
Checking the documentation, it states that a message body can't be altered once it has been created, meaning that once you have already created an email this message can't be changed. You can verify this here.
You can instead update a message draft which is possibly what you are trying to do, however using the endpoint you have in your code this won't be possible and will lead to the error message you are getting, try using instead the users.draft.update method that allows you to modify the content of the draft sitting in your mailbox. Please note as well that using the method users.messages does not have any update method as they only have the modify one's, those methods can only update the labels though so please be aware of that.
I've built an API using C# that uses JWT tokens for authorization. On the frontend I store these tokens in local storage and get them, when creating a request. When creating GET or DELETE requests, everything works fine, and using console.log() I can see that fetch options have the Authorization header added. However when using POST or PATCH methods, the Authorization header is missing immediatly after adding it to the object. Here is my request method:
const send = async (apiOptions: ApiParams): Promise<FetchReturn> => {
const accessToken = GetAccessToken()
const options: ApiOptions = {
method: apiOptions.method,
headers: {
Authorization: `Bearer ${accessToken}`
}
}
console.log(options)
if (apiOptions.data) {
options.headers = {
'Content-Type': 'application/json'
}
options.body = JSON.stringify(apiOptions.data)
}
const result = await fetch(`${getUrl()}/${apiOptions.path}`, options).then(res => res).catch(err => err)
if (!result.ok) {
if (IsExpired()) {
const refreshResult = await fetch(`${getUrl()}/api/user/refresh`, {method: 'POST', headers:{
'Content-Type': 'application/json'
}, body: JSON.stringify(GetRefreshRequest())}).then(res => res).catch(err => err)
if (refreshResult.ok) {
Login(JSON.parse(await refreshResult.text()))
return await send(apiOptions)
} else if (refreshResult.status === 401) {
Logout()
window.location.reload()
return { code: 0, text: ""}
}
}
}
const text = await result.text()
return { code: result.status, text: text }
}
I suppose that in apiParams for POST you have property 'data' assigned, and later you have if-condition that completely replaces request headers object.
Change it to:
options.headers['Content-Type'] = 'application/json';
To keep authorization in headers
The first time check your apiOptions.data
i think , its null when you call POST/Patch request
Just put console.log("...") In the if statement , Then try for resolve your Error
If your problem not resolved, put a replay under my post
I am trying to return an altered header if the token a user sends up is expired so that I can resend up my refresh token if it is expired.
I am using .NET Core 2.2 with "In-Process" hosting incase that matters.
Here is my ConfigureServices method from my Startup.cs.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "bearer";
options.DefaultChallengeScheme = "bearer";
}).AddJwtBearer("bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["serverSigningPassword"])),
ValidateLifetime = true,
ClockSkew = System.TimeSpan.Zero //the default for this setting is 5 minutes
};
options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return System.Threading.Tasks.Task.CompletedTask;
}
};
});
And then when I try to fetch on an "authorize" endpoint from javascript with the following.
async function fetchWithCredentials(url, options) {
options.headers['Authorization'] = 'Bearer ' + jwtToken;
var response = await fetch(url, options);
if (response.ok) { //all is good, return the response
return response;
}
console.log(response.headers) //nothing in this array
// it will never do this "if" statement because there are no headers
if (response.status === 401 && response.headers.has('Token-Expired')) {
// refresh the token
return await fetchWithCredentials(url, options); //repeat the original request
} else { //status is not 401 and/or there's no Token-Expired header
return response;
}
}
This image is from hovering over the header. It certainly hits my breakpoint (for the context.Response.Headers.Add() and I can see the count = 1 (which is the "Token-Expired" when I examine it).
Finally, here is a screenshot from Postman after a failed request so the response is sending, but not being received in my JS.
Any ideas as to why my header is not sticking to my response in the javascript?
There is a restriction to access response headers when you are using Fetch API over CORS. Due to this restriction, you can access only following standard headers:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
Reference : https://stackoverflow.com/a/44816592/5751404
So one way to access your custom header in client is to add the header access-control-expose-headers to response, with the comma-separated headers:
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Token-Expired"); ;
}));
In Configure:
app.UseCors("MyPolicy");
So that you can access the custom header from client using Fetch API over CORS .
Here I describe for both -
1. Token expire and get refresh token
2. Only for unauthorized request.
async function fetchWithCredentials(url, options) {
options.headers['Authorization'] = 'Bearer ' + jwtToken;
var response = await fetch(url, options);//this is a function for get a response. I didn't explain it here. Hope you understand.
if (response.ok) {
return response;
}
let flag:boolean=false; //set flag for executing one if statement at a time.
if (response.status == 401 && response.headers.has('Token-Expired')) {
// refresh the token
flag=true; //set flag true.
//write something as per your requirement.
}
if (response.status == 401 && flag==false) {
**// Only for unauthorized request. You can use this for your problem.**
//write something as per your requirement.
}
}
And most important thing is, You have to use below code in startup.cs.
services.AddCors(context => context.AddPolicy("CustomPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Token-Expired"); ;
}));
In Configure:
app.UseCors("CustomPolicy");
and use below code as it is.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "bearer";
options.DefaultChallengeScheme = "bearer";
}).AddJwtBearer("bearer", options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(Configuration["serverSigningPassword"])),
ValidateLifetime = true,
ClockSkew = System.TimeSpan.Zero //the default for this setting is 5 minutes
};
options.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return System.Threading.Tasks.Task.CompletedTask;
}
};
});
Now, you'll get response on client side.
Hope you'll find your solution. Please let me know for any doubt.
Importand node
Was a problem within the back end, not angular, requests are correct.
I like to upload different files at once in my angular5 app. To do this I use rsjx forkJoin. I append the request in an array as you can see in the following code.
After adding the request to the array, they are all the same requests, meaning that I upload one file multiple times instead of the different selected files. Before I add them to the array, you can see in the screenshot, it's fine, different request are created by the service. I know this is some kind of pointer problem, but I have no idea how to fix this, I tried to make a deep copy with Object.assign({}, request), but doesn't work for the Observable.
Console log ~ ~ (ignore the error, the server refuses always the second request because of some unique constraint requirement - it is correct): ~ ~
The component code:
uploadFiles() {
var requests = [];
var ctrNoFailuer = this.filesReplace.length;
for (var i = 0; i < this.filesReplace.length; i++) {
let request = this.solutionFileService.update(this.filesReplace[i][0], this.filesReplace[i][1]);
console.log(request);
requests.push(request);
}
if (requests.length == 0) {
this.runTests();
} else {
forkJoin(requests).subscribe(
res => {
// [...]
}
)
}
}
The service looks like this:
update(solutionFile: SolutionFile, file?: File): Observable<SolutionFile> {
console.log(solutionFile);
let url = `${this.url}/src_uploads/${solutionFile.id}/`;
if (file) {
let headers = new HttpHeaders({
'enctype': 'multipart/form-data',
'Authorization': `JWT ${this.authService.getToken()}`
})
const formData: FormData = new FormData();
formData.append('student_solution', String(solutionFile.student_solution));
formData.append('file_src', file, file.name);
return this.httpClient.put<SolutionFile>(url, formData, { headers: headers })
.pipe(
catchError(this.handleError('UPDATE solution-file', new SolutionFile()))
);
}
return this.httpClient.put<SolutionFile>(url, solutionFile, { headers: this.headers })
.pipe(
catchError(this.handleError('UPDATE solution-file', new SolutionFile())
)
);
}
Your code seems correct, although you can make some improvements (factorisation, syntax ...), such as this :
uploadFiles() {
const requests = this.filesReplace.map(fr => this.solutionFileService.update(fr[0], fr[1]));
if (!requests.length) {
this.runTests();
} else {
forkJoin(requests).subscribe(
res => {
// [...]
}
)
}
}
update(solutionFile: SolutionFile, file ?: File): Observable < SolutionFile > {
let url = `${this.url}/src_uploads/${solutionFile.id}/`;
const headers = file ?
new HttpHeaders({
'enctype': 'multipart/form-data',
'Authorization': `JWT ${this.authService.getToken()}`
}) : this.headers;
const formData: FormData = new FormData();
formData.append('student_solution', String(solutionFile.student_solution));
formData.append('file_src', file, file && file.name || undefined);
const payload = file ? formData : solutionFile;
return this.httpClient.put<SolutionFile>(url, payload, { headers })
.pipe(
catchError(() => this.handleError('UPDATE solution-file', new SolutionFile()))
);
}
Also, please post your logs as text, because not everyone has access to imgur. Did you try to log the result of the forkJoin call ?
I haven't fully understood the question, but as I understood the forkJoin is not working for you, because you trying to pass array of observables. To overcome that issue, try to apply array of observables:
forkJoin.apply(this, requests).subscribe(
Hope this helps
I am working on my first Angular app, but am having a problem going an http.put call. Here is the function I call:
updateUser(columns, values) : Observable<boolean> | boolean {
const headers: Headers = new Headers(); // Need to set content type
headers.append('Content-Type', 'application/json; charset=utf-8');
headers.append('Authorization', `Bearer ${this.authenticationService.token}`);
const options = new RequestOptions({ headers: headers });console.log('test service');
return this.http.put(`${API_URL}users/${this.authenticationService.userId}`, JSON.stringify({ columns: columns, values: values }) , options)
.map((response: Response) => {
console.log('test service1');return Observable.of(true);
})
.catch(e => {
console.log('test service2');return Observable.of(false);
});
When I call the function test service prints to the console, but test service1 and test service2 never print out. I checked my express backend and chrome dev tools and the app is never making the put call to the backend. There are no errors in the console either. So I am missing something, but can't figure it out.
Thank you for any help
Edit: I'm wondering if the issue is because I am just calling this function in another function:
saveColumns(){
this.userService.updateUser('home_columns',this.columns_show);
localStorage.setItem('columns_show', JSON.stringify(this.columns_show) );
}
for http.get functions, I typically do something like this:
loadStudents(page: number, grade = []) {
if (grade.length != 0){
this.student_query_filter = { key:'grade_level',value:grade.join('||') };
} else {
this.student_query_filter = {};
}
this.studentService.getStudentsCount([{ key: 'last_name', value: this.student_search_filter },this.student_query_filter])
.subscribe(
total => this.total = total, //Assign returned student count to local property
err => { console.log(err); });
}
You want to pass the data as an object instead of with JSON.stringify.
You want to return the result from map, not another Observable. If you did want to return a different observable you should change map to switchMap.
The signature should be Observable<boolean> as that is what you are returning.
Be sure to check the developer console in your browser to see if the request is being sent and what the response is. It might be something simple like putting together the URL incorrectly (missing a / for example)
updateUser(columns, values) : Observable<boolean> {
const headers: Headers = new Headers(); // Need to set content type
headers.append('Content-Type', 'application/json; charset=utf-8');
headers.append('Authorization', `Bearer ${this.authenticationService.token}`);
const options = new RequestOptions({ headers: headers });
console.log('test service, sending to: ' + `${API_URL}users/${this.authenticationService.userId}`);
return this.http.put(`${API_URL}users/${this.authenticationService.userId}`, { columns: columns, values: values }, options)
.map((response: Response) => {
console.log('test service1');
return true;
})
.catch(e => {
console.log('test service2');
return false;
});
}
Edit
If your caller is not going to do anything with the result and you do not care what that result is then do not return an Observable at all. Change the return signature to void and execute a subscribe after the call to log the result.
this.http.put(`${API_URL}users/${this.authenticationService.userId}`, { columns: columns, values: values }, options)
.subscribe((response: Response) => {
console.log('test service1'); }
, e => {
console.log('test service2');
});