Ensure `skip_defaults` doesn't cause extra fields to be serialized
diff --git a/fastapi/routing.py b/fastapi/routing.py
index 930cbe001ff1d..08f43bf524a8d 100644
--- a/fastapi/routing.py
+++ b/fastapi/routing.py
@@ -45,6 +45,8 @@ def serialize_response(
 ) -> Any:
     if field:
         errors = []
+        if skip_defaults and isinstance(response, BaseModel):
+            response = response.dict(skip_defaults=skip_defaults)
         value, errors_ = field.validate(response, {}, loc=("response",))
         if isinstance(errors_, ErrorWrapper):
             errors.append(errors_)
@@ -52,8 +54,6 @@ def serialize_response(
             errors.extend(errors_)
         if errors:
             raise ValidationError(errors)
-        if skip_defaults and isinstance(response, BaseModel):
-            value = response.dict(skip_defaults=skip_defaults)
         return jsonable_encoder(
             value,
             include=include,
diff --git a/tests/test_skip_defaults.py b/tests/test_skip_defaults.py
index 8579b50ea560a..9997ba7b2c4b1 100644
--- a/tests/test_skip_defaults.py
+++ b/tests/test_skip_defaults.py
@@ -16,9 +16,13 @@ class Model(BaseModel):
     sub: SubModel
 
 
+class ModelSubclass(Model):
+    y: int
+
+
 @app.get("/", response_model=Model, response_model_skip_defaults=True)
-def get() -> Model:
-    return Model(sub={})
+def get() -> ModelSubclass:
+    return ModelSubclass(sub={}, y=1)
 
 
 client = TestClient(app)

Currently, if `skip_defaults` is true, the secure cloned field is not used when serializing the response. This can lead to extra information leaking out if the `response_model` differs in type from the returned model.

This pull request fixes this bug, and updates the relevant unit test to check for it.

(The bug was introduced in #422 -- sorry about that!)

-----

I believe this pull request also improves performance in the case where `skip_defaults` is `True`, as the response will now only be validated once in the `serialize_response` call, instead of twice.
