Zend Apigility : DELETE HTTP method Validation - zend-framework2

I have recently explored Apigility I want to use HTTP DELETE method to delete some entity but before deleting I need to validate "entityId" must be given and must be Digit and trim.
Problem is documentation mentions that:
Content Validation currently only works for POST, PATCH, and PUT requests. If you need to validate query string parameters, you will need to write your own logic for those tasks.
https://apigility.org/documentation/content-validation/intro
I have make some custome modification in config file as bellow:
'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Controller' => [
'DELETE' => 'NetworkingNightAPI\\V1\\Rpc\\AssignTimeSlotToLoginUser\\Validator',
],
As I have mention DELETE method to validate same as NetworkingNightAPI\V1\Rpc\AssignTimeSlotToLoginUser\Validator but the issue is it always return 'Value could not be empty' even I have added valid row JSON values using PostMan
Thanks!

Thank you for your reply
What I have found is Apigility uses 'zf-content-validation' module for validating the input data (https://github.com/zfcampus/zf-content-validation)
This module dose not restrict such HTTP Methods you can apply validation to DELETE method as well Like it says that
"In the above example, the Application\Controller\HelloWorld\Validator service will be selected for PATCH, PUT, or DELETE requests, while the Application\Controller\HelloWorld\CreationValidatorwill be selected for POST requests."
So you just need to add manual entry for DELETE method in config file as below:
'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Controller' => [
'input_filter' => 'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Validator',
'DELETE' => 'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Validator',
],
In addition HTTP DELETE method will not validate using JSON row body from POSTMAN you have to pass query parameters and in your controller you need to get validated data using plugin like below:
$recruiterId = $this->getInputFilter()->getValues()['recruiterId'];
$timeSlotId = $this->getInputFilter()->getValues()['timeSlotId'];

If you want to delete a resource your should use the url that includes the route to that entity. This means the id would be in your route parameters, not in your query parameters. So the id is a route parameter/identifier and the RestController will search your entity using the identifier in the fetch($id) method of your resource listener. The listener should return a not found (404) response in case the entity with that identifier doesn't exist.
The content validation you mention in your question is for validating POST/GET parameters. So there is no need for such validator in case of a delete request.
So say for example you want to delete a Slot you would have a route:
api/v1/slots/[slot_id]
And if you want to delete Slot with id 1 you would send a delete request to:
DELETE
api/v1/slots/1
Your listener should simply return a 404 response in case a Slot with slot_id 1 doesn't exist.

I see you're using RPC Rather than Rest style - if you're passing the parameter using the query string you will have to validate it yourself inside the controller, for example:
public function someActionMethod()
{
$id = $this->getRequest()->getQuery('id');
$validator = new Input('id');
$validator->getValidatorChain()
->attach(new \Zend\Validator\NotEmpty())
;
$validator->getFilterChain()
->attach(new StringToUpper())
;
$inputFilter = new InputFilter();
$inputFilter
->add($validator)
->setData($this->getRequest()->getQuery())
;
if( ! $inputFilter->isValid()) {
return new \ZF\ApiProblem\ApiProblemResponse(
new ApiProblem(400, $inputFilter)
);
}
}
Apigility won't use any of the config generated using the UI to validate those fields for you wuen passed via query string as it says in the docs - they will be ignored. You would need to generate the valaidator yourself.
You could set it up to generate the validation using a config if you wished and then load the validator inside the controller to save writing boiler plate code as above.

Related

Custom route for record.save() in ember

In ember , I have a model called "Post" .
If i perform
record.save()
on creating a post , by default it posts to my /post url in Rails(backend). This works in my case. But for future uses, how do i specify a custom route? Let's say I want to post this model to my backend to a route called
"/post/save"
How do i make record.save() in ember to go to this route?
Adapters in ember manage how a model communicates with remote data. If you need to go outside of ember convention, you can create a custom adapter for your model that can point actions to different places.
ember generate adapter model-name
In your case, you want to call /post/save instead of /post when you create a post. You would overload the method urlForCreateRecord:
urlForCreateRecord(modelName, snapshot) {
return '/post/save';
}
Each url for an action (create, update, new, find, query) can be adjusted to fit your backend.
You can do that in the adapter of your post model.
Given your case, you need to add:
urlForCreateRecord () {
return this._super(...arguments) + '/save';
}
That url will only be used when the save method is creating the record (POST), when saveing already existing records (PATCH) you should use urlForUpdateRecord

MVC Redirect to a different view in another controller

After a user has completed a form in MVC and the post action is underway, I am trying to redirect them back to another view within another model.
Eg. This form is a sub form of a main form and once the user has complete this sub form I want them to go back to the main form.
I thought the following might have done it, but it doesn't recognize the model...
//send back to edit page of the referral
return RedirectToAction("Edit", clientViewRecord.client);
Any suggestions are more that welcome...
You can't do it the way you are doing it. You are trying to pass a complex object in the url, and that just doesn't work. The best way to do this is using route values, but that requires you to build the route values specifically. Because of all this work, and the fact that the route values will be shown on the URL, you probably want this to be as simple a concise as possible. I suggest only passing the ID to the object, which you would then use to look up the object in the target action method.
For instance:
return RedirectToAction("Edit", new {id = clientViewRecord.client.ClientId});
The above assumes you at using standard MVC routing that takes an id parameter. and that client is a complex object and not just the id, in which case you'd just use id = clientViewRecord.client
A redirect is actually just a simple response. It has a status code (302 or 307 typically) and a Location response header that includes the URL you want to redirect to. Once the client receives this response, they will typically, then, request that URL via GET. Importantly, that's a brand new request, and the client will not include any data with it other than things that typically go along for the ride by default, like cookies.
Long and short, you cannot redirect with a "payload". That's just not how HTTP works. If you need the data after the redirect, you must persist it in some way, whether that be in a database or in the user's session.
If your intending to redirect to an action with the model. I could suggest using the tempdata to pass the model to the action method.
TempData["client"] = clientViewRecord.client;
return RedirectToAction("Edit");
public ActionResult Edit ()
{
if (TempData["client"] != null)
{
var client= TempData["client"] as Client ;
//to do...
}
}

Previous GET parameters being unintentionally preserved in POST request

As of current I navigate to a view using a GET request, looking something like this:
/batches/install?Id=2&ScheduledDate=07.29%3A12
From there, I send a POST request using a form (where I include what data I wish to include in the request.
Furthermore I set the forms action to "Create" which is the action I wish to send the request to.
My issue is the fact that sending this request keeps the GET arguments in the POST url, making it look the following:
../batches/Create/2?ScheduledDate=07.29%3A12
I do not want this since:
1: it looks weird
2: it sends data I do not intend it to send in this request
3: if my model already has a property named "id" or "scheduledDate" the unintentional GET parameters will get bound to those properties.
How can I ignore the current GET parameters in my new POST request?
I just want to send the form POST data to the url:
../batches/create
(without any GET parameters)
How would this be done?
As requested, here is my POST form:
#using (var f = Html.Bootstrap().Begin(new Form("Create")))
{
//inputs omitted for brevity
#Html.Bootstrap().SubmitButton().Style(ButtonStyle.Success).Text("Create batch")
}
Note that I use the TwitterBootstrapMVC html helpers (https://www.twitterbootstrapmvc.com/), althought this really shouldn't matter.
As requested, to sum up:
I send a get request to : /batches/install?Id=2&ScheduledDate=07.29%3A12.
Through the returned view I send a POST request to: /batches/create.
However the previous get parameters get included in the POST request URL making the POST request query: /batches/Create/2?ScheduledDate=07.29%3A12 (which is NOT intended).
This is not a great Idea though, but will give you what you want.
[HttpPost]
public ActionResult Create() {
//do whatever you want with the values
return RedirectToAction("Create", "batches");
}
This is a "Feature" of MVC that was previously reported as a bug (issue 1346). As pointed out in the post, there are a few different workarounds for it:
Use named routes to ensure that only the route you want will get used to generate the URL (this is often a good practice, though it won't help in this particular scenario)
Specify all route parameters explicitly - even the values that you want to be empty. That is one way to solve this particular problem.
Instead of using Routing to generate the URLs, you can use Razor's ~/ syntax or call Url.Content("~/someurl") to ensure that no extra (or unexpected) processing will happen to the URL you're trying to generate.
For this particular scenario, you could just explicitly declare the parameters with an empty string inside of the Form.
#using (var f = Html.Bootstrap().Begin(new Form("Create").RouteValues(new { Id = "", ScheduledDate = "" })))
{
//inputs omitted for brevity
#Html.Bootstrap().SubmitButton().Style(ButtonStyle.Success).Text("Create batch")
}

fetchEntityByKey Queries against an invalid api route

My entity name is "Products" but in my WebApi the route is defined as "GetProducts" so in default it will query against /api/Products? instead of /api/GetProducts, is there any way to specify where it should fetch the data from the server if it can't find what i want in the cache.
So far i got this
manager.fetchEntityByKey("Products", productId, true)
.then(fetchSucceeded)
.fail(queryFailed);
This will call http://localhost:1990/breeze/Products?$filter=Id%20eq%201
But i want it to call http://localhost:1990/breeze/GetProducts?$filter=Id%20eq%201 instead
There's also another option to specify this on the server. With WebApi 2, you can use the RouteAttribute on your controller action to customize the routing.
See http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
EDIT:
On the client, the Breeze EntityType has a property called defaultResourceName which you can modify as well to tell Breeze which default resource name to use if only an entity type name is specified.
var productType = metadataStore.getEntityType('Products');
productType.defaultResourceName = 'GetProducts';
//Will create fetch request to breeze/GetProducts
manager.fetchEntityByKey('Products', productId);
You can change entity's default resourceName(that hits the breeze webapi method name) with
manager.metadataStore.setEntityTypeForResourceName("GetProducts", "Product")
But be sure the code above is executed when promise of manager.fetchMetadata() is resolved:
manager.fetchMetadata().then(function(){
manager.metadataStore.setEntityTypeForResourceName(...);
})
If setEntityTypeForResourceName is called before metadata has loaded you will get an error, i.e
Unable to locate 'entityTypeName' of: Product
You can set the Resource for the given entity type which would hit the resource instead -
manager.metadataStore.setEntityTypeForResourceName('GetProducts', 'Products');
But as a note it seems like a design flaw to name your entity Products instead of Product.

How to get Url Hash (#) from server side

I know on client side (javascript) you can use windows.location.hash but could not find anyway to access from the server side. I'm using asp.net.
We had a situation where we needed to persist the URL hash across ASP.Net post backs. As the browser does not send the hash to the server by default, the only way to do it is to use some Javascript:
When the form submits, grab the hash (window.location.hash) and store it in a server-side hidden input field Put this in a DIV with an id of "urlhash" so we can find it easily later.
On the server you can use this value if you need to do something with it. You can even change it if you need to.
On page load on the client, check the value of this this hidden field. You will want to find it by the DIV it is contained in as the auto-generated ID won't be known. Yes, you could do some trickery here with .ClientID but we found it simpler to just use the wrapper DIV as it allows all this Javascript to live in an external file and be used in a generic fashion.
If the hidden input field has a valid value, set that as the URL hash (window.location.hash again) and/or perform other actions.
We used jQuery to simplify the selecting of the field, etc ... all in all it ends up being a few jQuery calls, one to save the value, and another to restore it.
Before submit:
$("form").submit(function() {
$("input", "#urlhash").val(window.location.hash);
});
On page load:
var hashVal = $("input", "#urlhash").val();
if (IsHashValid(hashVal)) {
window.location.hash = hashVal;
}
IsHashValid() can check for "undefined" or other things you don't want to handle.
Also, make sure you use $(document).ready() appropriately, of course.
[RFC 2396][1] section 4.1:
When a URI reference is used to perform a retrieval action on the
identified resource, the optional fragment identifier, separated from
the URI by a crosshatch ("#") character, consists of additional
reference information to be interpreted by the user agent after the
retrieval action has been successfully completed. As such, it is not
part of a URI, but is often used in conjunction with a URI.
(emphasis added)
[1]: https://www.rfc-editor.org/rfc/rfc2396#section-4
That's because the browser doesn't transmit that part to the server, sorry.
Probably the only choice is to read it on the client side and transfer it manually to the server (GET/POST/AJAX).
Regards
Artur
You may see also how to play with back button and browser history
at Malcan
Just to rule out the possibility you aren't actually trying to see the fragment on a GET/POST and actually want to know how to access that part of a URI object you have within your server-side code, it is under Uri.Fragment (MSDN docs).
Possible solution for GET requests:
New Link format: http://example.com/yourDirectory?hash=video01
Call this function toward top of controller or http://example.com/yourDirectory/index.php:
function redirect()
{
if (!empty($_GET['hash'])) {
/** Sanitize & Validate $_GET['hash']
If valid return string
If invalid: return empty or false
******************************************************/
$validHash = sanitizeAndValidateHashFunction($_GET['hash']);
if (!empty($validHash)) {
$url = './#' . $validHash;
} else {
$url = '/your404page.php';
}
header("Location: $url");
}
}

Resources