We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bug: pydantic validations aren't being handled by DTOs
### Description

In the documentation of Litestar we have:

> When a value fails pydantic validation, the result will be a ValidationException with the extra key set to the pydantic validation errors. Thus, this data will be made available for the API consumers by default.

But when combined with DTO's in a post request, if a pydantic validation fails the exception is not handled and the client receives an InternalServerError (500) instead of a BadRequest (400):


```
    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
pydantic_core._pydantic_core.ValidationError: 1 validation error for User
name
  String should have at most 2 characters [type=string_too_long, input_value='abc', input_type=str]
    For further information visit https://errors.pydantic.dev/2.2/v/string_too_long

Status code: 500
```

If the validation isn't handled by pydantic (or at least it isn't handled **first** by pydantic) it works, for example an Enum validation returns a proper 400 status code because `msgspec` catches it first:

```python
from enum import Enum

class NameEnum(str, Enum):
    a = "A"
    b = "B"

# (replace the User class of the example with this)
class User(BaseModel):
    name: NameEnum
```

Output for incorrect Enum:

```
  File ".../.venv/lib64/python3.11/site-packages/litestar/serialization/msgspec_hooks.py", line 191, in decode_json
    raise SerializationException(str(msgspec_error)) from msgspec_error
litestar.exceptions.base_exceptions.SerializationException: Invalid enum value 'abc' - at `$.name`


...

  File ".../.venv/lib64/python3.11/site-packages/litestar/routes/http.py", line 186, in _get_response_data
    raise ClientException(str(e)) from e
litestar.exceptions.http_exceptions.ClientException: 400: Invalid enum value 'abc' - at `$.name`
```

### URL to code causing the issue

_No response_

### MCVE

```python
from litestar import Litestar, post
from litestar.contrib.pydantic import PydanticDTO
from litestar.testing import TestClient
from pydantic import BaseModel, Field


class User(BaseModel):
    name: str = Field(max_length=2)


UserDTO = PydanticDTO[User]


@post("/user", dto=UserDTO, sync_to_thread=False)
def create_user(data: User) -> User:
    return data


with TestClient(Litestar([create_user], debug=True)) as client:
    response = client.post("/user", json={"name": "abc"})
    print(response.text)
    print(f"Status code: {response.status_code}")
    assert response.status_code == 201
```


### Steps to reproduce

```bash
1. Execute the MCVE
```


### Screenshots

_No response_

### Logs

_No response_

### Litestar Version

2.0.0rc1

### Platform

- [X] Linux
- [ ] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)

<!-- POLAR PLEDGE BADGE START -->
---

## Funding
* If you would like to see an issue prioritized, make a pledge towards it!
* We receive the pledge once the issue is completed & verified

<a href="https://polar.sh/litestar-org/litestar/issues/2190">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/litestar-org/litestar/issues/2190/pledge.svg?darkmode=1">
  <img alt="Fund with Polar" src="https://polar.sh/api/github/litestar-org/litestar/issues/2190/pledge.svg">
</picture>
</a>
<!-- POLAR PLEDGE BADGE END -->

StaticFilesConfig and virtual directories
I'm trying to write a ``FileSystemProtocol`` to load files from the package data using [importlib_resources](https://importlib-resources.readthedocs.io/en/latest/using.html#). But because ``directories`` is defined as ``DirectoryPath``, pydantic checks if the given directories exist in the local filesystem. 

This is not generally true, especially in any kind of virtual filesystem (e.g. a zipped package). I think this condition should be relaxed to support virtual filesystems.

