Comments

Wim Leers created an issue. See original summary.

wim leers’s picture

Status: Postponed (maintainer needs more info) » Active

Oops.

larowlan’s picture

Wouldn't this constitute an api break? Consumers would be catching the existing exception only

wim leers’s picture

It's still a 4xx response. So this will still end up in the general "error response handling" bucket.

We've changed many of our error responses (status codes, but especially the bodies), and need to, to provide sensible feedback. I think there's no way around that.

dawehner’s picture

wim leers’s picture

Title: EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException » EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400
Issue summary: View changes
larowlan’s picture

It's still a 4xx response. So this will still end up in the general "error response handling" bucket.

We've changed many of our error responses (status codes, but especially the bodies), and need to, to provide sensible feedback. I think there's no way around that.

I'm not talking about http error handling, there are other ways in which the serializer is used outside the REST controllers.

I'm talking about something like this:

try {
  $this->serializer->denormalize($data, $class, 'hal_json');
} 
catch (UnexpectedValueException $e) {
  // Do something.
}

With this proposed change, all of a sudden you've got uncaught exceptions.

dawehner’s picture

Status: Active » Needs review
StatusFileSize
new8.27 KB

I totally believe that semantically 422 is the right exception, see https://tools.ietf.org/html/rfc4918#page-78

   The 422 (Unprocessable Entity) status code means the server
   understands the content type of the request entity (hence a
   415(Unsupported Media Type) status code is inappropriate), and the
   syntax of the request entity is correct (thus a 400 (Bad Request)
   status code is inappropriate) but was unable to process the contained
   instructions.  For example, this error condition may occur if an XML
   request body contains well-formed (i.e., syntactically correct), but
   semantically erroneous, XML instructions.

What about introducing a new exception subclassing the existing one, which indicates that kind of semantic meaning and use it?

Status: Needs review » Needs work

The last submitted patch, 8: 2827084-8.patch, failed testing.

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new10.89 KB
new2.63 KB

Let's fix the remaining failures ...

wim leers’s picture

#7: ahhh!

@dawehner Well, @larowlan is right that this change would break BC for code that does this:

try {
  $this->serializer->denormalize($data, $class, 'hal_json');
} 
catch (UnexpectedValueException $e) {
  // Do something.
}

I think this means we want to wrap every (de)normalize() call and every (de)serialize() call in a try/catch and convert any exception to a \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException?

dawehner’s picture

@Wim Leers
Did you had a look at what the patch is actually doing?

larowlan’s picture

Status: Needs review » Reviewed & tested by the community

perfect

wim leers’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/modules/rest/src/RequestHandler.php
@@ -107,9 +108,10 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
         catch (UnexpectedValueException $e) {
+          $status_code = $e instanceof UnprocessableEntityException ? 422 : 400;
           $error['error'] = $e->getMessage();
           $content = $serializer->serialize($error, $format);
-          return new Response($content, 400, array('Content-Type' => $request->getMimeType($format)));
+          return new Response($content, $status_code, array('Content-Type' => $request->getMimeType($format)));
         }
       }

This will change again in #2813853: RequestHandler has its own error handling rather than leaving it to KernelEvents::EXCEPTION event subscribers, but that's not really a problem.

My question is simply this: what if we just always convert a caught UnexpectedValueException here to a UnprocessableEntityHttpException?

