mevdschee / php-crud-api

Single file PHP script that adds a REST API to a SQL database

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Cannot POST, but GET,PUT,DELETE OK via Swift URLSession request

junmcenroe opened this issue · comments

Dear Author

I found this GREAT crud api finally. Then I start to try, but I cannot make it solve by myself. I checked the past issues but I cannot find answers.
I can't add new recode with POST. But I can get all records with GET, and can modify/delete the existing records with PUT/DELETE.
POST result is always null.

Can you advise me what is wrong? I am newbie from this area.

<Environment>
MAMP(local sever)  Apache:2.4.46, MySQL:5.7.34,PHP:7.4.21
<Database> GBTable
id:Int(255) <- Primary Key
sku:varcahr(10)
code:varcahr(10)
<php-crud-api>
 modified as following. Nothing changed except identifying table and customization afterhandler

namespace Tqdev\PhpCrudApi {

use Tqdev\PhpCrudApi\Api;
use Tqdev\PhpCrudApi\Config\Config;
use Tqdev\PhpCrudApi\RequestFactory;
use Tqdev\PhpCrudApi\ResponseUtils;

$config = new Config([
    'username' => '**********',
    'password' => '*****************',
    'database' => 'testDB',
    'tables' => 'GBTable',
    'middlewares' => 'cors,customization',
        'customization.afterHandler' => function ($operation, $tableName, $response, $environment) {
            $json = json_decode($response->getBody()->getContents());
            return ResponseFactory::fromObject(200, $json->records,0);
        }
//         'debug' => false
]);
$request = RequestFactory::fromGlobals();
$api = new Api($config);
$response = $api->handle($request);
ResponseUtils::output($response);

//file_put_contents('request.log',RequestUtils::toString($request)."===\n",FILE_APPEND);
//file_put_contents('request.log',ResponseUtils::toString($response)."===\n",FILE_APPEND);
}

My POST code

let serverAddress = "https://localhost/api.php/records/GBTable"

let url = URL(string: serverAddress)!
print("url= \(url)")

let request = NSMutableURLRequest(url: url)

// set the method(HTTP-POST)
request.httpMethod = "POST"
// set the header(s)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")

// set the request-body(JSON)
let params: [String: Any] = [
    "id": "3",
    "sku":"JP",
    "Code":"DDD"
]

do{
    request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
}catch{
    print(error.localizedDescription)
}

// use NSURLSessionDataTask
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {data, response, error in
    if error == nil {
        print("OK")
        let result = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
        print("result= \(result)")
    } else {
        print("error")
        print(error as Any)
    }
})
task.resume()

My PUT code

let serverAddress = "https://localhost/api.php/records/GBTable/3"
let url = URL(string: serverAddress)!
print("url= \(url)")

let request = NSMutableURLRequest(url: url)

// set the method(HTTP-POST)
request.httpMethod = "PUT"
// set the header(s)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")

// set the request-body(JSON)
let params: [String: Any] = [
    "id": "3",
    "sku":"AP",
    "Code":"XXX",
]

do{
    request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
}catch{
    print(error.localizedDescription)
}

// use NSURLSessionDataTask
let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: {data, response, error in
    if error == nil {
        print("OK")
        let result = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
        print("result= \(result)")
    } else {
        print("error")
        print(error as Any)
    }
    
})
task.resume()

Thank you for asking this question. Your client is written in Swift, am I right? I seem to recognize some keywords from here: https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/WorkingWithHTTPAndHTTPSRequests/WorkingWithHTTPAndHTTPSRequests.html

I'm looking forward to help you. Do you use mitmproxy or an equivalent proxy such as charlesproxy?

Dear mevdschee

Thanks for your quick response.
Yes, I wrote in Swift 5.
My code using "PUT" and set json as Contents-Type, and set httpBody with params
can modify my server data which I can checked PhpAdmin.

request.httpMethod = "PUT"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])

