diff --git a/examples/Error/Client/Client.csproj b/examples/Error/Client/Client.csproj new file mode 100644 index 000000000..6b5421801 --- /dev/null +++ b/examples/Error/Client/Client.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + + + + + + + + + + + + + + diff --git a/examples/Error/Client/Program.cs b/examples/Error/Client/Program.cs new file mode 100644 index 000000000..3021039b3 --- /dev/null +++ b/examples/Error/Client/Program.cs @@ -0,0 +1,57 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using Google.Rpc; +using Greet; +using Grpc.Core; +using Grpc.Net.Client; +using Grpc.StatusProto; + +using var channel = GrpcChannel.ForAddress("https://localhost:5001"); +var client = new Greeter.GreeterClient(channel); + +Console.WriteLine("Hello world app"); +Console.WriteLine("==============="); + +while (true) +{ + Console.WriteLine(); + + Console.Write("Enter name: "); + var name = Console.ReadLine(); + + try + { + var reply = await client.SayHelloAsync(new HelloRequest { Name = name }); + Console.WriteLine("Greeting: " + reply.Message); + } + catch (RpcException ex) + { + Console.WriteLine($"Server error: {ex.Status.Detail}"); + + var badRequest = ex.GetRpcStatus()?.GetStatusDetail(); + if (badRequest != null) + { + foreach (var fieldViolation in badRequest.FieldViolations) + { + Console.WriteLine($"Field: {fieldViolation.Field}"); + Console.WriteLine($"Description: {fieldViolation.Description}"); + } + } + } +} diff --git a/examples/Error/Error.sln b/examples/Error/Error.sln new file mode 100644 index 000000000..c15fc0471 --- /dev/null +++ b/examples/Error/Error.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34004.107 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server", "Server\Server.csproj", "{534AC5F8-2DF2-40BD-87A5-B3D8310118C4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "Client\Client.csproj", "{48A1D3BC-A14B-436A-8822-6DE2BEF8B747}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grpc.StatusProto", "..\..\src\Grpc.StatusProto\Grpc.StatusProto.csproj", "{08BEBE52-D94B-449B-A5B5-9ABAABDE7CB2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grpc.Core.Api", "..\..\src\Grpc.Core.Api\Grpc.Core.Api.csproj", "{E00E8BDE-19A9-47D7-B2D8-B304B2DEBAF3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grpc.Net.Client", "..\..\src\Grpc.Net.Client\Grpc.Net.Client.csproj", "{E2C141A0-E21F-4F0F-9E32-FD90C57E8AD0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grpc.Net.Common", "..\..\src\Grpc.Net.Common\Grpc.Net.Common.csproj", "{23678E37-37D4-4543-9961-403B3760862B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grpc.AspNetCore.Server", "..\..\src\Grpc.AspNetCore.Server\Grpc.AspNetCore.Server.csproj", "{A1368174-6D27-49F1-B6DC-F1868CAE000F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {534AC5F8-2DF2-40BD-87A5-B3D8310118C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {534AC5F8-2DF2-40BD-87A5-B3D8310118C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {534AC5F8-2DF2-40BD-87A5-B3D8310118C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {534AC5F8-2DF2-40BD-87A5-B3D8310118C4}.Release|Any CPU.Build.0 = Release|Any CPU + {48A1D3BC-A14B-436A-8822-6DE2BEF8B747}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {48A1D3BC-A14B-436A-8822-6DE2BEF8B747}.Debug|Any CPU.Build.0 = Debug|Any CPU + {48A1D3BC-A14B-436A-8822-6DE2BEF8B747}.Release|Any CPU.ActiveCfg = Release|Any CPU + {48A1D3BC-A14B-436A-8822-6DE2BEF8B747}.Release|Any CPU.Build.0 = Release|Any CPU + {08BEBE52-D94B-449B-A5B5-9ABAABDE7CB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08BEBE52-D94B-449B-A5B5-9ABAABDE7CB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08BEBE52-D94B-449B-A5B5-9ABAABDE7CB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08BEBE52-D94B-449B-A5B5-9ABAABDE7CB2}.Release|Any CPU.Build.0 = Release|Any CPU + {E00E8BDE-19A9-47D7-B2D8-B304B2DEBAF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E00E8BDE-19A9-47D7-B2D8-B304B2DEBAF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E00E8BDE-19A9-47D7-B2D8-B304B2DEBAF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E00E8BDE-19A9-47D7-B2D8-B304B2DEBAF3}.Release|Any CPU.Build.0 = Release|Any CPU + {E2C141A0-E21F-4F0F-9E32-FD90C57E8AD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2C141A0-E21F-4F0F-9E32-FD90C57E8AD0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2C141A0-E21F-4F0F-9E32-FD90C57E8AD0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2C141A0-E21F-4F0F-9E32-FD90C57E8AD0}.Release|Any CPU.Build.0 = Release|Any CPU + {23678E37-37D4-4543-9961-403B3760862B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23678E37-37D4-4543-9961-403B3760862B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23678E37-37D4-4543-9961-403B3760862B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23678E37-37D4-4543-9961-403B3760862B}.Release|Any CPU.Build.0 = Release|Any CPU + {A1368174-6D27-49F1-B6DC-F1868CAE000F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1368174-6D27-49F1-B6DC-F1868CAE000F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1368174-6D27-49F1-B6DC-F1868CAE000F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1368174-6D27-49F1-B6DC-F1868CAE000F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D22B3129-3BFB-41FA-9FCE-E45EBEF8C2DD} + EndGlobalSection +EndGlobal diff --git a/examples/Error/Proto/greet.proto b/examples/Error/Proto/greet.proto new file mode 100644 index 000000000..26d0c794d --- /dev/null +++ b/examples/Error/Proto/greet.proto @@ -0,0 +1,33 @@ +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package greet; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply); +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/examples/Error/Server/Program.cs b/examples/Error/Server/Program.cs new file mode 100644 index 000000000..74aeee754 --- /dev/null +++ b/examples/Error/Server/Program.cs @@ -0,0 +1,27 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using Server; + +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddGrpc(); + +var app = builder.Build(); +app.MapGrpcService(); + +app.Run(); diff --git a/examples/Error/Server/Server.csproj b/examples/Error/Server/Server.csproj new file mode 100644 index 000000000..c35044e3c --- /dev/null +++ b/examples/Error/Server/Server.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + + + + + + + + + + + + + + + diff --git a/examples/Error/Server/Services/GreeterService.cs b/examples/Error/Server/Services/GreeterService.cs new file mode 100644 index 000000000..a9e9adbb3 --- /dev/null +++ b/examples/Error/Server/Services/GreeterService.cs @@ -0,0 +1,63 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using System.Runtime.CompilerServices; +using Google.Protobuf.WellKnownTypes; +using Google.Rpc; +using Greet; +using Grpc.Core; +using Grpc.StatusProto; + +namespace Server +{ + public class GreeterService : Greeter.GreeterBase + { + public override Task SayHello(HelloRequest request, ServerCallContext context) + { + ArgumentNotNullOrEmpty(request.Name); + + return Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); + } + + private static void ArgumentNotNullOrEmpty(string value, [CallerArgumentExpression(nameof(value))] string? paramName = null) + { + if (string.IsNullOrEmpty(value)) + { + throw new Google.Rpc.Status + { + Code = (int)Code.InvalidArgument, + Message = "Bad request", + Details = + { + Any.Pack(new BadRequest + { + FieldViolations = + { + new BadRequest.Types.FieldViolation + { + Field = paramName, + Description = "Value is null or empty" + } + } + }) + } + }.ToRpcException(); + } + } + } +} diff --git a/examples/Error/Server/appsettings.Development.json b/examples/Error/Server/appsettings.Development.json new file mode 100644 index 000000000..fe20c40cc --- /dev/null +++ b/examples/Error/Server/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Grpc": "Information", + "Microsoft": "Information" + } + } +} diff --git a/examples/Error/Server/appsettings.json b/examples/Error/Server/appsettings.json new file mode 100644 index 000000000..f5f63744b --- /dev/null +++ b/examples/Error/Server/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information" + } + }, + "AllowedHosts": "*", + "Kestrel": { + "EndpointDefaults": { + "Protocols": "Http2" + } + } +}