https://github.com/starlite-api/starlite/blob/9bb6dcd57c10a591377cf8e3a537e9292566d5b9/starlite/config/static_files.py#L32

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `litestar/contrib/pydantic/pydantic_dto_factory.py`
Content:
```
1 from __future__ import annotations
2 
3 from dataclasses import replace
4 from typing import TYPE_CHECKING, Collection, Generic, TypeVar
5 
6 from litestar.dto.base_dto import AbstractDTO
7 from litestar.dto.data_structures import DTOFieldDefinition
8 from litestar.dto.field import DTO_FIELD_META_KEY, DTOField
9 from litestar.exceptions import MissingDependencyException
10 from litestar.types.empty import Empty
11 
12 if TYPE_CHECKING:
13     from typing import Generator
14 
15     from litestar.typing import FieldDefinition
16 
17 
18 try:
19     import pydantic
20 
21     if pydantic.VERSION.startswith("2"):
22         from pydantic_core import PydanticUndefined
23     else:  # pragma: no cover
24         from pydantic.fields import Undefined as PydanticUndefined  # type: ignore
25 except ImportError as e:
26     raise MissingDependencyException("pydantic") from e
27 
28 __all__ = ("PydanticDTO",)
29 
30 T = TypeVar("T", bound="pydantic.BaseModel | Collection[pydantic.BaseModel]")
31 
32 
33 class PydanticDTO(AbstractDTO[T], Generic[T]):
34     """Support for domain modelling with Pydantic."""
35 
36     @classmethod
37     def generate_field_definitions(
38         cls, model_type: type[pydantic.BaseModel]
39     ) -> Generator[DTOFieldDefinition, None, None]:
40         model_field_definitions = cls.get_model_type_hints(model_type)
41 
42         if pydantic.VERSION.startswith("1"):  # pragma: no cover
43             model_fields: dict[str, pydantic.fields.FieldInfo] = {k: model_field.field_info for k, model_field in model_type.__fields__.items()}  # type: ignore
44         else:
45             model_fields = dict(model_type.model_fields)
46 
47         for field_name, field_info in model_fields.items():
48             field_definition = model_field_definitions[field_name]
49             dto_field = (field_definition.extra or {}).pop(DTO_FIELD_META_KEY, DTOField())
50 
51             if field_info.default is not PydanticUndefined:
52                 default = field_info.default
53             elif field_definition.is_optional:
54                 default = None
55             else:
56                 default = Empty
57 
58             yield replace(
59                 DTOFieldDefinition.from_field_definition(
60                     field_definition=field_definition,
61                     dto_field=dto_field,
62                     model_name=model_type.__name__,
63                     default_factory=field_info.default_factory
64                     if field_info.default_factory and field_info.default_factory is not PydanticUndefined  # type: ignore[comparison-overlap]
65                     else Empty,
66                 ),
67                 default=default,
68                 name=field_name,
69             )
70 
71     @classmethod
72     def detect_nested_field(cls, field_definition: FieldDefinition) -> bool:
73         return field_definition.is_subclass_of(pydantic.BaseModel)
74
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/litestar/contrib/pydantic/pydantic_dto_factory.py b/litestar/contrib/pydantic/pydantic_dto_factory.py
--- a/litestar/contrib/pydantic/pydantic_dto_factory.py
+++ b/litestar/contrib/pydantic/pydantic_dto_factory.py
@@ -3,20 +3,23 @@
 from dataclasses import replace
 from typing import TYPE_CHECKING, Collection, Generic, TypeVar
 
+from typing_extensions import override
+
 from litestar.dto.base_dto import AbstractDTO
 from litestar.dto.data_structures import DTOFieldDefinition
 from litestar.dto.field import DTO_FIELD_META_KEY, DTOField
-from litestar.exceptions import MissingDependencyException
+from litestar.exceptions import MissingDependencyException, ValidationException
 from litestar.types.empty import Empty
 
 if TYPE_CHECKING:
-    from typing import Generator
+    from typing import Any, Generator
 
     from litestar.typing import FieldDefinition
 
 
 try:
     import pydantic
+    from pydantic import ValidationError
 
     if pydantic.VERSION.startswith("2"):
         from pydantic_core import PydanticUndefined
@@ -33,6 +36,20 @@
 class PydanticDTO(AbstractDTO[T], Generic[T]):
     """Support for domain modelling with Pydantic."""
 
+    @override
+    def decode_builtins(self, value: dict[str, Any]) -> Any:
+        try:
+            return super().decode_builtins(value)
+        except ValidationError as ex:
+            raise ValidationException(extra=ex.errors()) from ex
+
+    @override
+    def decode_bytes(self, value: bytes) -> Any:
+        try:
+            return super().decode_bytes(value)
+        except ValidationError as ex:
+            raise ValidationException(extra=ex.errors()) from ex
+
     @classmethod
     def generate_field_definitions(
         cls, model_type: type[pydantic.BaseModel]

{"golden_diff": "diff --git a/litestar/contrib/pydantic/pydantic_dto_factory.py b/litestar/contrib/pydantic/pydantic_dto_factory.py\n--- a/litestar/contrib/pydantic/pydantic_dto_factory.py\n+++ b/litestar/contrib/pydantic/pydantic_dto_factory.py\n@@ -3,20 +3,23 @@\n from dataclasses import replace\n from typing import TYPE_CHECKING, Collection, Generic, TypeVar\n \n+from typing_extensions import override\n+\n from litestar.dto.base_dto import AbstractDTO\n from litestar.dto.data_structures import DTOFieldDefinition\n from litestar.dto.field import DTO_FIELD_META_KEY, DTOField\n-from litestar.exceptions import MissingDependencyException\n+from litestar.exceptions import MissingDependencyException, ValidationException\n from litestar.types.empty import Empty\n \n if TYPE_CHECKING:\n-    from typing import Generator\n+    from typing import Any, Generator\n \n     from litestar.typing import FieldDefinition\n \n \n try:\n     import pydantic\n+    from pydantic import ValidationError\n \n     if pydantic.VERSION.startswith(\"2\"):\n         from pydantic_core import PydanticUndefined\n@@ -33,6 +36,20 @@\n class PydanticDTO(AbstractDTO[T], Generic[T]):\n     \"\"\"Support for domain modelling with Pydantic.\"\"\"\n \n+    @override\n+    def decode_builtins(self, value: dict[str, Any]) -> Any:\n+        try:\n+            return super().decode_builtins(value)\n+        except ValidationError as ex:\n+            raise ValidationException(extra=ex.errors()) from ex\n+\n+    @override\n+    def decode_bytes(self, value: bytes) -> Any:\n+        try:\n+            return super().decode_bytes(value)\n+        except ValidationError as ex:\n+            raise ValidationException(extra=ex.errors()) from ex\n+\n     @classmethod\n     def generate_field_definitions(\n         cls, model_type: type[pydantic.BaseModel]\n", "issue": "Bug: pydantic validations aren't being handled by DTOs\n### Description\r\n\r\nIn the documentation of Litestar we have:\r\n\r\n> When a value fails pydantic validation, the result will be a ValidationException with the extra key set to the pydantic validation errors. Thus, this data will be made available for the API consumers by default.\r\n\r\nBut when combined with DTO's in a post request, if a pydantic validation fails the exception is not handled and the client receives an InternalServerError (500) instead of a BadRequest (400):\r\n\r\n\r\n```\r\n    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)\r\npydantic_core._pydantic_core.ValidationError: 1 validation error for User\r\nname\r\n  String should have at most 2 characters [type=string_too_long, input_value='abc', input_type=str]\r\n    For further information visit https://errors.pydantic.dev/2.2/v/string_too_long\r\n\r\nStatus code: 500\r\n```\r\n\r\nIf the validation isn't handled by pydantic (or at least it isn't handled **first** by pydantic) it works, for example an Enum validation returns a proper 400 status code because `msgspec` catches it first:\r\n\r\n```python\r\nfrom enum import Enum\r\n\r\nclass NameEnum(str, Enum):\r\n    a = \"A\"\r\n    b = \"B\"\r\n\r\n# (replace the User class of the example with this)\r\nclass User(BaseModel):\r\n    name: NameEnum\r\n```\r\n\r\nOutput for incorrect Enum:\r\n\r\n```\r\n  File \".../.venv/lib64/python3.11/site-packages/litestar/serialization/msgspec_hooks.py\", line 191, in decode_json\r\n    raise SerializationException(str(msgspec_error)) from msgspec_error\r\nlitestar.exceptions.base_exceptions.SerializationException: Invalid enum value 'abc' - at `$.name`\r\n\r\n\r\n...\r\n\r\n  File \".../.venv/lib64/python3.11/site-packages/litestar/routes/http.py\", line 186, in _get_response_data\r\n    raise ClientException(str(e)) from e\r\nlitestar.exceptions.http_exceptions.ClientException: 400: Invalid enum value 'abc' - at `$.name`\r\n```\r\n\r\n### URL to code causing the issue\r\n\r\n_No response_\r\n\r\n### MCVE\r\n\r\n```python\r\nfrom litestar import Litestar, post\r\nfrom litestar.contrib.pydantic import PydanticDTO\r\nfrom litestar.testing import TestClient\r\nfrom pydantic import BaseModel, Field\r\n\r\n\r\nclass User(BaseModel):\r\n    name: str = Field(max_length=2)\r\n\r\n\r\nUserDTO = PydanticDTO[User]\r\n\r\n\r\n@post(\"/user\", dto=UserDTO, sync_to_thread=False)\r\ndef create_user(data: User) -> User:\r\n    return data\r\n\r\n\r\nwith TestClient(Litestar([create_user], debug=True)) as client:\r\n    response = client.post(\"/user\", json={\"name\": \"abc\"})\r\n    print(response.text)\r\n    print(f\"Status code: {response.status_code}\")\r\n    assert response.status_code == 201\r\n```\r\n\r\n\r\n### Steps to reproduce\r\n\r\n```bash\r\n1. Execute the MCVE\r\n```\r\n\r\n\r\n### Screenshots\r\n\r\n_No response_\r\n\r\n### Logs\r\n\r\n_No response_\r\n\r\n### Litestar Version\r\n\r\n2.0.0rc1\r\n\r\n### Platform\r\n\r\n- [X] Linux\r\n- [ ] Mac\r\n- [ ] Windows\r\n- [ ] Other (Please specify in the description above)\r\n\r\n<!-- POLAR PLEDGE BADGE START -->\r\n---\r\n\r\n## Funding\r\n* If you would like to see an issue prioritized, make a pledge towards it!\r\n* We receive the pledge once the issue is completed & verified\r\n\r\n<a href=\"https://polar.sh/litestar-org/litestar/issues/2190\">\r\n<picture>\r\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://polar.sh/api/github/litestar-org/litestar/issues/2190/pledge.svg?darkmode=1\">\r\n  <img alt=\"Fund with Polar\" src=\"https://polar.sh/api/github/litestar-org/litestar/issues/2190/pledge.svg\">\r\n</picture>\r\n</a>\r\n<!-- POLAR PLEDGE BADGE END -->\r\n\nStaticFilesConfig and virtual directories\nI'm trying to write a ``FileSystemProtocol`` to load files from the package data using [importlib_resources](https://importlib-resources.readthedocs.io/en/latest/using.html#). But because ``directories`` is defined as ``DirectoryPath``, pydantic checks if the given directories exist in the local filesystem. \r\n\r\nThis is not generally true, especially in any kind of virtual filesystem (e.g. a zipped package). I think this condition should be relaxed to support virtual filesystems.\r\n\r\nhttps://github.com/starlite-api/starlite/blob/9bb6dcd57c10a591377cf8e3a537e9292566d5b9/starlite/config/static_files.py#L32\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom dataclasses import replace\nfrom typing import TYPE_CHECKING, Collection, Generic, TypeVar\n\nfrom litestar.dto.base_dto import AbstractDTO\nfrom litestar.dto.data_structures import DTOFieldDefinition\nfrom litestar.dto.field import DTO_FIELD_META_KEY, DTOField\nfrom litestar.exceptions import MissingDependencyException\nfrom litestar.types.empty import Empty\n\nif TYPE_CHECKING:\n    from typing import Generator\n\n    from litestar.typing import FieldDefinition\n\n\ntry:\n    import pydantic\n\n    if pydantic.VERSION.startswith(\"2\"):\n        from pydantic_core import PydanticUndefined\n    else:  # pragma: no cover\n        from pydantic.fields import Undefined as PydanticUndefined  # type: ignore\nexcept ImportError as e:\n    raise MissingDependencyException(\"pydantic\") from e\n\n__all__ = (\"PydanticDTO\",)\n\nT = TypeVar(\"T\", bound=\"pydantic.BaseModel | Collection[pydantic.BaseModel]\")\n\n\nclass PydanticDTO(AbstractDTO[T], Generic[T]):\n    \"\"\"Support for domain modelling with Pydantic.\"\"\"\n\n    @classmethod\n    def generate_field_definitions(\n        cls, model_type: type[pydantic.BaseModel]\n    ) -> Generator[DTOFieldDefinition, None, None]:\n        model_field_definitions = cls.get_model_type_hints(model_type)\n\n        if pydantic.VERSION.startswith(\"1\"):  # pragma: no cover\n            model_fields: dict[str, pydantic.fields.FieldInfo] = {k: model_field.field_info for k, model_field in model_type.__fields__.items()}  # type: ignore\n        else:\n            model_fields = dict(model_type.model_fields)\n\n        for field_name, field_info in model_fields.items():\n            field_definition = model_field_definitions[field_name]\n            dto_field = (field_definition.extra or {}).pop(DTO_FIELD_META_KEY, DTOField())\n\n            if field_info.default is not PydanticUndefined:\n                default = field_info.default\n            elif field_definition.is_optional:\n                default = None\n            else:\n                default = Empty\n\n            yield replace(\n                DTOFieldDefinition.from_field_definition(\n                    field_definition=field_definition,\n                    dto_field=dto_field,\n                    model_name=model_type.__name__,\n                    default_factory=field_info.default_factory\n                    if field_info.default_factory and field_info.default_factory is not PydanticUndefined  # type: ignore[comparison-overlap]\n                    else Empty,\n                ),\n                default=default,\n                name=field_name,\n            )\n\n    @classmethod\n    def detect_nested_field(cls, field_definition: FieldDefinition) -> bool:\n        return field_definition.is_subclass_of(pydantic.BaseModel)\n", "path": "litestar/contrib/pydantic/pydantic_dto_factory.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom dataclasses import replace\nfrom typing import TYPE_CHECKING, Collection, Generic, TypeVar\n\nfrom typing_extensions import override\n\nfrom litestar.dto.base_dto import AbstractDTO\nfrom litestar.dto.data_structures import DTOFieldDefinition\nfrom litestar.dto.field import DTO_FIELD_META_KEY, DTOField\nfrom litestar.exceptions import MissingDependencyException, ValidationException\nfrom litestar.types.empty import Empty\n\nif TYPE_CHECKING:\n    from typing import Any, Generator\n\n    from litestar.typing import FieldDefinition\n\n\ntry:\n    import pydantic\n    from pydantic import ValidationError\n\n    if pydantic.VERSION.startswith(\"2\"):\n        from pydantic_core import PydanticUndefined\n    else:  # pragma: no cover\n        from pydantic.fields import Undefined as PydanticUndefined  # type: ignore\nexcept ImportError as e:\n    raise MissingDependencyException(\"pydantic\") from e\n\n__all__ = (\"PydanticDTO\",)\n\nT = TypeVar(\"T\", bound=\"pydantic.BaseModel | Collection[pydantic.BaseModel]\")\n\n\nclass PydanticDTO(AbstractDTO[T], Generic[T]):\n    \"\"\"Support for domain modelling with Pydantic.\"\"\"\n\n    @override\n    def decode_builtins(self, value: dict[str, Any]) -> Any:\n        try:\n            return super().decode_builtins(value)\n        except ValidationError as ex:\n            raise ValidationException(extra=ex.errors()) from ex\n\n    @override\n    def decode_bytes(self, value: bytes) -> Any:\n        try:\n            return super().decode_bytes(value)\n        except ValidationError as ex:\n            raise ValidationException(extra=ex.errors()) from ex\n\n    @classmethod\n    def generate_field_definitions(\n        cls, model_type: type[pydantic.BaseModel]\n    ) -> Generator[DTOFieldDefinition, None, None]:\n        model_field_definitions = cls.get_model_type_hints(model_type)\n\n        if pydantic.VERSION.startswith(\"1\"):  # pragma: no cover\n            model_fields: dict[str, pydantic.fields.FieldInfo] = {k: model_field.field_info for k, model_field in model_type.__fields__.items()}  # type: ignore\n        else:\n            model_fields = dict(model_type.model_fields)\n\n        for field_name, field_info in model_fields.items():\n            field_definition = model_field_definitions[field_name]\n            dto_field = (field_definition.extra or {}).pop(DTO_FIELD_META_KEY, DTOField())\n\n            if field_info.default is not PydanticUndefined:\n                default = field_info.default\n            elif field_definition.is_optional:\n                default = None\n            else:\n                default = Empty\n\n            yield replace(\n                DTOFieldDefinition.from_field_definition(\n                    field_definition=field_definition,\n                    dto_field=dto_field,\n                    model_name=model_type.__name__,\n                    default_factory=field_info.default_factory\n                    if field_info.default_factory and field_info.default_factory is not PydanticUndefined  # type: ignore[comparison-overlap]\n                    else Empty,\n                ),\n                default=default,\n                name=field_name,\n            )\n\n    @classmethod\n    def detect_nested_field(cls, field_definition: FieldDefinition) -> bool:\n        return field_definition.is_subclass_of(pydantic.BaseModel)\n", "path": "litestar/contrib/pydantic/pydantic_dto_factory.py"}]}