gRPC Introduktion
Avidentifiera API via gRPC erbjuder ett högpresterande, typsäkert alternativ till REST för server-till-server-kommunikation. API:et tar emot text, PDF eller DOCX och returnerar en avidentifierad version baserat på regelstyrd behandling av personuppgifter.
Tjänsten RedactionService har två huvudsakliga RPC-metoder:
-
RedactFile– tar emot en PDF eller DOCX (Word) som bytes eller Base64 och returnerar den avidentifierade filen på samma sätt. -
RedactText– tar en textsträng och returnerar den avidentifierade texten.
Endpoint (Native & Web):
https://api.avidentifiera.se:50051
Direkt in, direkt ut – inga känsliga dokument lagras
Precis som med vårt REST-API har vi valt en synkron modell för datasäkerhet och enkelhet. Dokumentet skickas i din begäran, bearbetas helt i minnet och det avidentifierade resultatet skickas tillbaka i samma anrop. Vi lagrar aldrig ditt originaldokument på våra servrar.
För verksamheter som hanterar sekretessreglerade handlingar betyder detta att du skickar ett dokument och får tillbaka en avidentifierad version direkt – utan att vi behöver spara ditt original.
Proto-definition
Du kan alltid hämta den senaste kontraktsdefinitionen (.proto) här:
Klient-setup
För att komma igång behöver du gRPC-verktyg för ditt språk för att generera klientkod från vår .proto-fil. Här är de vanligaste paketen och kommandona.
<!-- Lägg till i din .csproj-fil -->
<ItemGroup>
<PackageReference Include="Google.Api.CommonProtos" Version="2.17.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.17.0" />
<PackageReference Include="Grpc.Net.Client.Web" Version="2.17.0" />
<PackageReference Include="Google.Protobuf" Version="3.33.1" />
<PackageReference Include="Grpc.Tools" Version="2.17.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\redaction.v1.proto" GrpcServices="Client" />
</ItemGroup>
# 1. Installera paket
pip install grpcio grpcio-tools googleapis-common-protos
# 2. Ladda ner .proto-filen och generera kod
# Se till att ha google/rpc/status.proto och google/rpc/bad_request.proto i samma mapp
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. redaction.v1.proto google/rpc/status.proto google/rpc/bad_request.proto
# För native gRPC (backend)
npm install @grpc/grpc-js @grpc/proto-loader google-protobuf
# För gRPC-Web (webbläsare)
npm install grpc-web google-protobuf
Autentisering
Varje anrop måste innehålla en giltig API-nyckel som metadata med nyckeln
authorization och värdet Bearer <din-nyckel>.
Om nyckeln saknas eller är ogiltig returneras statuskoden UNAUTHENTICATED.
var metadata = new Grpc.Core.Metadata { { "Authorization", "Bearer live_xxx..." } };
var response = await client.RedactTextAsync(request, metadata);
metadata = [('authorization', 'Bearer live_xxx...')]
response = stub.RedactText(request, metadata=metadata)
const md = new grpc.Metadata();
md.set('authorization', 'Bearer live_xxx...');
client.RedactText(request, md, (err, resp) => { /* ... */ });
Avidentifiera fil
RPCRedactFile
Skicka en PDF eller DOCX och få tillbaka en avidentifierad version. Filen kan skickas som råa bytes eller som en Base64-kodad sträng.
Request: RedactFileRequest
| Fält | Beskrivning |
|---|---|
fileName |
Filnamn, t.ex. "dokument.pdf". Används för att avgöra filtyp. |
fileBytes |
Filens innehåll som en byte-array. Använd antingen detta fält eller fileBase64. |
fileBase64 |
Filens innehåll som en Base64-kodad sträng. Använd antingen detta fält eller fileBytes. |
options |
Valfri konfiguration. Se Regelkonfiguration. |
Response: RedactFileResponse
| Fält | Beskrivning |
|---|---|
took |
Total bearbetningstid på servern i millisekunder. |
result |
Innehåller den avidentifierade filen som redactedFileBytes eller redactedFileBase64 (beroende på request), samt funna entities.
|
using Google.Protobuf;
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Net.Client.Web;
using Grpc.Redaction.V1;
const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };
var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, new HttpClientHandler());
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpHandler = grpcWebHandler });
var client = new RedactionService.RedactionServiceClient(channel);
var fileBytes = await File.ReadAllBytesAsync("dokument.pdf");
var req = new RedactFileRequest
{
FileName = "dokument.pdf",
FileBytes = ByteString.CopyFrom(fileBytes),
Options = new Options { Entities = { EntityType.Name, EntityType.Address } }
};
var resp = await client.RedactFileAsync(req, metadata);
await File.WriteAllBytesAsync("redacted.pdf", resp.Result.RedactedFileBytes.ToByteArray());
using Google.Protobuf;
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Redaction.V1;
using System.Net;
const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };
var socketsHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true };
var httpClient = new HttpClient(socketsHandler) { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact };
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpClient = httpClient });
var client = new RedactionService.RedactionServiceClient(channel);
var fileBytes = await File.ReadAllBytesAsync("dokument.pdf");
var req = new RedactFileRequest
{
FileName = "dokument.pdf",
FileBytes = ByteString.CopyFrom(fileBytes),
Options = new Options { Entities = { EntityType.Name, EntityType.Address } }
};
var resp = await client.RedactFileAsync(req, metadata);
await File.WriteAllBytesAsync("redacted.pdf", resp.Result.RedactedFileBytes.ToByteArray());
import { RedactionServiceClient } from './generated/RedactionServiceClientPb.js';
import { RedactFileRequest, Options, EntityType } from './generated/redaction_v1_pb.js';
const client = new RedactionServiceClient('https://api.avidentifiera.se:50051');
const req = new RedactFileRequest();
// I webbläsare är det vanligast att hantera filen som Base64
const base64String = "JVBERi0xLjQKJ..."; // Din Base64-kodade PDF/DOCX
req.setFileName("dokument.pdf");
req.setFilebase64(base64String);
const options = new Options();
options.addEntities(EntityType.NAME);
options.addEntities(EntityType.ADDRESS);
req.setOptions(options);
const metadata = { 'authorization': 'Bearer live_xxx...' };
client.redactFile(req, metadata, (err, resp) => {
if (err) return console.error(err);
// Svaret kommer som Base64, som kan användas för att skapa en nedladdningslänk
const redactedBase64 = resp.getResult().getRedactedfilebase64();
console.log('Mottog avidentifierad fil som Base64.');
});
import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';
import fs from 'fs';
const def = protoLoader.loadSync('./Protos/redaction.v1.proto', { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true });
const proto = grpc.loadPackageDefinition(def).grpc.redaction.v1;
const client = new proto.RedactionService('api.avidentifiera.se:50051', grpc.credentials.createSsl());
const md = new grpc.Metadata();
md.set('authorization', 'Bearer live_xxx...');
const bytes = fs.readFileSync('dokument.pdf');
const req = {
fileName: 'dokument.pdf',
fileBytes: bytes,
options: { entities: ['name','address'] }
};
client.RedactFile(req, md, (err, resp) => {
if (err) return console.error(err);
fs.writeFileSync('redacted.pdf', resp.result.redactedFileBytes);
console.log('Avidentifierad fil sparad som redacted.pdf');
});
import grpc
import redaction_v1_pb2
import redaction_v1_pb2_grpc
API_KEY = "Bearer live_xxx..."
ADDRESS = "api.avidentifiera.se:50051"
def run():
creds = grpc.ssl_channel_credentials()
with grpc.secure_channel(ADDRESS, creds) as channel:
stub = redaction_v1_pb2_grpc.RedactionServiceStub(channel)
with open("dokument.pdf", "rb") as f:
file_bytes = f.read()
req = redaction_v1_pb2.RedactFileRequest(
fileName="dokument.pdf",
fileBytes=file_bytes,
options=redaction_v1_pb2.Options(entities=[redaction_v1_pb2.EntityType.name, redaction_v1_pb2.EntityType.address])
)
md = [('authorization', API_KEY)]
resp = stub.RedactFile(req, metadata=md)
with open("redacted.pdf", "wb") as out:
out.write(resp.result.redactedFileBytes)
print("Avidentifierad fil sparad som redacted.pdf")
if __name__ == "__main__":
run()
Avidentifiera text
RPCRedactText
Skicka en textsträng och få tillbaka en avidentifierad version. Lyckade anrop returnerar status OK.
Request: RedactTextRequest
| Fält | Beskrivning |
|---|---|
text |
Texten som ska avidentifieras. Max 1 000 000 tecken. |
options |
Valfri konfiguration som styr avidentifieringen. Om den utelämnas används standardprofilen (namn, adress, personnummer maskeras). Se Regelkonfiguration. |
Response: RedactTextResponse
Svaret innehåller resultatet av avidentifieringen samt detaljerad metadata om vad som hittades.
| Fält | Beskrivning |
|---|---|
took |
Total bearbetningstid på servern i millisekunder. |
result |
Innehåller den avidentifierade texten i fältet redactedText.
Fältet entities innehåller strukturerad data för varje entitetstyp (t.ex. name, address). Varje entitetsobjekt har följande fält:
Notera: Listan |
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Net.Client.Web;
using Grpc.Redaction.V1;
const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };
var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, new HttpClientHandler());
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpHandler = grpcWebHandler });
var client = new RedactionService.RedactionServiceClient(channel);
var request = new RedactTextRequest
{
Text = "Mitt namn är Anna Andersson.",
Options = new Options
{
Entities = { EntityType.Name },
Rules = new Rules { Name = new NameRules { Method = Method.ReplaceWithInitials } }
}
};
var response = await client.RedactTextAsync(request, metadata);
Console.WriteLine($"Redacted: {response.Result.RedactedText}");
// Iterera över utförda ändringar för namn
foreach (var redaction in response.Result.Entities.Name.Redactions)
{
Console.WriteLine($"Changed '{redaction.Original}' to '{redaction.Replacement}'");
}
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Redaction.V1;
using System.Net;
const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };
var socketsHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true };
var httpClient = new HttpClient(socketsHandler) { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact };
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpClient = httpClient });
var client = new RedactionService.RedactionServiceClient(channel);
var request = new RedactTextRequest
{
Text = "Mitt namn är Anna Andersson.",
Options = new Options
{
Entities = { EntityType.Name },
Rules = new Rules { Name = new NameRules { Method = Method.ReplaceWithInitials } }
}
};
var response = await client.RedactTextAsync(request, metadata);
Console.WriteLine($"Redacted: {response.Result.RedactedText}");
// Visa detaljer om vad som ändrades
if (response.Result.Entities.Name != null)
{
foreach (var redaction in response.Result.Entities.Name.Redactions)
{
Console.WriteLine($"Original: {redaction.Original} -> Nytt: {redaction.Replacement}");
}
}
import { RedactionServiceClient } from './generated/RedactionServiceClientPb.js';
import { RedactTextRequest, Options, Rules, NameRules, Method, EntityType } from './generated/redaction_v1_pb.js';
const client = new RedactionServiceClient('https://api.avidentifiera.se:50051');
const req = new RedactTextRequest();
req.setText('Mitt namn är Anna Andersson.');
const options = new Options();
options.addEntities(EntityType.NAME);
const rules = new Rules();
const nameRules = new NameRules();
// Notera: Metodnamn i genererad JS-kod kan variera beroende på protoc-plugin
nameRules.setMethod(Method.REPLACEWITHINITIALS);
rules.setName(nameRules);
options.setRules(rules);
req.setOptions(options);
const metadata = { 'authorization': 'Bearer live_xxx...' };
client.redactText(req, metadata, (err, resp) => {
if (err) return console.error(err);
console.log(resp.getResult().getRedactedText());
// Hämta detaljer om namn-redigeringar
const nameResult = resp.getResult().getEntities().getName();
const redactions = nameResult.getRedactionsList();
redactions.forEach(r => {
console.log(`Original: ${r.getOriginal()}, Replacement: ${r.getReplacement()}`);
});
});
import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';
const def = protoLoader.loadSync('./Protos/redaction.v1.proto', { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true });
const proto = grpc.loadPackageDefinition(def).grpc.redaction.v1;
const client = new proto.RedactionService('api.avidentifiera.se:50051', grpc.credentials.createSsl());
const md = new grpc.Metadata();
md.set('authorization', 'Bearer live_xxx...');
const request = {
text: 'Mitt namn är Anna Andersson.',
options: {
entities: ['name'],
rules: {
name: { method: 'replaceWithInitials' }
}
}
};
client.RedactText(request, md, (err, resp) => {
if (err) return console.error(err);
console.log(resp.result.redactedText);
// Accessa entities.name.redactions
const nameRedactions = resp.result.entities.name.redactions;
nameRedactions.forEach(r => {
console.log(`Original: ${r.original} -> ${r.replacement}`);
});
});
import grpc
import redaction_v1_pb2
import redaction_v1_pb2_grpc
API_KEY = "Bearer live_xxx..."
ADDRESS = "api.avidentifiera.se:50051"
def run():
creds = grpc.ssl_channel_credentials()
with grpc.secure_channel(ADDRESS, creds) as channel:
stub = redaction_v1_pb2_grpc.RedactionServiceStub(channel)
req = redaction_v1_pb2.RedactTextRequest(
text="Mitt namn är Anna Andersson.",
options=redaction_v1_pb2.Options(
entities=[redaction_v1_pb2.EntityType.name],
rules=redaction_v1_pb2.Rules(
name=redaction_v1_pb2.NameRules(method=redaction_v1_pb2.Method.replaceWithInitials)
)
)
)
md = [('authorization', API_KEY)]
resp = stub.RedactText(req, metadata=md)
print(f"Text: {resp.result.redactedText}")
# Loopa igenom redactions för namn
for r in resp.result.entities.name.redactions:
print(f"Original: {r.original} -> Replacement: {r.replacement}")
if __name__ == "__main__":
run()
Regelkonfiguration (Options)
Meddelandet Options är valfritt. Om du utelämnar det används standardläget: namn, adress och personnummer maskeras.
Strukturen består av två delar:
entities– en lista med deEntityTypesom API:t ska leta efter och behandla.rules– ett objekt där du kan specificera detaljerade regler för varje entitetstyp.
Övergripande struktur: Options
| Fält | Beskrivning |
|---|---|
entities |
Vilka entiteter som ska avidentifieras. Värden:
name,
address,
email,
phoneNumber,
personalNumber.
|
rules |
Ett objekt som innehåller en default-regel och specifika regler för varje entitetstyp (t.ex. name, address). Specifika regler ärver från och skriver över default-regeln.
|
Gemensamma regelfält (gäller alla entiteter)
| Fält | Beskrivning |
|---|---|
method | Hur värdet ska hanteras: mask,pseudonymize, replaceWithInitials (endast för name), replaceWith, remove. |
replacementText | Krävs om method är replaceWith. |
maskingCharacter | Tecken som används vid textmaskering när method är mask. Standard är "█". |
fillColor / textColor | Hex-färger (#RRGGBB eller #RRGGBBAA) för visuell redigering i PDF/DOCX. |
findValues / ignoreValues | Listor med strängar för att tvinga fram eller ignorera specifika matchningar. |
Gemensamma regelfält (gäller alla entiteter)
| Fält | Beskrivning |
|---|---|
method |
Hur värdet ska hanteras. Tillgängliga metoder:
|
replacementText | Krävs om method är replaceWith. |
maskingCharacter | Tecken som används vid textmaskering när method är mask. Standard är "█". |
fillColor / textColor | Hex-färger (#RRGGBB eller #RRGGBBAA) för visuell redigering i PDF/DOCX. |
findValues / ignoreValues | Listor med strängar för att tvinga fram eller ignorera specifika matchningar. |
Idempotens
För att göra säkra retries utan att orsaka dubbeldebitering kan du skicka med metadata-headern
idempotency-key: <unik-nyckel>.
Detta är valfritt men rekommenderat för robusta integrationer.
Om ett anrop med en specifik nyckel lyckas, kommer efterföljande anrop med samma nyckel och identisk request-payload inom 15 minuter att returnera det cachade svaret istället för att utföra operationen på nytt.
Svaret innehåller två trailers (metadata som skickas efter responsen) för att bekräfta status:
idempotency-key: Ekar nyckeln som användes. Om du inte skickade en, innehåller den en server-genererad nyckel.idempotency-status: Antingencreated(första lyckade anropet) ellerreplayed(ett cachat svar returnerades).
Felhantering
gRPC-tjänsten använder standardiserade gRPC-statuskoder för att signalera resultat. Alla fel som uppstår i kommunikationen eller på servern signaleras som en RpcException (eller motsvarande i ditt språk).
Vid valideringsfel (INVALID_ARGUMENT) skickas en mer detaljerad felbeskrivning enligt Google RPC Error Model. Denna information finns i en binär trailer med nyckeln grpc-status-details-bin.
Standard gRPC-statuskoder
| Statuskod | Betydelse |
|---|---|
OK | Anropet lyckades. |
INVALID_ARGUMENT | Ogiltig indata, t.ex. saknat fält eller felaktiga värden i Options. Detaljer finns i grpc-status-details-bin. |
UNAUTHENTICATED | API-nyckel saknas eller är ogiltig. |
FAILED_PRECONDITION | En förutsättning är inte uppfylld, t.ex. filtypen som skickades stöds inte. |
RESOURCE_EXHAUSTED | Din dagliga sidkvot har överskridits. |
ALREADY_EXISTS | Idempotenskonflikt. Samma idempotency-key har använts med en annan request-payload. |
INTERNAL | Ett oväntat serverfel inträffade. |
UNAVAILABLE | Tjänsten är tillfälligt otillgänglig, t.ex. på grund av överbelastning eller underhåll. En retry kan fungera. |
DEADLINE_EXCEEDED | Anropet tog för lång tid och avbröts. |
Hantering av detaljerade valideringsfel (INVALID_ARGUMENT)
När ett anrop returnerar INVALID_ARGUMENT innehåller grpc-status-details-bin en serialiserad Google.Rpc.Status-meddelande. Detta meddelande kan i sin tur innehålla en lista av Any-objekt i fältet details, där varje Any kan packa upp till exempelvis ett Google.Rpc.BadRequest-meddelande med specifika FieldViolation-objekt.
Varje FieldViolation innehåller:
field: Sökvägen till det fält som orsakade felet (t.ex.options.rules.personalNumber.replacementText).description: En läsbar beskrivning av valideringsfelet.
För att kunna parsa dessa detaljerade fel behöver du inkludera proto-definitionerna för google/rpc/status.proto och google/rpc/bad_request.proto i din klient-setup. I C# hanteras detta automatiskt via Google.Api.CommonProtos paketet.
using Grpc.Core;
using Grpc.Net.Client;
using Grpc.Redaction.V1;
using Google.Rpc; // För Status och BadRequest
using Google.Protobuf; // För ByteString
using System.Net;
const string apiKey = "Bearer live_xxx...";
var metadata = new Metadata { { "Authorization", apiKey } };
var socketsHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true };
var httpClient = new HttpClient(socketsHandler) { DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionExact };
using var channel = GrpcChannel.ForAddress("https://api.avidentifiera.se:50051", new GrpcChannelOptions { HttpClient = httpClient });
var client = new RedactionService.RedactionServiceClient(channel);
// Skapa en request som medvetet orsakar ett valideringsfel
// replacementText kräver att method är ReplaceWith
var invalidRequest = new RedactTextRequest
{
Text = "Test text.",
Options = new Options
{
Entities = { EntityType.Name },
Rules = new Rules
{
Name = new NameRules {
Method = Method.Mask, // Fel: Mask är satt, men replacementText är också satt
ReplacementText = "NAMN"
}
}
}
};
try
{
var response = await client.RedactTextAsync(invalidRequest, metadata);
Console.WriteLine("Anrop lyckades: " + response.Result.RedactedText);
}
catch (RpcException rpcEx)
{
Console.WriteLine($"gRPC-fel: {rpcEx.StatusCode} - {rpcEx.Status.Detail}");
if (rpcEx.StatusCode == Grpc.Core.StatusCode.Unauthenticated)
{
Console.WriteLine("Fel: Ogiltig eller saknad API-nyckel.");
}
else if (rpcEx.StatusCode == Grpc.Core.StatusCode.InvalidArgument)
{
// Försök läsa detaljerade valideringsfel
var trailerEntry = rpcEx.Trailers.Get("grpc-status-details-bin");
if (trailerEntry?.ValueBytes is { Length: > 0 })
{
try
{
var status = Google.Rpc.Status.Parser.ParseFrom(trailerEntry.ValueBytes);
Console.WriteLine($"Detaljerat felmeddelande: {status.Message}");
foreach (var anyDetail in status.Details)
{
if (anyDetail.Is())
{
var badRequest = anyDetail.Unpack();
Console.WriteLine("Valideringsfel:");
foreach (var violation in badRequest.FieldViolations)
{
Console.WriteLine($"- Fält: {violation.Field}, Beskrivning: {violation.Description}");
}
}
// Här kan du lägga till hantering för andra typer av detaljer om de finns
}
}
catch (Exception parseEx)
{
Console.WriteLine($"Kunde inte parsa grpc-status-details-bin: {parseEx.Message}");
}
}
}
else
{
Console.WriteLine("Ett annat gRPC-fel inträffade.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Ett oväntat fel inträffade: {ex.Message}");
}
import grpc
import redaction_v1_pb2
import redaction_v1_pb2_grpc
from google.rpc import status_pb2, bad_request_pb2 # Importera status och bad_request
API_KEY = "Bearer live_xxx..."
ADDRESS = "api.avidentifiera.se:50051"
def run():
creds = grpc.ssl_channel_credentials()
with grpc.secure_channel(ADDRESS, creds) as channel:
stub = redaction_v1_pb2_grpc.RedactionServiceStub(channel)
# Skapa en request som medvetet orsakar ett valideringsfel
# replacementText kräver att method är ReplaceWith
invalid_request = redaction_v1_pb2.RedactTextRequest(
text="Test text.",
options=redaction_v1_pb2.Options(
entities=[redaction_v1_pb2.EntityType.name],
rules=redaction_v1_pb2.Rules(
name=redaction_v1_pb2.NameRules(
method=redaction_v1_pb2.Method.mask, # Fel: Mask är satt, men replacementText är också satt
replacementText="NAMN"
)
)
)
)
md = [('authorization', API_KEY)]
try:
response = stub.RedactText(invalid_request, metadata=md)
print(f"Anrop lyckades: {response.result.redactedText}")
except grpc.RpcError as rpc_error:
print(f"gRPC-fel: {rpc_error.code()} - {rpc_error.details()}")
if rpc_error.code() == grpc.StatusCode.UNAUTHENTICATED:
print("Fel: Ogiltig eller saknad API-nyckel.")
elif rpc_error.code() == grpc.StatusCode.INVALID_ARGUMENT:
# Försök läsa detaljerade valideringsfel från trailers
for key, value in rpc_error.trailing_metadata():
if key == 'grpc-status-details-bin':
try:
# Parsa Google.Rpc.Status meddelandet
status = status_pb2.Status()
status.ParseFromString(value)
print(f"Detaljerat felmeddelande: {status.message}")
for detail in status.details:
if detail.Is(bad_request_pb2.BadRequest.DESCRIPTOR):
bad_request = bad_request_pb2.BadRequest()
detail.Unpack(bad_request)
print("Valideringsfel:")
for violation in bad_request.field_violations:
print(f"- Fält: {violation.field}, Beskrivning: {violation.description}")
# Här kan du lägga till hantering för andra typer av detaljer om de finns
except Exception as parse_ex:
print(f"Kunde inte parsa grpc-status-details-bin: {parse_ex}")
else:
print("Ett annat gRPC-fel inträffade.")
except Exception as ex:
print(f"Ett oväntat fel inträffade: {ex}")
if __name__ == "__main__":
run()
import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';
import { Status } from 'google-protobuf/google/rpc/status_pb.js'; // Importera Status
import { BadRequest } from 'google-protobuf/google/rpc/bad_request_pb.js'; // Importera BadRequest
// Ladda in dina egna proto-filer
const packageDefinition = protoLoader.loadSync(
['./Protos/redaction.v1.proto', 'google/rpc/status.proto', 'google/rpc/bad_request.proto'], // Inkludera RPC error protos
{ keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }
);
const proto = grpc.loadPackageDefinition(packageDefinition).grpc.redaction.v1;
const client = new proto.RedactionService('api.avidentifiera.se:50051', grpc.credentials.createSsl());
const API_KEY = 'Bearer live_xxx...';
const metadata = new grpc.Metadata();
metadata.set('authorization', API_KEY);
// Skapa en request som medvetet orsakar ett valideringsfel
// replacementText kräver att method är ReplaceWith
const invalidRequest = {
text: 'Test text.',
options: {
entities: ['name'],
rules: {
name: {
method: 'mask', // Fel: Mask är satt, men replacementText är också satt
replacementText: 'NAMN'
}
}
}
};
client.RedactText(invalidRequest, metadata, (err, resp) => {
if (err) {
console.error(`gRPC-fel: ${err.code} - ${err.details}`);
if (err.code === grpc.status.UNAUTHENTICATED) {
console.error("Fel: Ogiltig eller saknad API-nyckel.");
} else if (err.code === grpc.status.INVALID_ARGUMENT) {
// Försök läsa detaljerade valideringsfel från trailers
const statusDetailsBin = err.metadata.get('grpc-status-details-bin');
if (statusDetailsBin && statusDetailsBin.length > 0) {
try {
// statusDetailsBin är en array av Buffers, vi tar den första
const status = Status.deserializeBinary(statusDetailsBin[0]);
console.error(`Detaljerat felmeddelande: ${status.getMessage()}`);
for (const anyDetail of status.getDetailsList()) {
// Kontrollera om detaljen är av typen BadRequest
if (anyDetail.getTypeName() === 'type.googleapis.com/google.rpc.BadRequest') {
const badRequest = anyDetail.unpack(BadRequest.deserializeBinary, BadRequest.prototype.getTypeName());
if (badRequest) {
console.error("Valideringsfel:");
for (const violation of badRequest.getFieldViolationsList()) {
console.error(`- Fält: ${violation.getField()}, Beskrivning: ${violation.getDescription()}`);
}
}
}
// Här kan du lägga till hantering för andra typer av detaljer om de finns
}
} catch (parseEx) {
console.error(`Kunde inte parsa grpc-status-details-bin: ${parseEx.message}`);
}
}
} else {
console.error("Ett annat gRPC-fel inträffade.");
}
} else {
console.log("Anrop lyckades: " + resp.result.redactedText);
}
});