I use API platform with Symfony5, and I created a service to verify a statement if it is correct.
This statement if is correct, I want to change an existing object instead of adding on a POST request.
So, on POST request I created an event with PRE_WRITE events priorities, and this event calls a service who verifies if the statement is correct if is true, I edit an existing object.
All that work correctly without any problem, but the POST request is always for adding a new object, so, I get a new line on the database table.
Is there any solution, to return 200 responses on the edit object?
Try adding a Response to the kernel.view event like:
....
public function yourEvent(Event $event)
{
// do your service stuff
// ...
$event->setResponse(new Response('ok', 200));
}
Maybe you need to use PUT - for changes.
POST- to create a record
Related
I just started working with asp.net mvc and am working on a legacy codebase with controllers that all have the same validation code at the front of every controller. (ie. multiple lines of the exact same code returning if the user id/key are not correct)
Is there any way to separate this out into an external function that can return an HTTP response immediately if validation fails?
Right now I can do something like this:
if (!validate()) {
return forbid("Your un or Password is incorrect");
}
or for a more specific message:
var errorMsg = validate();
if (errorMsg) {
return forbid(errorMsg);
}
but if I'm going to put this at the front of most of my controllers, I'd rather have a one-liner that I takes up as little room as possible. Is there an option for bypassing the rest of the controller function and returning an HTTP response from inside my validate() function? Or is there possibly a more appropriate way to do this?
I'm writing in C# for ASP.NET Web API 2. What I want is a catch-all method that will execute for every single request that comes to my Web API.
If the method returns null, then the original routing should continue, seeking out the correct method. However, if the method returns, say, an HTTPResponseMessage, the server should return that response and not proceed on to normal routing.
The use case would be the ability to handle various scenarios that may impact the entire API. For example: ban a single IP address, block (or whitelist) certain user agents, deal with API call counting (e.g. someone can only make X requests to any API method in Y minutes).
The only way I can imagine to do this right now is to literally include a method call in each and every new method I write for my API. For example,
[HttpGet]
public HttpResponseMessage myNewMethod()
{
// I want to avoid having to do this in every single method.
var check = methodThatEitherReturnsResponseOrNull(Request);
if (check != null) return (HttpResponseMessage)check;
// The method returned null so we go ahead with normal processing.
...
}
Is there some way to accomplish this in routing?
This is what Action Filters are for. These are Attributes that you can place either globally, at the class (Controller), or at the method (Action) levels. These attributes can do preprocessing where you execute some code before your action executes or post processing where you execute code after the action executes.
When using pre processing you have the option to return a result to the caller and not have your method (action) be fired at all. This is good for model validation, authorization checks, etc.
To register a filter globally edit the WebApiConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new YourFilterAttribute()); // add record
// rest of code
}
}
To create a custom attribute inherit from System.Web.Http.Filters.ActionFilterAttribute or you can implement interface System.Web.Http.Filters.IActionFilter or you can implement IAuthorizationFilter/AuthorizationFilterAttribute if you specifically want to allow/deny a request.
It also sounds like you want to create multiple attributes, one for each role like IP filtering or count calling etc. That way it would be more modular instead of one enormous authorization filter.
There are many tutorials out there like this one (chosen at random in my Google search results). I am not going to post code because you did not do so either so I would just be guessing as to what you wanted to do.
My application has two databases with exactly the same schema. Basically, I need to change the DbContext based on what data I'm accessing. Two countries are in one Db and 4 countries in the other. I want the client to decide which context is being used. I tried changing my BreezeWebApiConfig file so that the route looks like this:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{dbName}/{controller}/{action}/{id}",
defaults: new {id=RouteParameter.Optional,dbName="db1"}
);
I added the string to the controller actions:
[HttpGet]
public string Metadata(string dbName="")
{
return _contextProvider.Metadata();
}
And changed the entityManager service Name.
Now when the client spins up, it accesses the corrent metadata action and I get a message:
Error: Metadata query failed for: /breeze/clienthistory/kenya/Metadata. Unable to either parse or import metadata: Type .... already exists in this MetadataStore
When I go to the metadata url from the browser, I get the correct metadata (exactly the same as when I remove the {dbName} segment from the route). If I remove the {dbName} segment from the route I get no error and everything works fine
(I have not started implementing the multiple contexts yet -- I am just trying to make the additional segment work).
Thanks.
I think the problem is that your Breeze client is issuing two separate requests for the same metadata, once under each of the two "serviceNames". Breeze tries to blend them both into the same EntityManager.metadataStore ... and can't because that would mean duplication of EntityType names.
One approach that should work is to begin your application by fetching the metadata immediately upon app start and then adding all the associated "DataServiceNames" to the MetadataStore.
Try something along these lines (pseudo-code):
var manager;
var store = new breeze.MetadataStore();
return store.fetchMetadata(serviceName1)
.then(gotMetadata)
.catch(handleFail);
function gotMetadata() {
// register the existing metadata with each of the other service names
store.addDataService(new breeze.DataService(serviceName2));
... more services as needed ...
manager = new breeze.EntityManager({
dataService: store.getDataService(serviceName1), // service to start
metadataStore: store
});
return true; // return something
}
Alternative
Other approaches to consider don't involve 'db' placeholder in the base URL nor any toying with the Web API routes. Let's assume you stay vanilla in that respect with your basic service name
var serviceName = '/breeze/clienthistory/';
..
For example, you could add an optional parameter to your routes (let's call it db) as needed via a withParameters clause.
Here is a query:
return new breeze.EntityQuery.from('Clients')
.where(...)
.withParameters({db: database1}); // database1 == 'kenya'
.using(manager).execute()
.then(success).catch(failed);
which produces a URL like:
/breeze/clienthistory/Clients/?$filter=...&db=kenya
It makes an implicit first-time-only metadata request that resolves to:
/breeze/clienthistory/Metadata
Your server-side Web API query methods can expect db as an optional parameter:
[HttpGet]
public string Metadata(string db="")
{
... do what is right ...
}
Saves?
I assume that you also want to identify the target database when you save. There are lots of ways you can include that in the save request
in a custom HTTP header via a custom AJAX adapter (you could do this for queries too)
in a query string parameter or hash segment on the saveChanges POST request URL (again via a custom AJAX adapter).
in the tag property of the saveOptions object (easily accessed by the server implementation of SaveChanges)
in the resourceName property of the saveOptions object (see "named save")
You'll want to explore this variety of options on your own to find the best choice for you.
I'm testing the controllers using the crawler, but when I'm posting a form that doesn't generate any errors, it save the form in the database.
How can I prevent him to do so without changing the controller, and without testing something else.
Is there best practice about this kinds of test ?
I tried the rollback, but in the ControllerTest there is no more active transactions
You need to write your own test client class extending Symfony\Bundle\FrameworkBundle\Client.
It's because default client doesn't share connection object between requests (so you can't use transactions outside test client). If you extend test client you can handle transaction by your own.
In your client class you need make static connection object, and override method doRequest() to avoid creating new connection object every time but use our static one instead.
It's well described here:
http://alexandre-salome.fr/blog/Symfony2-Isolation-Of-Tests
When you have your own doRequest method all you need is handle transaction, so you wrap handle() method with begin and rollback. Your doRequest method could look sth like that:
protected function doRequest($request)
{
// here you need create your static connection object if it's doesn't exist yet
// and put it into service container as 'doctrine.dbal.default_connection'
(...)
self::$connection->beginTransaction();
$response = $this->kernel->handle($request);
self::$connection->rollback();
(...)
return $response
}
You can read the documentation of PHPUnit for database testing
http://www.phpunit.de/manual/3.6/en/database.html
You will need setup your database and teardown the changes you made.
If you think that the above is too complicated maybe you are interested in make a mockup of your database layer
http://www.phpunit.de/manual/3.6/en/test-doubles.html
Mockup is create a custom object based in the original object where put your own test controls. Probably in this case you are interested in mockup the Entity Manager of Doctrine
Is there possible get request type in controller? How?
To detect if the request is a master or not requires the use of the RequestStack, which should be injected into your controller. The request stack has 3 useful methods
getCurrentRequest();
getMasterRequest();
getParentRequest();
The getParentRequest() will always return null if the current request is the master.
I was looking for this myself, and it seems it is just passed around, so there doesn't seem to be one single place that knows what it is.
My thought for solving this would be to create a simple kernel.request listener that just adds an attribute to the request. Rough (un-tested) code below:
public function onKernelRequest(GetResponseEvent $event)
{
$event->getRequest()->attributes->set('_request_type', $event->getRequestType());
}
Then in the controller you should be able to do:
$requestType = $this->getRequest()->attributes->get('_request_type');
Again this is untested. You would need to write out the full listener class and add it to the services config file, but other than that I think this will work.
Easy, just call the getMethod() method on your Request object:
$method = $this->get('request')->getMethod();
This will return the HTTP method of the current request, e.g. GET, POST, PUT or DELETE.