Skip to content

Commit

Permalink
[core] RLC content-type header fix (#32672)
Browse files Browse the repository at this point in the history
### Packages impacted by this PR

@azure-rest/core-client

### Issues associated with this PR

Resolves #31843

### Describe the problem that is addressed by this PR

Per the HTTP spec, `application/json` content type does not define a
charset as all JSON is UTF and @azure/core-client uses `application/json` for its
default content-type for JSON.

In migrating KeyVault to modular, based on RLC, we noticed that the
default content type in @azure-rest/core-client uses `application/json;
charset=UTF-8`

While compliant servers _should_ accept this version and ignore the
charset, in practice KeyVault rejects requests to certain endpoints with an
unsupported content type.

Since the charset parameter is not needed and not defined per spec,
removing it will increase compatibility. 

See:

- https://www.iana.org/assignments/media-types/application/json
- https://datatracker.ietf.org/doc/html/rfc4627#section-6


Finally, looking at other languages shows that others define the
content-type as `application/json` by default, without the charset.
  • Loading branch information
maorleger authored Jan 24, 2025
1 parent d4b35cd commit 947613e
Show file tree
Hide file tree
Showing 29 changed files with 34 additions and 33 deletions.
2 changes: 1 addition & 1 deletion sdk/ai/ai-inference-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/ai/ai-inference-rest",
"Tag": "js/ai/ai-inference-rest_ef0e506dc8"
"Tag": "js/ai/ai-inference-rest_adc3d6a0ba"
}
2 changes: 1 addition & 1 deletion sdk/anomalydetector/ai-anomaly-detector-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/anomalydetector/ai-anomaly-detector-rest",
"Tag": "js/anomalydetector/ai-anomaly-detector-rest_182b0f265c"
"Tag": "js/anomalydetector/ai-anomaly-detector-rest_617d44b1c0"
}
2 changes: 1 addition & 1 deletion sdk/appservice/arm-appservice-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/appservice/arm-appservice-rest",
"Tag": "js/appservice/arm-appservice-rest_705bf81a12"
"Tag": "js/appservice/arm-appservice-rest_ef14e3c2c9"
}
2 changes: 1 addition & 1 deletion sdk/compute/arm-compute-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/compute/arm-compute-rest",
"Tag": "js/compute/arm-compute-rest_0ae84fff28"
"Tag": "js/compute/arm-compute-rest_1d6b2247ad"
}
2 changes: 1 addition & 1 deletion sdk/contentsafety/ai-content-safety-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/contentsafety/ai-content-safety-rest",
"Tag": "js/contentsafety/ai-content-safety-rest_1fb3b4ba95"
"Tag": "js/contentsafety/ai-content-safety-rest_c96a71b19d"
}
1 change: 1 addition & 0 deletions sdk/core/core-client-rest/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### Bugs Fixed

- Handle error responses with no body without causing a `TypeError`. PR [#32566](https://github.com/Azure/azure-sdk-for-js/pull/32566)
- Content-Type header now defaults to `application/json` instead of `application/json; charset=UTF-8` to conform to the HTTP standard. PR [#32672](https://github.com/Azure/azure-sdk-for-js/pull/32672)

### Other Changes

Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-client-rest/src/multipart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function getPartContentType(descriptor: PartDescriptor): HeaderValue | undefined
}

// arbitrary non-text object -> generic JSON content type by default. We will try to JSON.stringify the body.
return "application/json; charset=UTF-8";
return "application/json";
}

