koltyakov / sp-rest-proxy

🌐 SharePoint API Proxy for local development
MIT License
172 stars 43 forks source link

Error 400 (Bad Request) when Using Batching #178

Open Guseff opened 6 months ago

Guseff commented 6 months ago

Hello!

I'm using SP Rest Proxy v. 3.3.4. In case I make single request it works good. While I try to make request using batching it returns 400 error Screenshot 2024-05-06 at 1 59 09 PM

When deployed to env batching works. The problem presents only when the application is running locally.

Code looks like how it described here: https://pnp.github.io/pnpjs/concepts/batching/#advanced-batching

Screenshot 2024-05-06 at 2 04 55 PM

and baseUrl === 'http://localhost:8080'

I tried to utilise SP url from webAbsoluteUrl, that I received using loadPageContext util. In this case it returns 403 error: Screenshot 2024-05-06 at 2 21 03 PM Url in webAbsoluteUr is correct SP URL.

Could you, please, give me a pease of advice how to handle this?

koltyakov commented 6 months ago

Hi @Guseff,

There is the thing with batch requests. They are processed inside SharePoint API backend, the multipart body contains a list or API calls which should have hostnames known by the SharePoint/farm.

When you use the proxy you route API calls to its host and it routes to SharePoint injecting Authentication headers/cookies/etc. to SharePoint:

Frontend (http://localhost:3000) ---[api request]--> Proxy (http://localhost:8080/sites/site-a/_api/web) ---> SharePoint Server (https://contoso.sharepoint.com/sites/site-a/_api/web)

http://localhost:3000 in that case is the frontend dev server address. http://localhost:8080 is the stand along proxy (it can be a part of dev server, or routed through it). The app doesn't know anything about https://contoso.sharepoint.com/sites/site-a/_api/web thinking that http://localhost:8080/sites/site-a/_api/web is SharePoint entry.

The batch API requests's body should contain https://contoso.sharepoint.com/sites/site-a or http://localhost:8080/sites/site-a, URL path part should be valid, e.g. not http://localhost:8080/_api/web.

The localhost addresses then are replaced by this method https://github.com/koltyakov/sp-rest-proxy/blob/master/src/core/routers/restBatch.ts#L35 to https://contoso.sharepoint.com so SharePoint API then knows the host.

That said, make sure that what's inside batch API call body is valid.

My guess is that:

The solution would be to initiate PnPjs const web = Web('http://localhost:8080/sites/site-a'); explicitly with web absolute address (proxy's host and site path: http://localhost:8080/sites/site-a), otherwise you send requests to the root site instead your application hosting site.

Guseff commented 5 months ago

Hi, Andrew @koltyakov!

As I understand, I initialise sp variable using http://localhost:8080 base url. And in batch request body I have urls with this base: Screenshot 2024-05-07 at 8 36 58 PM And I need to replace http://localhost:8080 in this body with a real sp-site url. But I don't understand how I can do that. It looks like the approach described here https://github.com/koltyakov/sp-rest-proxy/issues/42 is deprecated and don't work at the moment.

I try to use batched web (https://pnp.github.io/pnpjs/concepts/batching/#using-a-batched-web) but it doesn't work too.

koltyakov commented 5 months ago

Hey @Guseff,

So you confirmed the guess that it's:

  • you have connected a site /sites/site-a (in proxy auth config)
  • but then call a root one / (in the application code)

And the solution suggested is still actual.

The solution would be to initiate PnPjs const web = Web('http://localhost:8080/sites/site-a');

When you use non-batched queries the proxy replaces / to the contextual site (/sites/site-a, from auth configuration).

With batched queries this is not an option and you should explicitly define you're working with specific site: https://github.com/koltyakov/sp-rest-proxy?tab=readme-ov-file#load-page-context-helper

import { loadPageContext } from 'sp-rest-proxy/dist/utils/env';

// ...

// In both localhost and published to SharePoint page
await loadPageContext(); // `_spPageContextInfo` will contain correct info for vital props

// PnPjs's Web object should be created in the following way
const web = new Web(_spPageContextInfo.webAbsoluteUrl); // check if this still relevant with PnPjs
// _spPageContextInfo.webAbsoluteUrl -> http://localhost:8080/sites/site-a

// Then goes ordinary PnPjs code
const batch = web.createBatch();

const list = web.getList(`${_spPageContextInfo.webServerRelativeUrl}/List/ListName`);
const entityName = await list.getListItemEntityTypeFullName();

[1, 2, 3, 4].forEach((el) => {
  list.items.inBatch(batch).add({
    Title: `${el}`
  }, entityName);
});

await batch.execute();
console.log('Done');

P.S. Batches aren't usually worth using them. They ending up with the same throttling hits. I've rarely seen any benefits from batches to be honest, but more complex error handling.