I am writing a web API with codeigniter, here is one of my result:
http://manage.pineconetassel.com/index.php/api/v1/colors2
It looks close to right, but if I feed it into hurl.it (GET testing service, sorry cannot post url here)
it cannot be parsed, correctly. it shows "colors", but no values
To compare, the following JSON can be parsed correctly by hurl.it
http://www.w3schools.com//website/Customers_JSON.php
what is wrong with my code? The code generating this API is the following:
model:
public function get_colors2(){
$q = $this->publicDB->select('color')->get('car_colors');
if ($q->num_rows > 0) {
return $q->result_array();
} else {return FALSE;}
}
I am using Phil Sturgeon's RESTful server implementation
controller:
function colors2_get()
{
$result = $this->api_model->get_colors2();
if ($result) {
$this->response($result,200);
} else {$this->response(NULL,404);}
}
Related
I'm trying to use Firebase and its callable Cloud Functions for my Unity project.
With the docs and different posts I found on the web I struggle to understand how returning data works. (I come from Azure Functions in C#)
I use TypeScript, and try to return a custom object CharactersResponse:
export class CharactersResponse //extends CustomResponse
{
Code!: CharactersCode;
CharacterID?: string;
}
export enum CharactersCode
{
Success = 0,
InvalidName = 2000,
CharacterNameAlreadyExists = 2009,
NoCharacterSlotAvailable = 3000,
InvalidCharacterClass = 4000,
EmptyResponse = 9000,
UnknownError = 9999,
}
(Custom Response is a parent class with only an UnknownErrorMessage string property, that I use for adding extra message when needed, but only in Unity. I don't need it in my functions.)
I have the same in my C# Unity Project:
public class CharactersResponse : CustomResponse
{
public CharactersCode Code;
public string CharacterID;
}
public enum CharactersCode
{
Success = 0,
InvalidName = 2000,
CharacterNameAlreadyExists = 2009,
NoCharacterSlotAvailable = 3000,
InvalidCharacterClass = 4000,
EmptyResponse = 9000,
UnknownError = 9999,
}
I'm still learning but I found it useful to do this way for displaying correct messages in Unity (and also regarding localization).
When the Code is 0 (Success), I will usually need to get some data at the same time like in this example CharacterID, or CharacterLevel, CharacterName etc.. CharacterResponse will be used for all functions regarding Characters like "GetAllCharacters", "CreateNewCharacter" etc..
My Function (CreateNewCharacter) looks like this:
import * as functions from "firebase-functions";
import { initializeApp } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
import { CharactersResponse } from "./CharactersResponse";
import { CharactersCode } from "./CharactersResponse";
import { StringUtils } from "../Utils/StringUtils";
// DATABASE INITIALIZATION
initializeApp();
const db = getFirestore();
// CREATE NEW CHARACTER
export const CreateNewCharacter =
functions.https.onCall((data, context) =>
{
// Checking that the user is authenticated.
if (!context.auth)
{
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
// TEST
data.text = '';
// Authentication / user information is automatically added to the request.
const uid: string = context?.auth?.uid;
const characterName: string = data.text;
// Check if UserID is present
if (StringUtils.isNullOrEmpty(uid))
{
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'Missing UserID in Auth Context.');
}
const response = new CharactersResponse();
if (StringUtils.isNullOrEmpty(characterName))
{
response.Code = CharactersCode.InvalidName;
console.log("character name null or empty return");
return response; // PROBLEM IS HERE *****************
}
console.log("end return");
return "Character created is named : " + characterName + ". UID = " + uid;
});
In Unity, the function call looks like this:
private static FirebaseFunctions functions = FirebaseManager.Instance.Func;
public static void CreateNewCharacter(string text, Action<CharactersResponse> successCallback, Action<CharactersResponse> failureCallback)
{
Debug.Log("Preparing Function");
// Create the arguments to the callable function.
var data = new Dictionary<string, object>();
data["text"] = text;
// Call the function and extract the operation from the result.
HttpsCallableReference function = functions.GetHttpsCallable("CreateNewCharacter");
function.CallAsync(data).ContinueWithOnMainThread((task) =>
{
if (task.IsFaulted)
{
foreach(var inner in task.Exception.InnerExceptions)
{
if (inner is FunctionsException)
{
var e = (FunctionsException)inner;
// Function error code, will be INTERNAL if the failure
// was not handled properly in the function call.
var code = e.ErrorCode;
var message = e.Message;
Debug.LogError($"Code: {code} // Message: {message}");
if (failureCallback != null)
{
failureCallback.Invoke(new CharactersResponse()
{
Code = CharacterCode.UnknownError,
UnknownErrorMessage = $"ERROR: {code} : {message?.ToString()}"
});
}
}
}
}
else
{
Debug.Log("About to Deserialize response");
// PROBLEM IS HERE *********************
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());
Debug.Log("Deserialized response");
if (response == null)
{
Debug.LogError("Response is NULL");
}
else
{
Debug.Log("ELSE");
Debug.Log($"Response: {response}");
Debug.Log(response.Code.ToString());
}
}
});
}
The problem :
In my Unity C# code, task.Result.Data contains the CharactersCode I've set in my function, but I can't find a way to convert it to CharactersResponse. (It worked in Azure Functions). Moreover, the line just after Deserialization Debug.Log("Deserialized response"); is not executed. The code seems stuck in the deserialization process.
I tried with and without extending my TypeScript class with CustomResponse(because I don't need it in my Function so I didn't extended it at first).
I also tried setting a CharacterID because I thought maybe it didn't like the fact that this property was missing but the result is the same.
I don't understand what is the problem here? If any of you can help.
Thanks.
HttpsCallableResult.Data is of type object!
=> Your ToString will simply return the type name something like
System.Object
or in your case the result is a dictionary so it prints out that type.
=> This is of course no valid JSON content and not what you expected.
Simply construct the result yourself from the data:
var result = (Dictionary<string, object>)task.Result.Data;
CharactersResponse response = new CharactersResponse
{
Code = (CharactersCode)(int)result["Code"],
CharacterID = (string)result["CharacterID"];
};
I wanted to implement derHugo's solution but couldn't find a way to convert task.Result.Data to Dictionary<string, object>.
The code was stuck at var result = (Dictionary<string, object>)task.Result.Data; even in step by step debugging and no error popped up.
OLD SOLUTION:
So I did a little research and stumbled upon this post and ended up using this instead :
var json = JsonConvert.SerializeObject(task.Result.Data);
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(json);
I basically convert the task.Result.Data to JSON and convert it back to CharactersResponse and it works. I have what I wanted.
However, I seem to understand that it is not the best solution performance-wise, but for now it is okay and I can now move forward in the project, I'll try to find a better solution later.
NEW SOLUTION:
I wanted to try one last thing, out of curiosity. I wondered what if I convert to JSON at the beginning (in my function) instead of at the end (in my Unity app). So I did this in my function's TypeScript code:
response.Code = CharactersCode.InvalidName;
var r = JSON.stringify(response); // Added this line
return r; // return 'r' instead of 'response'
In my C# code, I retried this line of code:
CharactersResponse response = JsonConvert.DeserializeObject<CharactersResponse>(task.Result.Data.ToString());
And it works ! I just needed to convert my object to JSON in my function before returning it. It allows me to "save" one line of code to process on the client side compared to the old solution.
Thanks derHugo for your answer as it helped me finding what I want.
I am wrote API method, after calling that method , I got my response like
[
{
"spark_version": "7.6.x-scala2.12"
}
]
Now I want to have variable in my API method which store value 7.6.x-scala2.12.
API Controller method
[HttpGet]
public IActionResult GetTest(int ActivityId)
{
string StoredJson = "exec sp_GetJobJSONTest " +
"#ActivityId = " + ActivityId ;
var result = _context.Test.FromSqlRaw(StoredJson);
return Ok(result);
}
So how this variable should call on this response to get string stored in spark_version?
Thank you
As you have the JavaScript tag, here's how you'd do it in JS:
If you are able to call your API method, then you can just assign the response to a variable. For example, if you are calling it using the fetch API, it would look something like:
let apiResponse;
fetch('myApiUrl.com').then((response) => {
if (response.status === 200) {
apiResponse = response.body;
console.log('Response:', apiResponse[0]['spark_version']);
}
});
(I defined the variable outside the then to make it globally accessible)
I have been trying to get an Entity Framework model to convert to JSON to display in a web page. The Entity object is created fine but something fails when it is returned. Below is the code from my ASP.NET Web API project. By setting a breakpoint I can see the object collection is created just fine.
public class ClientsController : ApiController
{
public IEnumerable<Client> GetAllClients()
{
using (var context = new MyClientModel.MyEntities())
{
var query = context.Clients.Where(c => c.State == "CA");
var customers = query.ToList();
return customers;
}
}
}
Here is the HTML/Javascript code I use to call the ASP.NET Web API
<script>
var uri = 'api/clients';
$(document).ready(function () {
// Send an AJAX request
$.getJSON(uri)
.done(function (data) {
// On success, 'data' contains a list of products.
alert('Made it!'); // ** Never reaches here **
$.each(data, function (key, item) {
// Add a list item for the product.
$('<li>', { text: item }).appendTo($('#clients'));
});
});
});
</script>
I use Fiddler to view the response and it returns a .NET error that says ...
The 'ObjectContent' type failed to serialize the response body for content type 'application/json; charset=utf-8'."
and the inner exception message is ...
Error getting value from 'Patients' on 'System.Data.Entity.DynamicProxies.Client
Patients is a related entity in my model but I am confused why it would be an issue as I am only returning Client objects.
I found a solution that works, though I admit I am not sure exactly how it works. I added the line context.Configuration.ProxyCreationEnabled = false; to my method that returns the object collection and all my objects were returned. I got the code from the following SO Question - WebApi with EF Code First generates error when having parent child relation.
public class ClientsController : ApiController
{
public IEnumerable<Client> GetAllClients()
{
using (var context = new MyClientModel.MyEntities())
{
context.Configuration.ProxyCreationEnabled = false; // ** New code here **
var query = context.Clients.Where(c => c.State == "CA");
var customers = query.ToList();
return customers;
}
}
}
I ran into an issue with CodeIgniter / CSRF / JSON.
I am sending http POST requests to my PHP backend with the Content-Type "application/json. The payload is JSON data. Along with the data, I pass the CSRF token that is generated and stored in the CSRF cookie. With a standard POST FORM request, it works just fine, but when sending as JSON it fails.
As $_POST array is empty because of the JSON content-type, CodeIgniter fails to validate the cookie and throws an error.
How can I have CodeIgniter check JSON payload and validate my CSRF token ?
To fix that issue, I had to change the code of the "Security.php" file located in "system/core/".
In function "csrf_verify", replace that code:
// Do the tokens exist in both the _POST and _COOKIE arrays?
if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name]))
{
$this->csrf_show_error();
}
// Do the tokens match?
if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
{
$this->csrf_show_error();
}
By that code:
// Do the tokens exist in both the _POST and _COOKIE arrays?
if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) {
// No token found in $_POST - checking JSON data
$input_data = json_decode(trim(file_get_contents('php://input')), true);
if ((!$input_data || !isset($input_data[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])))
$this->csrf_show_error(); // Nothing found
else {
// Do the tokens match?
if ($input_data[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
$this->csrf_show_error();
}
}
else {
// Do the tokens match?
if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
$this->csrf_show_error();
}
That code first checks $_POST then if nothing has been found, it checks the JSON payload.
The ideal way of doing this would be to check the incoming request Content-Type header value. But surprisingly, it's not straight forward to do ...
If someone has a better solution, please post it here.
Cheers
Alternatively, you can skip the CSRF checking by adding following code on application/config/config.php below Line No. 351 (based on CI 2.1.4).
$config['csrf_expire'] = 7200; // This is line no. 351
/* If the REQUEST_URI has method is POST and requesting the API url,
then skip CSRF check, otherwise don't do. */
if (isset($_SERVER["REQUEST_URI"]) &&
(isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'POST') ))
{
if (stripos($_SERVER["REQUEST_URI"],'/api/') === false ) { // Verify if POST Request is not for API
$config['csrf_protection'] = TRUE;
}
else {
$config['csrf_protection'] = FALSE;
}
} else {
$config['csrf_protection'] = TRUE;
}
If this needs to be overridden, best to extend the Security library rather than editing the core file directly.
Create the file My_Security.php in application/core/ and add the following (from the solution above):
<?php
class My_Security extends CI_Security {
/**
* Verify Cross Site Request Forgery Protection
*
* #return object
*/
public function csrf_verify()
{
// If it's not a POST request we will set the CSRF cookie
if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
{
return $this->csrf_set_cookie();
}
// Do the tokens exist in both the _POST and _COOKIE arrays?
if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])) {
// No token found in $_POST - checking JSON data
$input_data = json_decode(trim(file_get_contents('php://input')), true);
if ((!$input_data || !isset($input_data[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])))
$this->csrf_show_error(); // Nothing found
else {
// Do the tokens match?
if ($input_data[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
$this->csrf_show_error();
}
}
else {
// Do the tokens match?
if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
$this->csrf_show_error();
} // We kill this since we're done and we don't want to
// polute the _POST array
unset($_POST[$this->_csrf_token_name]);
// Nothing should last forever
unset($_COOKIE[$this->_csrf_cookie_name]);
$this->_csrf_set_hash();
$this->csrf_set_cookie();
log_message('debug', 'CSRF token verified');
return $this;
}
}
As Brian write, you have to put your custom class into /application/core/ ex. My_Security.php
This is mine solution, work for me, i check the application/json content_type and request cookies.
defined('BASEPATH') OR exit('No direct script access allowed');
class MY_Security extends CI_Security {
public function csrf_verify()
{
// If it's not a POST request we will set the CSRF cookie
if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
{
return $this->csrf_set_cookie();
}
/**
* mine implementation for application/json
*/
$reqHeaders = getallheaders();
$content_type = $reqHeaders["Content-Type"];
#it's a json request?
if(preg_match("/(application\/json)/i",$content_type))
{
#the check the cookie from request
$reqCookies = explode("; ",$reqHeaders["Cookie"]);
foreach($reqCookies as $c)
{
if(preg_match("/(".$this->_csrf_cookie_name."\=)/", $c))
{
$c = explode("=",$c);
if($_COOKIE[$this->_csrf_cookie_name] == $c[1])
{
return $this;
}
}
}
}
//< end
// Check if URI has been whitelisted from CSRF checks
if ($exclude_uris = config_item('csrf_exclude_uris'))
{
$uri = load_class('URI', 'core');
foreach ($exclude_uris as $excluded)
{
if (preg_match('#^'.$excluded.'$#i'.(UTF8_ENABLED ? 'u' : ''), $uri->uri_string()))
{
return $this;
}
}
}
// Do the tokens exist in both the _POST and _COOKIE arrays?
if ( ! isset($_POST[$this->_csrf_token_name], $_COOKIE[$this->_csrf_cookie_name])
OR $_POST[$this->_csrf_token_name] !== $_COOKIE[$this->_csrf_cookie_name]) // Do the tokens match?
{
$this->csrf_show_error();
}
// We kill this since we're done and we don't want to polute the _POST array
unset($_POST[$this->_csrf_token_name]);
// Regenerate on every submission?
if (config_item('csrf_regenerate'))
{
// Nothing should last forever
unset($_COOKIE[$this->_csrf_cookie_name]);
$this->_csrf_hash = NULL;
}
$this->_csrf_set_hash();
$this->csrf_set_cookie();
log_message('info', 'CSRF token verified');
return $this;
}
}
I have a JSON request, but it seems that it is not hitting the controller. Here's the jQuery code:
$("#ddlAdminLogsSelectLog").change(function() {
globalLogSelection = $("#ddlAdminLogsSelectLog").val();
alert(globalLogSelection);
$.getJSON("/Administrative/AdminLogsChangeLogSelection", { NewSelection: globalLogSelection }, function(data) {
if (data.Message == "Success") {
globalCurrentPage = 1;
} else if (data.Message == "Error") {
//Do Something
}
});
});
The alert is there to show me if it actually fired the change event, which it does.
Heres the method in the controller:
public ActionResult AdminLogsChangeLogSelection(String NewSelection)
{
String sMessage = String.Empty;
StringBuilder sbDataReturn = new StringBuilder();
try
{
if (NewSelection.Equals("Application Log"))
{
int i = 0;
}
else if (NewSelection.Equals("Email Log"))
{
int l = 0;
}
}
catch (Exception e)
{
//Do Something
sMessage = "Error";
}
return Json(new { Message = sMessage, DataReturn = sbDataReturn.ToString() }, JsonRequestBehavior.AllowGet);
}
I have a bunch of Json requests in my application, and it seems to only happen in this area. This is a separate area (I have 6 "areas" in the app, 5 of which work fine with JSON requests). This controller is named "AdministrativeController", if that matters.
Does anything jump out anyone as being incorrect or why the request would not pass to the server side?
Look at the GET in Firebug or Fiddler.
Either:
There is no GET, in which case your browser cached the results from last time (cough, IE, cough); change the cache policy on the response.
There is a GET, but it doesn't match your route; fix the routing or the JavaScript, as appropriate.
As it turns out, if the Area name and Controller name are the same, it looks like MVC gets a little confused. Im not sure if this is a bug on my side, or something witH MVC, but when I remove the "/" from the name in the Json request (ie. "Administrative/Action" instead of "/Administrative/Action") it works just fine. A colleague was the one to figure this one out for me, he found some forum response on it and showed me what they did. Once I removed the "/" it worked just fine.