I do not use any proxy. I set up the local in M1 Mac and set up the mkcert for https connection.

I check the php_error.log
when I try PUT,
PHP Notice: Trying to access array offset on value of type int in /Applications/MAMP/htdocs/api.php on line 12762
and success modification.

When I try POST
PHP Notice:Undefined index: records in /Applications/MAMP/htdocs/api.php on line 12762

line 12762 is
return ResponseFactory::fromObject(200, $json->records,0);

So It seems that something "customization.afterHandler' issue for POST?. But If this customization.afterHandler is missing , I cannot display native JSON format.
Actually POST is to add new record, so no index should be.

Any suggestion?

Any suggestion?

Sure, in the "afterHandler" you assume the response has a property "records", but a POST does not have that, that's why it might be failing.

    'customization.afterHandler' => function ($operation, $tableName, $response, $environment) {
        $json = json_decode($response->getBody()->getContents());
        return ResponseFactory::fromObject(200, $json->records,0);
    }

I hope this helps.

Dear mevdschee

Thanks for your quick response.
My understanding is following afterhandler is to display JSON form when received GET from client.
If this afterhandler is used as POST, how do I modify this afterhandler?
I am new be of php. So some sample code you can provide?

    'middlewares' => 'cors,customization',
    'customization.afterHandler' => function ($operation, $tableName, $response, $environment) {
        $json = json_decode($response->getBody()->getContents());
        return ResponseFactory::fromObject(200, $json->records,0);
    }

It sounds like you copied something without understanding what it is doing. You can leave out the 'customization.afterHandler' and everything will work just fine. Some people argue that responses (on 'list' operations) should not contain the "records" key and they use the 'customization.afterHandler' to get rid of that.

I hope this helps.

Dear mevdschee

I tried with small table with accept null condition. and can POST successfully even if I stay with 'customization.afterHandler'.
My initial failure will be happened with large table without null accept, and only few parts of param will be POSTED.

Thanks for your very quickly and helpful advice.

My initial failure will be happened with large table without null accept, and only few parts of param will be POSTED.

I don't know what "table without null accept" means, can you explain this a little bit further please? Also you write "only few parts of param will be POSTED" and what parts is that, can you elaborate? Can you give an example of the request, the expected result and the actual result? I want to help, but I do need a bit more information to go by.

Kind regards, Maurits

Dear mevdschee

I explain as much as clear.
Initial test condition was

  1. Database records have more than 3 records and each recode have no NULL acceptable condition. for example (4 records, actually my records had 15 records)
    "id" <- Primary Key
    "sku"
    "Code"
    "Name"
  2. In this condition, I made PUT, DELETE, GET successfully.
  3. So I tried to test POST with following code. This data is just 3 records.

// set the method(HTTP-POST)
request.httpMethod = "POST"
// set the header(s)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")

// set the request-body(JSON)
let params: [String: Any] = [
"id": "3",
"sku":"JP",
"Code":"DDD"
]

do{
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
}catch{
print(error.localizedDescription)
}


  1. In this case, result shows
    {"code":1010,"message":"Data integrity violation"}
  2. Then I changed the Database condition with NULL acceptable for each records except "id"
  3. then same 3 records POST will successfully completed.
  4. then I changed back again with NON NULL acceptable condition, then POST all records made successfully completed.

No change api.php which has following customization.


'middlewares' => 'cors,customization',
'customization.afterHandler' => function ($operation, $tableName, $response, $environment) {
    $json = json_decode($response->getBody()->getContents());
    return ResponseFactory::fromObject(200, $json->records,0);
}

I do not sure my approach is reasonable or not, but I can POST as of now.

It seems you don't POST "Name" while that field is not nullable and does not have a default. That may be the cause of the "Data integrity violation".

Dear mevdschee

I understood.
Thanks for your great support.
I need to continue studying these area.

Let me know if you have further questions. Feel free to open a new issue.