I am maintaining someone else's web app (manual JavaScript files and MVC using swagger-js to connect to a .NET API).
The one POST route is returning 415 (Unsupported Media Type), but all the other GET routes work ok. I've had a look at the existing questions on this topic and they refer to incorrect specs or other issues which don't apply to me... I think.
You can quite clearly see from the Request Headers that nothing is posted in the body. But I don't know why:
POST /api/Trip/search HTTP/1.1
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 0
Host: localhost:44393
Origin: https://localhost:44316
Referer: https://localhost:44316/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
accept: application/json
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
The Swagger spec is generated from dotnet core 3.1 using this route:
[Route("search")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<TripInformationSearchResponse>> TripInformationSearch(TripInformationSearchRequest request)
{
...
}
Part of the generated spec is:
"/api/Trip/search": {
"post": {
"tags": [
"Trip"
],
"operationId": "TripInformationSearch",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Cust.API.Models.Trip.TripInformationSearchRequest"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/Cust.API.Models.Trip.TripInformationSearchRequest"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/Cust.API.Models.Trip.TripInformationSearchRequest"
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Cust.API.Models.Trip.TripInformationSearchResponse"
}
}
}
},
"400": {
"description": "Bad Request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Microsoft.AspNetCore.Mvc.ProblemDetails"
}
}
}
}
}
}
}
The webapp client uses swagger-js from JavaScript (not exactly how I would do it but I'm not about to rewrite it just now) to call the search_trips function which calls the above API route:
CUSTAPI = {
swagger_client: function (urlBase) {
var specUrl = urlBase + '/swagger/v1/swagger.json';
SwaggerClient.http.withCredentials = true; // this activates CORS, if necessary
var swaggerClient = new SwaggerClient(specUrl);
return swaggerClient;
},
// ...and later
search_trips: function (keywords, tripNumbersArr, status, routeType, hasGLPostDate, successCallback) {
var failedSwaggerLoadSpecCallback = function (reason) { CUSTAPI.error_display("Unable to connect to the API: " + reason); };
var failedApiRequestCallback = function (reason) { CUSTAPI.error_display("Unable to fulfill the API request: " + reason); };
CUSTAPI.swagger_client(oldApiClient_UrlBase)
.then(
function (swaggerClient) {
var searchRequest = {
keywords: keywords,
tripNumbers: tripNumbersArr, //array
status: status,
routeType: routeType,
hasGLPostDate: hasGLPostDate
};
return swaggerClient.apis.Trip.TripInformationSearch(searchRequest); // chaining promises
}, failedSwaggerLoadSpecCallback)
.then(function (response) {
if (response.ok)
successCallback(response.obj);
//else
// return response
}, failedApiRequestCallback);
},
...
}
And from there no amount of debugging can show me why nothing is put in the body. Can you see why?
Related
I am new to Deno and have the following simple code...
const getCOP = async()=>{
const resp = await fetch("....", {
headers: {
accept: "application/json",
apiKey
},
});
return await resp.body;
}
let resp = {}
return new Response(resp.body, {
status: resp.status,
headers: {
"content-type": "application/json",
},
});
resp.body = await getCOP();
resp.status = 200;
It returns
{
"success": true,
"timestamp": 1675621083,
"base": "COP",
"date": "2023-02-05",
"rates": {
"EUR": 0.000199,
"GBP": 0.000179,
"USD": 0.000216
}
}
What I would like to do would be the equivalent of this in normal JS...
return {
rate : resp.body.rates,
copPerDollar : 1 / resp.body.rates.USD
}
Of course this doesn't seem to work because instead of an actual json obj it is a readable stream. How do I transform this stream into a json object and then restream it to the body of the sent request?
All my routes are working perfectly except CreateGoal. Whenever I add data using the Post method it is giving me the message method not allowed.When I checked the Get method Headers Content-type, it is application/json but when I checked the Post method Headers Content-type, it is text/plain; charset=utf-8. So I think there must be a problem with Content-type. I am not understanding how to solve this problem. I have attached the screenshots for reference.
Screenshots:
Routes:
func Setup(app *fiber.App) {
app.Get("/goals", controllers.GetGoals)
app.Get("/goals/:id", controllers.GetGoal)
app.Post("/goals/add", controllers.CreateGoal)
app.Put("/goals/:id", controllers.UpdateGoal)
app.Delete("/goals/:id", controllers.DeleteGoal)
}
Controllers:
import (
"strconv"
"github.com/gofiber/fiber/v2"
)
type Goal struct {
Id int `json:"id"`
Title string `json:"title"`
Status bool `json:"status"`
}
var goals = []*Goal{
{
Id: 1,
Title: "Read about Promises",
Status: true,
},
{
Id: 2,
Title: "Read about Closures",
Status: false,
},
}
func GetGoals(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(
// "success": true,
// "data": fiber.Map{
// "goals": goals,
// },
goals,
)
}
func CreateGoal(c *fiber.Ctx) error {
type Request struct {
Title string `json:"title"`
}
var body Request
err := c.BodyParser(&body)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse JSON",
"error": err,
})
}
goal := &Goal{
Id: len(goals) + 1,
Title: body.Title,
Status: false,
}
goals = append(goals, goal)
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"goal": goal,
},
})
}
Your endpoint is /goals/add in the application for POST method. But in Postman you called /goals
In application /goals expecting a GET request. That is why there method is not allowing.
I want to create a new document in Firestore using the REST API.
Very good examples here using Axios to send the POST request with some fields:
https://www.jeansnyman.com/posts/google-firestore-rest-api-examples/
axios.post(
"https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/<COLLECTIONNAME>",
{
fields: {
title: { stringValue: this.title },
category: { stringValue: this.category },
post: { stringValue: this.post },
summary: { stringValue: this.description },
published: { booleanValue: this.published },
created: { timestampValue: new Date() },
modified: { timestampValue: new Date() }
}
}
).then(res => { console.log("Post created") })
And an example here using Python Requests:
Using the Firestore REST API to Update a Document Field
(this is a PATCH request but the field formatting is the same as in a POST request)
import requests
import json
endpoint = "https://firestore.googleapis.com/v1/projects/[PROJECT_ID]/databases/(default)/documents/[COLLECTION]/[DOCUMENT_ID]?currentDocument.exists=true&updateMask.fieldPaths=[FIELD_1]"
body = {
"fields" : {
"[FIELD_1]" : {
"stringValue" : "random new value"
}
}
}
data = json.dumps(body)
headers = {"Authorization": "Bearer [AUTH_TOKEN]"}
print(requests.patch(endpoint, data=data, headers=headers).json())
I am using Google Apps Script UrlFetchApp.fetch to send my requests. I am able to use GET requests with no problems. For example, to get all the documents in a collection (in Google Apps Script):
function firestore_get_documents(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'GET'
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var parsed = JSON.parse(response.getContentText());
return parsed;
}
This works nicely. And changing 'method' to 'POST' creates a new document in myCollection as expected. Then I try to add a POST body with some fields (or just one field):
function firestore_create_new_document(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var contentText = response.getContentText();
var parsed = JSON.parse(response.getContentText());
return parsed;
}
I get the following errors:
code: 400 message: "Request contains an invalid argument."
status: "INVALID_ARGUMENT"
details[0][#type]: "type.googleapis.com/google.rpc.BadRequest"
details[0][fieldViolations][0][field]: "{title={stringValue=newTitle}}"
details[0][fieldViolations][0][description]: "Error expanding 'fields' parameter. Cannot find matching fields for path '{title={stringValue=newTitle}}'."
Documentation is available here:
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/createDocument
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
The problem may be the formatting of my 'fields' object - I've tried several different formats from the documentation and examples
The problem may be that the fields don't exist yet? I think I should be able to create a new document with new fields
The problem may be with the way UrlFetchApp.fetch sends my JSON body. I have tried using payload = JSON.stringify(payload_object) and that doesn't work either.
I think UrlFetchApp is doing something slightly different than Axios or Python Requests - the body is getting sent differently, and not parsing as expected.
How about the following modification?
From:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
To:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: JSON.stringify({fields: { title: { stringValue: 'newTitle' } } }),
contentType: "application/json",
muteHttpExceptions:true
}
When I tested above modified request, I could confirm that it worked. But if other error occurs, please tell me.
Reference:
Class UrlFetchApp
I'm following the facebook messenger develop QuickStart to create a Node.js project, and I improved it to work in quick reply. Then when I tried the Generic Template and List Template, but it didn't work.
As the following source code, when I input the work "generic" or "list", the messenger should reply me with the template messege. But there was nothing happened.
} else if (received_message.text === 'generic') {
console.log('generic in');
response = {
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements":[
{
"title":"Welcome!",
"image_url":"http://webapplication120181023051009.azurewebsites.net/colorcar1.jpg",
"subtitle":"We have the right hat for everyone.",
"default_action": {
"type": "web_url",
"url": "https://www.taobao.com/",
"messenger_extensions": false,
"webview_height_ratio": "tall",
"fallback_url": "https://www.taobao.com/"
},
"buttons":[
{
"type":"web_url",
"url":"https://www.taobao.com/",
"title":"View Website"
},{
"type":"postback",
"title":"Start Chatting",
"payload":"DEVELOPER_DEFINED_PAYLOAD"
}
]
}
]
}
}
}
// Sends the response message
callSendAPI(sender_psid, response);
// Sends response messages via the Send API
function callSendAPI(sender_psid, response) {
// Construct the message body
let request_body = {
"recipient": {
"id": sender_psid
},
"message": response
}
console.log('PAGE_ACCESS_TOKEN:');
console.log(PAGE_ACCESS_TOKEN);
console.log('request body:');
console.log(request_body);
// Send the HTTP request to the Messenger Platform
request({
"uri": "https://graph.facebook.com/v2.6/me/messages?access_token=" + PAGE_ACCESS_TOKEN,
"qs": { "access_token": PAGE_ACCESS_TOKEN },
"method": "POST",
"json": request_body
}, (err, res, body) => {
if (!err) {
console.log('message sent!')
} else {
console.error("Unable to send message:" + err);
}
});
}
Sorry, I forgot to add the url into whiltelist.
I know there are many questions similar to mine out there on this subject but those don't solve my issue. I know that web services naturally parse my objects into json as part of the framework. I have manually set the request header header Accept to 'application/json, text/javascript, /; q=0.01'. I have added the <ScriptMethod(ResponseFormat:=ResponseFormat.Json)> to my web service.
It is clear that my web service is responding to the file upload request header with a text/plain response and parsing my simple FineUploaderResponse object is failing. Keep in mind that a regular jQuery AJAX call to the same web service works fine. I would prefer not to use Web API or generic handlers in place of my web as multiple websites are reliant on my framework and expect this standard.
Thanks in advance!
The code:
Public Class FineUploaderResponse
Property Success As Boolean
End Class
<WebMethod(EnableSession:=True)> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
Public Function UploadPhotos()
'next three lines are pointless, didn't help
'HttpContext.Current.Response.Clear()
'HttpContext.Current.Response.ContentType = "application/json"
'HttpContext.Current.Response.Charset = "utf-8"
Dim response As New FineUploaderResponse()
response.Success = True
Return response
End Function
Now of course this web service works if I do this:
$(document).ready(function () {
$.ajax(
{
url: "/Services/PhotosService.asmx/UploadPhotos",
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (data) {
console.log(data.d);
}
});
});
The standard jquery request header is
POST http://localhost:3066/Services/PhotosService.asmx/UploadPhotos HTTP/1.1
Host: localhost:3066
Proxy-Connection: keep-alive
Content-Length: 0
Cache-Control: no-cache
Pragma: no-cache
Origin: http://localhost:3066
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Content-Type: application/json; charset=utf-8
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: http://localhost:3066/Test.aspx
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
But I am using FineUploader and it is posting a Content-Type:multipart/form-data; Notice though the Accept header is the same for both jQuery AJAX and FineUploader requests:
POST http://localhost:3066/Services/PhotosService.asmx/UploadPhotos HTTP/1.1
Host: localhost:3066
Proxy-Connection: keep-alive
Content-Length: 110634
Cache-Control: no-cache
Pragma: no-cache
Origin: http://localhost:3066
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary449tHPTKEpuO5jOR
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
Referer: http://localhost:3066/Sellers/photos/?pid=37344
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
The web service response from the standard Jquery ajax call is:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 17 Apr 2013 00:12:21 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Content-Length: 22
Connection: Close
The web service response from a FineUploader post file request is:
HTTP/1.1 500 Internal Server Error
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 17 Apr 2013 00:18:26 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Length: 1936
Connection: Close
The internal server 500 error error message details are:
System.InvalidOperationException: There was an error generating the XML document. ---> System.InvalidOperationException: The type Kazork.AppCode.PhotosService+FineUploaderResponse was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
at System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(String name, String ns, Object o, Boolean xsiType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write1_Object(String n, String ns, Object o, Boolean isNullable, Boolean needType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriter1.Write3_anyType(Object o)
at Microsoft.Xml.Serialization.GeneratedAssembly.ObjectSerializer1.Serialize(Object objectToSerialize, XmlSerializationWriter writer)
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
--- End of inner exception stack trace ---
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o, XmlSerializerNamespaces namespaces)
at System.Web.Services.Protocols.XmlReturnWriter.Write(HttpResponse response, Stream outputStream, Object returnValue)
at System.Web.Services.Protocols.HttpServerProtocol.WriteReturns(Object[] returnValues, Stream outputStream)
at System.Web.Services.Protocols.WebServiceHandler.WriteReturns(Object[] returnValues)
at System.Web.Services.Protocols.WebServiceHandler.Invoke()
And, in case you are curious, here is my FineUploader call:
var uploader = new qq.FineUploader({
element: document.getElementById('bootstrapped-fine-uploader'),
request: {
endpoint: "/Services/PhotosService.asmx/UploadPhotos",
forceMultipart: true,
params: { propertyId:<%=PropertyId %>},
customHeaders: { Accept: 'application/json, text/javascript, */*; q=0.01' }
},
text: {
uploadButton: '<i class="icon-upload icon-white"></i>Upload nice images.'
},
template: '<div class="qq-uploader">' +
'<pre class="qq-upload-drop-area"><span>{dragZoneText}</span></pre>' +
'<div class="qq-upload-button btn btn-success" style="width: auto;">{uploadButtonText}</div>' +
'<span class="qq-drop-processing"><span>{dropProcessingText}</span><span class="qq-drop-processing-spinner"></span></span>' +
'</div>' +
'<ul class="qq-upload-list" style="margin-top: 10px; text-align: center;"></ul>' +
'',
classes: {
success: 'alert alert-success',
fail: 'alert alert-error'
},
debug: false,
callbacks: {
// onComplete: function (id, fileName, responseJson) {
// $.when(loadThumbs()).done(function () {
// $(".qq-upload-list > .alert-success").remove();
// });
// toastr.success("Success!");
// },
onComplete: function(id, fileName, responseJSON) {
if (responseJSON.success) {
$('#file-' + id).removeClass('alert-info')
.addClass('alert-success')
.html('<i class="icon-ok"></i> ' +
'Successfully saved ' +
'“' + fileName + '”' +
'<br><img src="/images/message_ok.png" alt="' + fileName + '">');
$.when(loadThumbs()).done(function () {
$(".qq-upload-list > .alert-success").remove();
});
toastr.success("Success!");
} else {
$('#file-' + id).removeClass('alert-info')
.addClass('alert-error')
.html('<i class="icon-exclamation-sign"></i> ' +
'Error with ' +
'“' + fileName + '”: ' +
responseJSON.error);
}
},
onError: function (id, fileName, errorReason) {
toastr.error("Failed! Try again.");
}
}
});
Ok easy fix, frustrating though, web services kill a lot of my time trying to maintain a contemporary web app. But I don't have a need to migrate to Web API at this point, primarily because my web app is reliant on session (you can introduce session into Web API but that is not RESTful obviously).
So I replaced the FineUploaderResponse class with my own formatted JSON response:
<WebMethod(EnableSession:=True)> _
<ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
Public Sub UploadPhotos()
Context.Response.Write(New ResultData().GetResultDataJSON("success", "true"))
End Sub
Public Function GetResultDataJSON(key As String, value As String) As String
Dim oBuilder As StringBuilder = New StringBuilder()
oBuilder.Append("{")
oBuilder.AppendFormat("""{0}"" : {1}", key, value)
oBuilder.Append("}")
Return oBuilder.ToString()
End Function
And here is my FineUploader Javascript:
/*=================================================*/
//fineUploaderInitialize
/*=================================================*/
function createUploader() {
var uploader = new qq.FineUploader({
element: document.getElementById('bootstrapped-fine-uploader'),
request: {
endpoint: "/Services/PhotosService.asmx/UploadPhotos",
forceMultipart: true,
params: { propertyId:$('#hiddenPropertyIdUploadPhotosUserControl').val()},
customHeaders: { Accept: 'application/json, text/javascript, */*; q=0.01' },
allowedExtensions: ['gif', 'jpeg', 'jpg', 'png'],
},
text: {
uploadButton: 'Drag & drop photos into this area or CLICK HERE to upload photos'
},
template: '<div class="qq-uploader text-center" style="height:70px;background-color:white;border-radius:6px;">' +
'<div class="qq-upload-button btn btn-success col-lg-12">{uploadButtonText}</div>' +
'<span class="qq-drop-processing"><span>{dropProcessingText}</span><span class="qq-drop-processing-spinner"></span></span>' +
'<pre class="qq-upload-drop-area"><span>{dragZoneText}</span></pre>' +
'</div>' +
'<ul class="qq-upload-list" style="margin-top: 10px; text-align: center;"></ul>' +
'',
classes: {
success: 'alert alert-success',
fail: 'alert alert-error'
},
debug: false,
callbacks: {
onComplete: function(id, fileName, responseJSON) {
if (responseJSON.success) {
$('#file-' + id).removeClass('alert-info')
.addClass('alert-success')
.html('<i class="glyphicon glyphicon-ok"></i> ' +
'Successfully saved ' +
'“' + fileName + '”' +
'<br><img src="/images/message_ok.png" alt="' + fileName + '">');
$.when(loadThumbs()).done(function () {
$(".qq-upload-list > .alert-success").remove();
});
toastr.success("Success!");
} else {
$('#file-' + id).removeClass('alert-info')
.addClass('alert-error')
.html('<i class="glyphicon glyphicon-exclamation-sign"></i> ' +
'Error with ' +
'“' + fileName + '”: ' +
responseJSON.error);
}
},
onError: function (id, fileName, errorReason) {
if(errorReason == 'XHR returned response code 0'){
toastr.error('File Size Cannot Exceed 20 Megabytes');
}else{
toastr.error(errorReason);
}
}
}
});
}
/*=================================================*/