(That's what I'm getting at in #11.)

In other words:

  1. what's the value of changing all these normalizers and adding this slight variation of UnexpectedValueException? Especially if every single normalizer out there (including contrib ones) will be using UnexpectedValueException.
  2. isn't sending a 422 response for any exception during denormalizing appropriate?
wim leers’s picture

dawehner’s picture

what's the value of changing all these normalizers and adding this slight variation of UnexpectedValueException? Especially if every single normalizer out there (including contrib ones) will be using UnexpectedValueException.

Well, the usecase of the serializer is more than just REST. Given that, exceptions of the HTTP layer should not sneak into the serializer level, and the other way round.
Keep the exception in the serializer namespace, allows you to add additional semantic meaning, while both not breaking the API (see #7), and making it useful for others.
This way we could distinct between the 400 and 422 case properly.

wim leers’s picture

That's my point exactly! What I'm basically proposing is this:

  public function handle(RouteMatchInterface $route_match, Request $request) {
    …
        catch (UnexpectedValueException $e) {
          throw new UnprocessableEntityHttpException($e->getMessage());
        }
    …

That'd be even less disruptive, and would achieve the same AFAICT.

Am I missing something?

dawehner’s picture

Well, this exception is also thrown when the content is not decodeable in the first place for example. That one though should be a 400 instead of the 422

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

OMG, what a design fail in Symfony's serializer :/

Just to confirm what Daniel is saying: see \Symfony\Component\Serializer\Encoder\JsonDecode::decode(), \Symfony\Component\Serializer\Encoder\JsonEncode::encode(), \Symfony\Component\Serializer\Encoder\XmlEncoder::buildXml() and \Symfony\Component\Serializer\Encoder\XmlEncoder::decode(): they all throw \Symfony\Component\Serializer\Exception\UnexpectedValueException.

This means that the Symfony serializer's default exceptions do not allow us to distinguish between exceptions that should cause 400 exceptions and 422 exceptions.

Hence the approach in #10 presents the only possible solution: a new exception that extends the existing one, and defines additional semantic meaning. That way, all existing code continues to work. And code that wants to be aware of exceptions with the semantic meaning of a HTTP 422 response, can do so.

This is that new exception:

+++ b/core/modules/serialization/src/Exception/UnprocessableEntityException.php
@@ -0,0 +1,16 @@
+/**
+ * Exception to indicate a syntactic right but semantically wrong request.
+ *
+ * This exception might be converted to a HTTP 422 response.
+ *
+ * @see https://tools.ietf.org/html/rfc4918#page-78
+ */
+class UnprocessableEntityException extends UnexpectedValueException {
wim leers’s picture

Sorry for holding this up, and thanks for the extra clarifications. Now this patch totally makes sense!

wim leers’s picture

Issue tags: +SymfonyWTF

OMG, what a design fail in Symfony's serializer :/

The right thing to do is to +1 any relevant issues. But all of these issue searches result in zero matches:

And I also browsed Symfony's Serializer component docs. Turns out they're at least as bad, if not worse than Drupal's. No useful information regarding all this there either. That's how I stumbled upon https://api-platform.com/ though, which apparently is a Symfony bundle providing functionality similar to rest.module in Drupal 8. It's about a year old. So rest.module predates it by years. Surprisingly, zero mentions of 422 responses there too:
https://github.com/api-platform/core/search?utf8=%E2%9C%93&q=Unprocessab... or https://github.com/api-platform/core/search?utf8=%E2%9C%93&q=422.

So, created a new issue: https://github.com/symfony/symfony/issues/20534.

wim leers’s picture

While doing the research for #21, found a problem:

  1. +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
    @@ -234,7 +235,7 @@ protected function getTypedDataIds($types, $context = array()) {
    -        throw new UnexpectedValueException('Type must contain an \'href\' attribute.');
    +        throw new UnprocessableEntityException('Type must contain an \'href\' attribute.');
    
    @@ -247,7 +248,7 @@ protected function getTypedDataIds($types, $context = array()) {
    -      throw new UnexpectedValueException(sprintf('Type %s does not correspond to an entity on this site.', $type_uri));
    +      throw new UnprocessableEntityException(sprintf('Type %s does not correspond to an entity on this site.', $type_uri));
    

    These, and many like it, make sense. No BC break. Because the exception is being replaced by a subclassed exception.

  2. +++ b/core/modules/hal/src/Normalizer/FieldItemNormalizer.php
    @@ -41,10 +42,10 @@ public function normalize($field_item, $format = NULL, array $context = array())
    -      throw new InvalidArgumentException('$context[\'target_instance\'] must be set to denormalize with the FieldItemNormalizer');
    +      throw new UnprocessableEntityException('$context[\'target_instance\'] must be set to denormalize with the FieldItemNormalizer');
    ...
    -      throw new InvalidArgumentException('The field item passed in via $context[\'target_instance\'] must have a parent set.');
    +      throw new UnprocessableEntityException('The field item passed in via $context[\'target_instance\'] must have a parent set.');
    
    +++ b/core/modules/hal/src/Normalizer/FieldNormalizer.php
    @@ -57,10 +58,10 @@ public function normalize($field, $format = NULL, array $context = array()) {
    -      throw new InvalidArgumentException('$context[\'target_instance\'] must be set to denormalize with the FieldNormalizer');
    +      throw new UnprocessableEntityException('$context[\'target_instance\'] must be set to denormalize with the FieldNormalizer');
    ...
    -      throw new InvalidArgumentException('The field passed in via $context[\'target_instance\'] must have a parent set.');
    +      throw new UnprocessableEntityException('The field passed in via $context[\'target_instance\'] must have a parent set.');
    

    But these do break BC… :(

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 10: 2827084-10.patch, failed testing.

dawehner’s picture

Well the ones in 2) sound like a bug for me. They seem to throw the wrong exceptions?

Mixologic’s picture

Status: Needs work » Reviewed & tested by the community
catch’s picture

Issue tags: +Needs change record

I agree those two exceptions look like a bug, also no-one should be catching InvalidArgumentException?

This issue should have a change notice though - both for the different HTTP response code and the new exception.

dawehner’s picture

Issue tags: -Needs change record

Change record is written: https://www.drupal.org/node/2828773

catch’s picture

Version: 8.2.x-dev » 8.3.x-dev
Status: Reviewed & tested by the community » Needs work

Needs a re-roll for 8.3.x

Also given the new exception, I'd prefer to just put this in 8.3.x anyway.

rosinegrean’s picture

Assigned: Unassigned » rosinegrean
rosinegrean’s picture

Assigned: rosinegrean » Unassigned
Status: Needs work » Needs review
StatusFileSize
new10.95 KB

Re rolled patch.

Anything else to do here ?

Status: Needs review » Needs work

The last submitted patch, 30: 2827084-30.patch, failed testing.

rosinegrean’s picture

Status: Needs work » Needs review
dawehner’s picture

@prics
Well, we would have to agree whether its worth doing this change for 8.2.x itself. I'm not sure whether the slightly better HTTP status code is worth the BC thing. This issue is marked as task, and well, the current major should always just retrieve bug fixes, so I think not committing this to 8.2.x is the way to go.

damiankloip’s picture

I am not sure why there is an expectation for the serializer to return exceptions that relate to HTTP status codes. The serializer is not the REST module, and it isn't governed by REST implementations. Shouldn't REST module just be returning a 422 if it catches these exceptions? As noted above somewhere, the serialization component upstream also uses these exceptions, which I think it correct - as serialization is not really tied to the HTTP protocol. I would say that the REST module should catch these exceptions as they are and throw the correct HttpException that will return a 422 response.

+++ b/core/modules/rest/src/RequestHandler.php
@@ -107,9 +108,10 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
         catch (UnexpectedValueException $e) {
+          $status_code = $e instanceof UnprocessableEntityException ? 422 : 400;
           $error['error'] = $e->getMessage();
           $content = $serializer->serialize($error, $format);
-          return new Response($content, 400, array('Content-Type' => $request->getMimeType($format)));
+          return new Response($content, $status_code, array('Content-Type' => $request->getMimeType($format)));
         }

IMO, this is the only part of the code that this patch should really be making changes to.

dawehner’s picture

@damiankloip
Well, do you know a way how the serializer can communicate: this is not valid JSON/XML? This is basically the question we ask here.

damiankloip’s picture

It does not, but I think the solution to that is to separate how the serializer is called in RequestHnadler maybe? So you would first decode(), then denormalize(), and check each step.

damiankloip’s picture

damiankloip’s picture

StatusFileSize
new3.06 KB

I.e something a bit like this?

dawehner’s picture

That sounds like a good alternative approach! Note: We should still adapt all the tests ...

darrenwh’s picture

+++ b/core/modules/rest/src/RequestHandler.php
@@ -96,20 +97,39 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
+          // If an exceptin was thrown at this stage, there was a problem

Hi, Quite trivial, but Exception is spelt incorrectly here

Status: Needs review » Needs work

The last submitted patch, 38: 2827084-38.patch, failed testing.

damiankloip’s picture

Status: Needs work » Needs review
StatusFileSize
new5.86 KB
new3.59 KB

Yeah, let's try and get those resource tests passing now. The fails show that this patch is working though, I think. Also fixed the 'exceptin' typo :)

Status: Needs review » Needs work

The last submitted patch, 42: 2827084-40.patch, failed testing.

dawehner’s picture

@damiankloip
That's an interesting approach!

wim leers’s picture

I first had reservations about putting more logic in RequestHandler. But it's done for a good reason: to convert an exception to a 400 error response when decoding doesn't work, and to convert to a 422 response when denormalizing doesn't work. This makes a lot of sense.

Great insight, @damiankloip! :)

+++ b/core/modules/rest/src/RequestHandler.php
@@ -96,20 +97,39 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
+        // First decode the request data. We can then determine if the
+        // serialized data was malformed.
         try {
-          if (!empty($definition['serialization_class'])) {
-            $unserialized = $serializer->deserialize($received, $definition['serialization_class'], $format, array('request_method' => $method));
-          }
-          // If the plugin does not specify a serialization class just decode
-          // the received data.
-          else {
-            $unserialized = $serializer->decode($received, $format, array('request_method' => $method));
-          }
+          $unserialized = $serializer->decode($received, $format, ['request_method' => $method]);
         }
         catch (UnexpectedValueException $e) {
-          $error['error'] = $e->getMessage();
+          // If an exception was thrown at this stage, there was a problem
+          // decoding the data. Return a 400 response.
+          $error = ['error' => $e->getMessage()];
           $content = $serializer->serialize($error, $format);
-          return new Response($content, 400, array('Content-Type' => $request->getMimeType($format)));
+
+          return new Response($content, 400, ['Content-Type' => $request->getMimeType($format)]);
+        }
+
+        // Then attempt to denormalize if there is a serialization class.
+        if (!empty($definition['serialization_class'])) {
+          try {
+            $unserialized = $serializer->denormalize($unserialized, $definition['serialization_class'], $format, ['request_method' => $method]);
+          }
+          catch (\Exception $e) {
+            if ($e instanceof UnexpectedValueException || $e instanceof InvalidArgumentException) {
+              // If one of the above exceptions was thrown at this stage, there
+              // was a problem with the structure of the decoded data and it's
+              // not valid.
+              $error = ['error' => $e->getMessage()];
+              $content = $serializer->serialize($error, $format);
+              return new Response($content, 422, ['Content-Type' => $request->getMimeType($format)]);
+            }
+
+            // Otherwise, re-throw the exception.
+            throw $e;
+          }
         }

Let's put the exception handling logic in a helper method that we pass either 400 or 422. That will make this code less repetitive and more legible.

damiankloip’s picture

Thanks Daniel, and Wim. A helper sounds good!

damiankloip’s picture

Status: Needs work » Needs review
StatusFileSize
new7.84 KB
new3.7 KB

Status: Needs review » Needs work

The last submitted patch, 47: 2827084-47.patch, failed testing.

damiankloip’s picture

Status: Needs work » Needs review

Thought so!

wim leers’s picture

  1. +++ b/core/modules/rest/src/RequestHandler.php
    @@ -96,20 +97,34 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
             catch (UnexpectedValueException $e) {
    ...
    +          catch (\Exception $e) {
    +            if ($e instanceof UnexpectedValueException || $e instanceof InvalidArgumentException) {
    ...
    +            // Otherwise, re-throw the exception.
    +            throw $e;
    

    Why should these not also have a similar structure?

  2. +++ b/core/modules/rest/src/RequestHandler.php
    @@ -139,4 +154,23 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
    +   * Creates a format serialized error response.
    

    Something's wrong in this sentence.

  3. +++ b/core/modules/rest/src/RequestHandler.php
    @@ -139,4 +154,23 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
    +   * @param \Exception $e
    +   * @param int $status_code
    +   * @param \Symfony\Component\HttpFoundation\Request $request
    +   * @param string $format
    +   *
    +   * @return \Symfony\Component\HttpFoundation\Response
    

    Missing doc lines.

  4. +++ b/core/modules/rest/src/RequestHandler.php
    @@ -139,4 +154,23 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
    +    $error = ['error' => $e->getMessage()];
    

    This is consistent with HEAD, but is also a problem, because everywhere else we have ['messsage' => …].

    However, we have #2813853: RequestHandler has its own error handling rather than leaving it to KernelEvents::EXCEPTION event subscribers to fix this, and AFAICT doing that will stay equally simple. In fact, in #2813853 we should be able to remove this helper method altogether and throw a 400/422 exception in those catch statements above.

damiankloip’s picture

StatusFileSize
new8.01 KB
new836 bytes

1. My thinking was if it's something completely different, we should not send out usual REST response and just let the system handle it. That exception could catch anything. If we catch everything, then do we should default to a 500 response maybe? It's hard to know in a generic way.
2. Yes, that wasn't my best attempt at my native language :)
3. Fixed
4. I'll leave this for now then? I guess we would just throw the appropriate HttpException and be done. It can then we converted to the correct format etc... in the exception subscriber.

wim leers’s picture

  1. I'm not sure I fully understand this sentence/reasoning. Put more simply: why do we need to catch everything for denormalizing, but not for decoding? Why can't we just not do that? Oh wait, because we have two types of exceptions we need to catch for the denormalizing case, and only one for the decoding case! Gah, this is messy. Because… isn't it entirely possible that there are yet other exceptions (other than UnexpectedValueException and InvalidArgumentException) that denormalizers might throw?
  1. Yes, leave it for now. This was just an "FYI", but also a "please confirm that you think it's A) still possible, B) that that makes sense".

    I guess we would just throw the appropriate HttpException and be done. It can then we converted to the correct format etc... in the exception subscriber.

    Exactly. That's what #2813853: RequestHandler has its own error handling rather than leaving it to KernelEvents::EXCEPTION event subscribers is all about :)

damiankloip’s picture

StatusFileSize
new8.27 KB
new3.06 KB

1. Yes, I see what you mean now. This just avoids having another catch block, but doesn't really make the code any cleaner, just further nesting. I'll split it. I am not sure we whether we should use the same responses/exceptions for the generic exception, we would have to default the status code to 500 maybe? There is no real way to reliably determine this.
4. Yes, I agree it is sensible and totally doable. I have changed this patch to throw HttpExceptions instead. It currently requires the boilerplate for the body serialization and the headers, but we could remove that in #2813853: RequestHandler has its own error handling rather than leaving it to KernelEvents::EXCEPTION event subscribers.

damiankloip’s picture

+++ b/core/modules/rest/src/RequestHandler.php
@@ -105,8 +106,8 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
+          return $this->throwHttpException(400, $e, $request);

Meant to remove these return statements too.

dawehner’s picture

  1. +++ b/core/modules/rest/src/RequestHandler.php
    @@ -96,20 +98,34 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
    +          catch (\Exception $e) {
    +            if ($e instanceof UnexpectedValueException || $e instanceof InvalidArgumentException) {
    +              // If one of the above exceptions was thrown at this stage, there
    +              // was a problem with the structure of the decoded data and it's
    +              // not valid.
    +              return $this->throwHttpException(422, $e, $request);
    +            }
    

    Could we use multiple catch statements, see https://3v4l.org/HQ7pZ ?

  2. +++ b/core/modules/rest/src/RequestHandler.php
    @@ -139,4 +155,29 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
    +   * @return \Symfony\Component\HttpFoundation\Response
    ...
    +    throw new HttpException($status_code, $content, $e, ['Content-Type' => $request->getMimeType($format)]);
    

    This method throws an exception, instaed of returnign something

damiankloip’s picture

Yeah, we discussed just using multiple catch statements instead but we are still not sure whether we want to catch everything and assume it's a 422 at that stage (denormalization) or just catch the ones we care about like we are now?

dawehner’s picture

(denormalization) or just catch the ones we care about like we are now?

That is a tricky question indeed. For me UnexpectedValueException|InvalidArgumentException kind of cuts it. If you throw a random different exception it might be caused by something totally else, not caused by the unprocessable entity. Just imagine if the DB connection stops or anything like that, in this case throwing a 422 would be kind of wrong, wouldn't it?

damiankloip’s picture

Yeah, that was basically my thoughts on using those two things originally!

damiankloip’s picture

StatusFileSize
new8.23 KB
new2.75 KB
wim leers’s picture

Agreed! But how do we know there's not additional exceptions that may be thrown that should signify a 422?

AFAICT this is a huge oversight on Symfony's side: the serializer component should have its own exception base interface for this reason.

dawehner’s picture

Agreed! But how do we know there's not additional exceptions that may be thrown that should signify a 422?

Well, if they should signify, they should be one of those exceptions, indeed. I think though there is not really anything we can help with.

Alternative serialization systems like https://github.com/schmittjoh/serializer/tree/master/src/JMS/Serializer/... seems to deal with that problem at least.
I believe proposing a similar variant upstream could help. There was just one response, maybe others might be more open to that idea.

The last submitted patch, 53: 2827084-53.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 59: 2827084-59.patch, failed testing.

damiankloip’s picture

I think we need to just catch these two types for now, otherwise we are not really sure what the problem should be. We could get additional exception that signify 422, but we could also get additional exceptions that are nothing to do with that. I would much prefer to use that serializer! I always have but we already went with the Symfony one because we brought into Symfony in general. That serializer has events and everything.

wim leers’s picture

Title: EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400 » [PP-1] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400

The failing tests here look like this:

1) Drupal\Tests\hal\Functional\EntityResource\Comment\CommentHalJsonAnonTest::testPost
Failed asserting that Array &0 (
    0 => 'application/json'
) is identical to Array &0 (
    0 => 'application/hal+json'
).

/var/www/html/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:536

2) Drupal\Tests\hal\Functional\EntityResource\Comment\CommentHalJsonAnonTest::testPatch
Failed asserting that Array &0 (
    0 => 'application/json'
) is identical to Array &0 (
    0 => 'application/hal+json'
).

That's being fixed in #2805281: ?_format=hal_json error responses are application/json, yet should be application/hal+json, which is blocked on #2739617: Make it easier to write on4xx() exception subscribers, which is RTBC :)

wim leers’s picture

Title: [PP-1] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400 » [PP-2] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400

It's blocked on two issues, not one.

dawehner’s picture

Status: Needs work » Postponed
wim leers’s picture

Title: [PP-2] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400 » [PP-1] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400

And #2739617: Make it easier to write on4xx() exception subscribers, so now it's only blocked on one issue anymore.

wim leers’s picture

Status: Postponed » Needs review
damiankloip’s picture

Noted in IRC too, it seems that there is some overlap between the direction this headed in and #2813853: RequestHandler has its own error handling rather than leaving it to KernelEvents::EXCEPTION event subscribers. It seems like that issue is kind of redundant now we have this one. This one throws the exceptions directly already and has the improved status codes. I think I just referenced the wrong issue in the latest patch. Removing the exception throwing helper method was more dependent on the changes to improved exception responses?

wim leers’s picture

Title: [PP-1] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400 » EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400
wim leers’s picture

Oh, darn, I see what you mean, @damiankloip!

  • Both this patch and the one at #2813853 indeed touch all the hunks that contain // @todo … https://www.drupal.org/node/2813853.
  • Both this patch and the one at #2813853 touch RequestHandler::handle()

But…

  1. this patch doesn't fix the key thing that #2813853 fixes: changing the error key to message
  2. this patch would be a lot simpler of #2813853 landed first: then it could fix exactly what it was meant to fix: the 400 and 422 responses. It'd only need to make changes to RequestHandler::handle() and then do s/400/422/ in a few places in the tests.

So in other words: the concern of #2813853 is error -> message, the concern of this issue is 400 -> 422.


Which made me realize:

  1. that this issue summary needs a massive overhaul: it says almost nothing right now! A committer would have a very hard time committing this. I can take that on.
  2. that we perhaps want explicit test coverage for this? I'm not sure though. What do you think?
wim leers’s picture

StatusFileSize
new8.46 KB

This is a straight reroll of #59. Let's see how it does now.

Status: Needs review » Needs work

The last submitted patch, 73: 2827084-73.patch, failed testing.

damiankloip’s picture

Title: EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400 » [PP-1] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400
wim leers’s picture

Status: Needs work » Postponed

Per #75.

wim leers’s picture

Title: [PP-1] EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400 » EntityNormalizer::denormalize should not throw UnexpectedValueException, but \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException, so we get a 422 response instead of 400
Status: Postponed » Reviewed & tested by the community
StatusFileSize
new8.37 KB
new6.5 KB

#2813853: RequestHandler has its own error handling rather than leaving it to KernelEvents::EXCEPTION event subscribers landed, this is now unblocked! :)

Straight reroll of #73, with conflicts resolved.

Back to RTBC, all of the actual work here was done by @damiankloip — see #59 and earlier.

wim leers’s picture

Comparing #73 and #77 makes it clear that it was a good idea to wait for #2813853: RequestHandler has its own error handling rather than leaving it to KernelEvents::EXCEPTION event subscribers: the patch now is trivial. :)

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Status: Reviewed & tested by the community » Needs work

This no longer applies, apparently. The issue was not automatically marked needs work because I had to queue with the 8.4.x run manually. Thanks!

wim leers’s picture

wim leers’s picture

Status: Needs work » Reviewed & tested by the community
damiankloip’s picture

Looks good, Thanks Wim!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed d2fdd97 and pushed to 8.4.x. Thanks!

alexpott’s picture

Issue tags: +8.4.0 release notes
damiankloip’s picture

Did something happen to this commit? or not get pushed or something?

  • alexpott committed d2fdd97 on 8.4.x
    Issue #2827084 by damiankloip, Wim Leers, dawehner, prics, larowlan,...
wim leers’s picture

Ahh there it is :)

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

wim leers’s picture

Per #28, this didn't make it into 8.2.x. But sadly, this also didn't make 8.3.x. Alas.