/**
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/core-client-rest/src/sendRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ function getContentType(body: any): string | undefined {
if (typeof body === "string") {
try {
JSON.parse(body);
return "application/json; charset=UTF-8";
return "application/json";
} catch (error: any) {
// If we fail to parse the body, it is not json
return undefined;
}
}
// By default return json
return "application/json; charset=UTF-8";
return "application/json";
}

export interface InternalRequestParameters extends RequestParameters {
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/core-client-rest/test/multipart.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ describe("multipart buildBodyPart", () => {
{
description: "binary content gets passed through, regardless of content type",
descriptor: {
contentType: "application/json; charset=UTF-8",
contentType: "application/json",
body: new Uint8Array([1, 2, 3]),
},
expected: new Uint8Array([1, 2, 3]),
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/core-client-rest/test/sendRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ describe("sendRequest", () => {
},
{
headers: createHttpHeaders({
"content-type": "application/json; charset=UTF-8",
"content-type": "application/json",
"content-disposition": `form-data; name="fileArray2"`,
}),
body: stringToUint8Array("{}", "utf-8"),
Expand Down Expand Up @@ -461,7 +461,7 @@ describe("sendRequest", () => {
it("should set application/json by default if it is json string", async () => {
const mockPipeline: Pipeline = createEmptyPipeline();
mockPipeline.sendRequest = async (_client, request) => {
assert.equal(request.headers.get("content-type"), "application/json; charset=UTF-8");
assert.equal(request.headers.get("content-type"), "application/json");
return { headers: createHttpHeaders() } as PipelineResponse;
};

Expand Down
4 changes: 2 additions & 2 deletions sdk/core/ts-http-runtime/review/azure-core-comparison.diff
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ index 06bc091..90e6659 100644
/**
* The programmatic identifier of the bearerTokenAuthenticationPolicy.
diff --git a/src/client/multipart.ts b/src/client/multipart.ts
index e34a065..2bc3df1 100644
index 175b5cd..102ed57 100644
--- a/src/client/multipart.ts
+++ b/src/client/multipart.ts
@@ -1,14 +1,11 @@
Expand Down Expand Up @@ -789,7 +789,7 @@ index 3df6036..d653f06 100644

/**
diff --git a/src/client/sendRequest.ts b/src/client/sendRequest.ts
index 9e947e6..f2a1cac 100644
index 4ee4778..dc3b0f5 100644
--- a/src/client/sendRequest.ts
+++ b/src/client/sendRequest.ts
@@ -5,19 +5,16 @@ import type {
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/ts-http-runtime/src/client/multipart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function getPartContentType(descriptor: PartDescriptor): HeaderValue | undefined
}

// arbitrary non-text object -> generic JSON content type by default. We will try to JSON.stringify the body.
return "application/json; charset=UTF-8";
return "application/json";
}

/**
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/ts-http-runtime/src/client/sendRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,14 @@ function getContentType(body: any): string | undefined {
if (typeof body === "string") {
try {
JSON.parse(body);
return "application/json; charset=UTF-8";
return "application/json";
} catch (error: any) {
// If we fail to parse the body, it is not json
return undefined;
}
}
// By default return json
return "application/json; charset=UTF-8";
return "application/json";
}

export interface InternalRequestParameters extends RequestParameters {
Expand Down
2 changes: 1 addition & 1 deletion sdk/core/ts-http-runtime/test/client/multipart.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ describe("multipart buildBodyPart", () => {
{
description: "binary content gets passed through, regardless of content type",
descriptor: {
contentType: "application/json; charset=UTF-8",
contentType: "application/json",
body: new Uint8Array([1, 2, 3]),
},
expected: new Uint8Array([1, 2, 3]),
Expand Down
4 changes: 2 additions & 2 deletions sdk/core/ts-http-runtime/test/client/sendRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ describe("sendRequest", () => {
},
{
headers: createHttpHeaders({
"content-type": "application/json; charset=UTF-8",
"content-type": "application/json",
"content-disposition": `form-data; name="fileArray2"`,
}),
body: stringToUint8Array("{}", "utf-8"),
Expand Down Expand Up @@ -450,7 +450,7 @@ describe("sendRequest", () => {
it("should set application/json by default if it is json string", async () => {
const mockPipeline: Pipeline = createEmptyPipeline();
mockPipeline.sendRequest = async (_client, request) => {
assert.equal(request.headers.get("content-type"), "application/json; charset=UTF-8");
assert.equal(request.headers.get("content-type"), "application/json");
return { headers: createHttpHeaders() } as PipelineResponse;
};

Expand Down
2 changes: 1 addition & 1 deletion sdk/devcenter/developer-devcenter-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/devcenter/developer-devcenter-rest",
"Tag": "js/devcenter/developer-devcenter-rest_69f2e99d81"
"Tag": "js/devcenter/developer-devcenter-rest_13eff72e03"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/documentintelligence/ai-document-intelligence-rest",
"Tag": "js/documentintelligence/ai-document-intelligence-rest_77b0b5eb27"
"Tag": "js/documentintelligence/ai-document-intelligence-rest_a80611016b"
}
2 changes: 1 addition & 1 deletion sdk/easm/defender-easm-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/easm/defender-easm-rest",
"Tag": "js/easm/defender-easm-rest_fac8ec9d4b"
"Tag": "js/easm/defender-easm-rest_8e5b1ff15e"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/healthdataaiservices/azure-health-deidentification",
"Tag": "js/healthdataaiservices/azure-health-deidentification_69c12d020a"
"Tag": "js/healthdataaiservices/azure-health-deidentification_75ab7411d9"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/healthinsights/health-insights-cancerprofiling-rest",
"Tag": "js/healthinsights/health-insights-cancerprofiling-rest_ed82d67321"
"Tag": "js/healthinsights/health-insights-cancerprofiling-rest_22b888b428"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/healthinsights/health-insights-clinicalmatching-rest",
"Tag": "js/healthinsights/health-insights-clinicalmatching-rest_ca6a87502d"
"Tag": "js/healthinsights/health-insights-clinicalmatching-rest_136ccc8048"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/healthinsights/health-insights-radiologyinsights-rest",
"Tag": "js/healthinsights/health-insights-radiologyinsights-rest_e4b8dcdb33"
"Tag": "js/healthinsights/health-insights-radiologyinsights-rest_75e7a7a0ea"
}
2 changes: 1 addition & 1 deletion sdk/maps/maps-search-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/maps/maps-search-rest",
"Tag": "js/maps/maps-search-rest_02b37595e1"
"Tag": "js/maps/maps-search-rest_dd652b2e09"
}
2 changes: 1 addition & 1 deletion sdk/purview/purview-catalog-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/purview/purview-catalog-rest",
"Tag": "js/purview/purview-catalog-rest_693ca34caa"
"Tag": "js/purview/purview-catalog-rest_d24e6e64d9"
}
2 changes: 1 addition & 1 deletion sdk/purview/purview-sharing-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/purview/purview-sharing-rest",
"Tag": "js/purview/purview-sharing-rest_b319bf2e04"
"Tag": "js/purview/purview-sharing-rest_feee958ea2"
}
2 changes: 1 addition & 1 deletion sdk/purview/purview-workflow-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/purview/purview-workflow-rest",
"Tag": "js/purview/purview-workflow-rest_697f6dad7b"
"Tag": "js/purview/purview-workflow-rest_b4ecdbc64a"
}
2 changes: 1 addition & 1 deletion sdk/synapse/synapse-access-control-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/synapse/synapse-access-control-rest",
"Tag": "js/synapse/synapse-access-control-rest_d58b49a726"
"Tag": "js/synapse/synapse-access-control-rest_d7548336a7"
}
2 changes: 1 addition & 1 deletion sdk/translation/ai-translation-document-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/translation/ai-translation-document-rest",
"Tag": "js/translation/ai-translation-document-rest_4f72a8b574"
"Tag": "js/translation/ai-translation-document-rest_0d8f04de0a"
}
2 changes: 1 addition & 1 deletion sdk/translation/ai-translation-text-rest/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "js",
"TagPrefix": "js/translation/ai-translation-text-rest",
"Tag": "js/translation/ai-translation-text-rest_34ef4ea36f"
"Tag": "js/translation/ai-translation-text-rest_35e2b2e125"
}

0 comments on commit 947613e

Please sign in to comment.